From e678d27631be1c6f8290a4f5eb81bf830aa9aa88 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Sun, 18 Feb 2024 21:17:57 -0400 Subject: [PATCH 01/12] Initial commit from SciCookie --- .editorconfig | 25 +++ .github/ISSUE_TEMPLATE/bug-report.yml | 149 ++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 15 ++ .../ISSUE_TEMPLATE/documentation-report.yml | 55 ++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 83 +++++++++ .github/PULL_REQUEST_TEMPLATE.md | 55 ++++++ .github/workflows/main.yaml | 43 +++++ .github/workflows/release.yaml | 72 ++++++++ .gitignore | 81 ++------- .makim.yaml | 93 ++++++++++ .pre-commit-config.yaml | 84 +++++++++ .releaserc.json | 75 ++++++++ CODE_OF_CONDUCT.md | 132 ++++++++++++++ LICENSE | 53 +++--- README.md | 16 +- conda/dev.yaml | 10 ++ containers/Dockerfile | 63 +++++++ containers/compose.yaml | 17 ++ docs/api/references.md | 3 + docs/api/references.rst | 8 + docs/changelog.md | 2 + docs/contributing.md | 164 ++++++++++++++++++ docs/example.ipynb | 56 ++++++ docs/images/favicon.ico | Bin 0 -> 72342 bytes docs/images/logo.png | Bin 0 -> 20402 bytes docs/index.md | 48 +++++ docs/installation.md | 41 +++++ docs/mkdocs.yaml | 150 ++++++++++++++++ pyproject.toml | 106 +++++++++++ src/feedback_linker/__init__.py | 19 ++ src/feedback_linker/feedback_linker.py | 1 + src/feedback_linker/py.typed | 0 tests/__init__.py | 1 + tests/test_feedback_linker.py | 13 ++ 34 files changed, 1636 insertions(+), 97 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation-report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/main.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .makim.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 .releaserc.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 conda/dev.yaml create mode 100644 containers/Dockerfile create mode 100644 containers/compose.yaml create mode 100644 docs/api/references.md create mode 100644 docs/api/references.rst create mode 100644 docs/changelog.md create mode 100644 docs/contributing.md create mode 100644 docs/example.ipynb create mode 100644 docs/images/favicon.ico create mode 100644 docs/images/logo.png create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/mkdocs.yaml create mode 100644 pyproject.toml create mode 100644 src/feedback_linker/__init__.py create mode 100644 src/feedback_linker/feedback_linker.py create mode 100644 src/feedback_linker/py.typed create mode 100644 tests/__init__.py create mode 100644 tests/test_feedback_linker.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..eb61873 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +end_of_line = lf + +[*.py] +indent_style = space +indent_size = 4 + +[*.bat] +indent_style = tab +end_of_line = crlf + +[LICENSE] +insert_final_newline = true + +[Makefile] +indent_style = tab diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..9a179ed --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,149 @@ +--- +name: 🐛 Bug Report +description: Create a report to help us improve +labels: ["bug", "needs-triage"] + +body: +- type: markdown + attributes: + value: > + **Thank you for wanting to report a bug in Feedback Linker!** + + + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + + + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Abug + +- type: textarea + attributes: + label: Summary + description: Explain the problem briefly below. + placeholder: >- + When I try to do X with {{ cookiecutter.project_name }} and the following workspace, Y breaks or + Z happens in an unexpected manner. + Here are all the details I know about this problem. + validations: + required: true + +- type: textarea + attributes: + label: OS / Environment + description: >- + Provide information on your operating system. + Something like the output of `cat /etc/os-release` on Linux or + `system_profiler -detailLevel mini SPSoftwareDataType` on macOS. + render: console + placeholder: | + # Linux + $ cat /etc/os-release + NAME="Ubuntu" + VERSION="20.04.2 LTS (Focal Fossa)" + ID=ubuntu + ID_LIKE=debian + PRETTY_NAME="Ubuntu 20.04.2 LTS" + VERSION_ID="20.04" + HOME_URL="https://www.ubuntu.com/" + SUPPORT_URL="https://help.ubuntu.com/" + BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" + PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" + VERSION_CODENAME=focal + UBUNTU_CODENAME=focal + + # macOS + $ system_profiler -detailLevel mini SPSoftwareDataType | head -n 6 + Software: + + System Software Overview: + + System Version: macOS 10.15.7 (19H1323) + Kernel Version: Darwin 19.6.0 + validations: + required: true + +- type: textarea + attributes: + label: Steps to Reproduce + description: >- + Describe exactly how to reproduce the problem, using a minimal test-case. + It would *really* help us understand your problem if you paste in the Python code + that you're running. + + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + value: | + + ```python (paste below) + + ``` + + + ```console (paste below) + + ``` + validations: + required: true + +- type: textarea + attributes: + label: File Upload (optional) + description: >- + If your steps to reproduce your minimal failing example require either a spec or a + workspace file, please upload it by attaching it to the text area here. + + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + placeholder: >- + Attach any files or compressed archives by dragging & dropping, selecting, + or pasting them here. + validations: + required: false + +- type: textarea + attributes: + label: Expected Results + description: >- + Describe what you expected to happen when running the steps above. + placeholder: >- + I expected X to happen because I assumed Y. + validations: + required: true + +- type: textarea + attributes: + label: Actual Results + description: >- + Paste verbatim program or command output. + Don't wrap it with triple backticks — your whole input will be + turned into a code snippet automatically. + render: console + validations: + required: true + +- type: textarea + attributes: + # label: Feedback Linker version + # description: >- + # Paste verbatim output from `Feedback Linker --version` below, under the prompt line. + # Don't wrap it with triple backticks — your whole input will be + # turned into a code snippet automatically. + render: console + placeholder: | + python -m pip show Feedback Linker + validations: + required: true + +- type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + + [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +... +This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d423150 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,15 @@ +# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: true +contact_links: +- name: 🙋 Usage Questions + url: https://github.com/xmnlab/feedback-linker.git/discussions + about: | + Use Feedback Linker's GitHub Discussions to ask "How do I do X with Feedback Linker?". +- name: 📖 Tutorial + url: https://github.com/xmnlab/feedback-linker.git + about: | + The Feedback Linker tutorial is continually updated and provides an in depth walkthrough + of how to use the latest release of Feedback Linker. +- name: 📝 Feedback Linker Code of Conduct + url: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + about: Expectations for how people will interact with each other on Feedback Linker's GitHub. diff --git a/.github/ISSUE_TEMPLATE/documentation-report.yml b/.github/ISSUE_TEMPLATE/documentation-report.yml new file mode 100644 index 0000000..c77d7fa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-report.yml @@ -0,0 +1,55 @@ +--- +name: 📝 Documentation Report +description: Create a report for problems with the docs +labels: ["docs", "needs-triage"] + +body: +- type: markdown + attributes: + value: > + **Thank you for wanting to report a problem with Feedback Linker's documentation!** + + + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + + + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Adocs + +- type: textarea + attributes: + label: Summary + description: >- + Explain the problem briefly below, add suggestions to wording or structure. + If there are external references that are related please link them here + as well. + placeholder: >- + I was reading the Feedback Linker documentation for Feedback Linker version X and I'm having + problems understanding Y. + It would be very helpful if that got rephrased as Z. + validations: + required: true + +- type: input + attributes: + label: Documentation Page Link + description: | + Paste the link to the documentation webpage that you have a question on. + validations: + required: true + +- type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + + [CoC]: https://github.com/xmnlab/feedback-linker.git/blob/main/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +... + + +This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..3fe58d3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,83 @@ +--- +name: ✹ Feature Request +description: Suggest an idea for this project +labels: ["feat/enhancement ", "needs-triage"] + +body: +- type: markdown + attributes: + value: > + **Thank you for wanting to suggest a feature for Feedback Linker!** + + + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + Make sure to check the closed issues as well as it may + already be implemented in a development release. + + + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Afeat%2Fenhancement + + +- type: textarea + attributes: + label: Summary + description: > + Describe the new feature/improvement you would like briefly below. + + + What's the problem this feature will solve? + What are you trying to do, that you are unable to achieve + with the **latest** release of Feedback Linker? + + + * Provide examples of real-world use cases that this would enable + and how it solves the problem you described. + + * How do you solve this now? + + * Have you tried to work around the problem? + + * Could there be a different approach to solving this issue? + + + If there are external references or other GitHub Issues that are related + please link them here as well. + placeholder: >- + I am trying to do X with Feedback Linker version x.y.z and I think that implementing + new feature Y would be very helpful for me and every other user because of Z. + validations: + required: true + +- type: textarea + attributes: + label: Additional Information + description: | + If you can, describe how the feature would be used in a mock code example. + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + value: | + + + + ```python (paste below) + + ``` + validations: + required: false + +- type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + + [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +... + + +This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..16e9048 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,55 @@ +## Pull Request description + + + + +## How to test these changes + + + +* ```...``` + + +## Pull Request checklists + +This PR is a: +- [ ] bug-fix +- [ ] new feature +- [ ] maintenance + +About this PR: +- [ ] it includes tests. +- [ ] the tests are executed on CI. +- [ ] the tests generate log file(s) (path). +- [ ] pre-commit hooks were executed locally. +- [ ] this PR requires a project documentation update. + +Author's checklist: +- [ ] I have reviewed the changes and it contains no misspelling. +- [ ] The code is well commented, especially in the parts that contain more complexity. +- [ ] New and old tests passed locally. + +## Additional information + + + + + +## Reviewer's checklist + +Copy and paste this template for your review's note: + +``` +## Reviewer's Checklist + +- [ ] I managed to reproduce the problem locally from the `main` branch +- [ ] I managed to test the new changes locally +- [ ] I confirm that the issues mentioned were fixed/resolved +``` diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..a3c78e8 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,43 @@ +name: build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + + defaults: + run: + shell: bash -l {0} + + steps: + - uses: actions/checkout@v3 + + - uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + mamba-version: "1.*" + environment-file: conda/dev.yaml + channels: conda-forge,nodefaults + channel-priority: true + activate-environment: feedback-linker + use-mamba: true + miniforge-variant: Mambaforge + + + - name: Install dependencies + run: | + poetry install + + - name: Run tests + run: | + makim tests.unit + + - name: Run style checks + run: | + pre-commit install + makim tests.linter diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..147c2e1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,72 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + timeout-minutes: 10 + + defaults: + run: + shell: bash -l {0} + + steps: + - uses: actions/checkout@v3 + + + - uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + mamba-version: "1.*" + environment-file: conda/dev.yaml + channels: conda-forge,nodefaults + channel-priority: true + activate-environment: feedback-linker + use-mamba: true + miniforge-variant: Mambaforge + + + - name: Install deps + run: | + poetry install + + - name: Run semantic release (for tests) + if: ${{ github.event_name != 'workflow_dispatch' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + makim release.dry + + - name: Release command + if: ${{ github.event_name == 'workflow_dispatch' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + run: | + poetry config pypi-token.pypi ${PYPI_TOKEN} + makim release.ci + + - name: Generate documentation with changes from semantic-release + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + makim docs.build + + - name: GitHub Pages action + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: peaceiris/actions-gh-pages@v3.5.9 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: >- + build/ diff --git a/.gitignore b/.gitignore index 68bc17f..a505aea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__/ # Distribution / packaging .Python +env/ build/ develop-eggs/ dist/ @@ -20,11 +21,9 @@ parts/ sdist/ var/ wheels/ -share/python-wheels/ *.egg-info/ .installed.cfg *.egg -MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -39,17 +38,14 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ -.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover -*.py,cover .hypothesis/ .pytest_cache/ -cover/ # Translations *.mo @@ -58,8 +54,6 @@ cover/ # Django stuff: *.log local_settings.py -db.sqlite3 -db.sqlite3-journal # Flask stuff: instance/ @@ -68,65 +62,28 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ - # PyBuilder -.pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints -# IPython -profile_default/ -ipython_config.py - # pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff +.python-version + +# celery beat schedule file celerybeat-schedule -celerybeat.pid # SageMath parsed files *.sage.py -# Environments +# dotenv .env + +# virtualenv .venv -env/ venv/ ENV/ -env.bak/ -venv.bak/ # Spyder project settings .spyderproject @@ -134,27 +91,9 @@ venv.bak/ # Rope project settings .ropeproject - -# mkdocs documentation -/site - # mypy .mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# IDE settings +.vscode/ +.idea/ diff --git a/.makim.yaml b/.makim.yaml new file mode 100644 index 0000000..e65b4e5 --- /dev/null +++ b/.makim.yaml @@ -0,0 +1,93 @@ +version: 1.0 +groups: + clean: + targets: + all: + help: Clean unnecessary temporary files + run: | + rm -fr build/ + rm -fr dist/ + rm -fr .eggs/ + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -f {} + + find . -name '*.pyc' -exec rm -f {} + + find . -name + find . -name '__pycache__' -exec rm -fr '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} +{} + + rm -f .coverage + rm -fr htmlcov/ + rm -fr .pytest_cache + rm -fr .mypy_cache + rm -fr .ruff_cache + + docs: + targets: + build: + help: Build documentation + run: | + mkdocs build --config-file docs/mkdocs.yaml + + preview: + help: Preview documentation page locally + dependencies: + - target: docs.build + run: | + mkdocs build --config-file docs/mkdocs.yaml + + tests: + targets: + linter: + help: Run linter tools + run: | + pre-commit install + pre-commit run --all-files --verbose + + unit: + help: run tests + args: + path: + help: Specify the location of the tests + type: string + default: "" + params: + help: Specify parameters to be used for tests + type: string + default: "-vv" + run: | + pytest {{ args.path }} {{ args.params }} + + ci: + help: run the sames tests executed on CI + dependencies: + - target: tests.unit + - target: tests.linter + + package: + targets: + build: + help: "Build the package" + run: | + poetry build + + release: + vars: + app: | + npx --yes \ + -p semantic-release \ + -p "@semantic-release/commit-analyzer" \ + -p "@semantic-release/release-notes-generator" \ + -p "@semantic-release/changelog" \ + -p "@semantic-release/exec" \ + -p "@semantic-release/github" \ + -p "@semantic-release/git" \ + -p "@google/semantic-release-replace-plugin" \ + semantic-release + + targets: + ci: + help: run semantic release on CI + run: {{ vars.app }} --ci + + dry: + help: run semantic release in dry-run mode + run: {{ vars.app }} --dry-run diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2f7b549 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,84 @@ +default_stages: + - commit +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: end-of-file-fixer + + - repo: local + hooks: + # NOTE: This is a total unnecessary check, just used as part of the + # template. Remove this after creating the template. + - id: check-python + name: check-python + entry: python --version + language: system + pass_filenames: no + + - id: black + name: black + entry: black + language: system + pass_filenames: true + types: + - python + + - id: ruff + name: ruff + entry: ruff --fix + language: system + pass_filenames: true + files: "./" + types: + - python + + - id: mypy + name: mypy + entry: mypy + language: system + files: "src/feedback_linker" + pass_filenames: true + types: + - python + + - id: shellcheck + name: shellcheck + entry: shellcheck + language: system + types_or: + - sh + - shell + - ash + - bash + - bats + - dash + - ksh + + - id: bandit + name: bandit + entry: bandit + language: system + args: ['--configfile', 'pyproject.toml', '-iii', '-lll'] + pass_filenames: true + types: + - python + + - id: vulture + name: vulture + entry: vulture + language: system + files: "src/feedback_linker" + description: Find unused Python code. + pass_filenames: true + types: + - python + + - id: mccabe + name: mccabe + entry: python -m mccabe --min 10 + language: system + files: "src/feedback_linker" + pass_filenames: true + types: + - python diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..48b8450 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,75 @@ +{ + "branches": ["main"], + "tagFormat": "${version}", + "plugins": [ + "@semantic-release/commit-analyzer", + [ + "@google/semantic-release-replace-plugin", + { + "replacements": [ + { + "files": ["src/feedback_linker/__init__.py"], + "from": "return '.*' # semantic-release", + "to": "return '${nextRelease.version}' # semantic-release", + "results": [ + { + "file": "src/feedback_linker/__init__.py", + "hasChanged": true, + "numMatches": 1, + "numReplacements": 1 + } + ], + "countMatches": true + }, + { + "files": ["pyproject.toml"], + "from": "version = \".*\" # semantic-release", + "to": "version = \"${nextRelease.version}\" # semantic-release", + "results": [ + { + "file": "pyproject.toml", + "hasChanged": true, + "numMatches": 1, + "numReplacements": 1 + } + ], + "countMatches": true + } + ] + } + ], + "@semantic-release/release-notes-generator", + [ + "@semantic-release/changelog", + { + "changelogTitle": "# Release Notes\n---", + "changelogFile": "docs/changelog.md" + } + ], + [ + "@semantic-release/exec", + { + "prepareCmd": "poetry build", + "publishCmd": "poetry publish" + + } + ], + [ + "@semantic-release/github", + { + "assets": ["dist/*.whl", "dist/*.tar.gz"] + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "pyproject.toml", + "docs/changelog.md", + "src/feedback_linker/__init__.py" + ], + "message": "chore(release): ${nextRelease.version}" + } + ] + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..35b5167 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/LICENSE b/LICENSE index 3836a92..969fa19 100644 --- a/LICENSE +++ b/LICENSE @@ -1,28 +1,31 @@ -BSD 3-Clause License + + +BSD License Copyright (c) 2024, Ivan Ogasawara +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 051e73c..a766f1b 100644 --- a/README.md +++ b/README.md @@ -1 +1,15 @@ -# feedback-linker \ No newline at end of file +# Feedback Linker + +The main objective of this platform is to offer a space to handle and organize feedback between two people + +* Free software: BSD 3 Clause +* Documentation: https://opensciencelabs.github.io/feedback-linker + +## Features + +TBD + +## Credits + +This package was created with +[scicookie](https://github.com/osl-incubator/scicookie) project template. diff --git a/conda/dev.yaml b/conda/dev.yaml new file mode 100644 index 0000000..b396ecd --- /dev/null +++ b/conda/dev.yaml @@ -0,0 +1,10 @@ +name: feedback-linker +channels: + - nodefaults + - conda-forge +dependencies: + - python >=3.8.1,<4 + - pip + - poetry + - nodejs # used by semantic-release + - shellcheck diff --git a/containers/Dockerfile b/containers/Dockerfile new file mode 100644 index 0000000..155028c --- /dev/null +++ b/containers/Dockerfile @@ -0,0 +1,63 @@ +# ref: https://github.com/mamba-org/micromamba-docker/blob/main/Dockerfile + +FROM condaforge/mambaforge:latest + +LABEL maintainer="Ivan Ogasawara " +LABEL org.opencontainers.image.title="Feedback Linker" +LABEL org.opencontainers.image.authors="Feedback Linker Team" +LABEL org.opencontainers.image.source="https://github.com/xmnlab/feedback-linker.git" +LABEL org.opencontainers.image.version="latest" +LABEL org.opencontainers.image.description="The main objective of this platform is to offer a space to handle and organize feedback between two people" + +# it is the default, but using it here to have it explicitly +USER root + +SHELL ["/bin/bash", "-c"] + +# Use bash in Dockerfile RUN commands and make sure bashrc is sourced when +# executing commands with /bin/bash -c +# Needed to have the micromamba activate command configured etc. + +ENV ENV_NAME=feedback_linker +ENV DEBIAN_FRONTEND=noninteractive +ARG UID=1000 +ARG GID=1000 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + apt-utils \ + build-essential \ + curl \ + git \ + libffi-dev \ + libldap2-dev \ + libpq-dev \ + libsasl2-dev \ + libssl-dev \ + locales \ + postgresql-client \ + vim && \ + rm -rf /var/lib/apt/lists/* + +USER feedback_linker + +WORKDIR /feedback-linker + +COPY pyproject.toml . + +# Create and copy virtual environment: Poetry is configured not to create a new +# virtual environment and necessary dependencies are installed without +# development packages + +RUN mamba install -y poetry && \ + poetry config virtualenvs.create false && \ + poetry install --no-dev --no-interaction --no-ansi + + +COPY . /feedback-linker/ + +COPY . . + +COPY compose.yaml . + +CMD ["python", "feedback_linker.py"] diff --git a/containers/compose.yaml b/containers/compose.yaml new file mode 100644 index 0000000..fe46e35 --- /dev/null +++ b/containers/compose.yaml @@ -0,0 +1,17 @@ +version: '3.9' + +services: + web: + build: . + ports: + - "5000:5000" + volumes: + - .:/code + + postgres-db: + image: postgres + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - ./tmp/db:/var/lib/postgresql/data diff --git a/docs/api/references.md b/docs/api/references.md new file mode 100644 index 0000000..ecc54ab --- /dev/null +++ b/docs/api/references.md @@ -0,0 +1,3 @@ +# Api references + +::: feedback_linker diff --git a/docs/api/references.rst b/docs/api/references.rst new file mode 100644 index 0000000..0769463 --- /dev/null +++ b/docs/api/references.rst @@ -0,0 +1,8 @@ +API references +============== + +.. automodule:: feedback_linker + :members: + +.. automodule:: feedback_linker.feedback_linker + :members: diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..254524b --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,2 @@ +# Release Notes +--- diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..89870b7 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,164 @@ +# Contributing + +In order to be able to contribute, it is important that you understand +the project layout. +This project uses the *src layout*, which means that the package code is located +at `./src/feedback_linker`. + +For my information, check the official documentation: +https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/ + +In addition, you should know that to build our package we use +[Poetry](https://python-poetry.org/), it's a Python package management tool that +simplifies the process of building and publishing Python packages. It allows us +to easily manage dependencies, virtual environments and package versions. Poetry +also includes features such as dependency resolution, lock files and publishing +to PyPI. Overall, Poetry streamlines the process of managing Python packages, +making it easier for us to create and share our code with others. + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +You can contribute in many ways: + +## Types of Contributions + +### Report Bugs + +Report bugs at https://github.com/xmnlab/feedback-linker.git/issues. + +If you are reporting a bug, please include: + + - Your operating system name and version. + - Any details about your local setup that might be helpful in + troubleshooting. + - Detailed steps to reproduce the bug. + +### Fix Bugs + +Look through the GitHub issues for bugs. Anything tagged with “bug” and +“help wanted” is open to whoever wants to implement it. + +### Implement Features + +Look through the GitHub issues for features. Anything tagged with +“enhancement” and “help wanted” is open to whoever wants to implement +it. + +### Write Documentation + +Feedback Linker could always use more documentation, +whether as part of the official Feedback Linker docs, +in docstrings, or even on the web in blog posts, articles, and such. + +### Submit Feedback + +The best way to send feedback is to file an issue at +https://github.com/xmnlab/feedback-linker.git/issues. + +If you are proposing a feature: + + - Explain in detail how it would work. + - Keep the scope as narrow as possible, to make it easier to + implement. + - Remember that this is a volunteer-driven project, and that + contributions are welcome :) + +## Get Started! + +Ready to contribute? Here’s how to set up `feedback-linker` for local development. + +1. Fork the `feedback-linker` repo on GitHub. + +2. Clone your fork locally:: + + $ git clone git@github.com:your_name_here/feedback-linker.git + +3. Install your local copy into a virtualenv. Assuming you have + virtualenvwrapper installed, this is how you set up your fork for + local development:: + + $ mkvirtualenv feedback-linker + $ cd feedback-linker/ + $ python setup.py develop + +4. Create a branch for local development:: + + $ git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +5. When you’re done making changes, check that your changes pass flake8 + and the tests, including testing other Python versions with tox:: + + $ make lint + $ make test + + To get flake8 and tox, just pip install them into your virtualenv. + +6. Commit your changes and push your branch to GitHub:: + + $ git add . $ git commit -m “Your detailed description of your + changes.” $ git push origin name-of-your-bugfix-or-feature + +7. Submit a pull request through the GitHub website. + +## Pull Request Guidelines + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. + Put your new functionality into a function with a docstring, and add + the feature to the list in README.rst. +3. The pull request should work for Python >= 3.8. + +## Tips + +To run a subset of tests:: +``` +$ pytest tests.test_feedback_linker +``` + + +## Release + +This project uses semantic-release in order to cut a new release +based on the commit-message. + +### Commit message format + +**semantic-release** uses the commit messages to determine the consumer +impact of changes in the codebase. Following formalized conventions for +commit messages, **semantic-release** automatically determines the next +[semantic version](https://semver.org) number, generates a changelog and +publishes the release. + +By default, **semantic-release** uses [Angular Commit Message +Conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format). +The commit message format can be changed with the `preset` or `config` +options_ of the +[@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer#options) +and +[@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator#options) +plugins. + +Tools such as [commitizen](https://github.com/commitizen/cz-cli) or +[commitlint](https://github.com/conventional-changelog/commitlint) can +be used to help contributors and enforce valid commit messages. + +The table below shows which commit message gets you which release type +when `semantic-release` runs (using the default configuration): + +| Commit message | Release type | +|----------------------------------------------------------------|------------------| +| `fix(pencil): stop graphite breaking when pressure is applied` | Fix Release | +| `feat(pencil): add 'graphiteWidth' option` | Feature Release | +| `perf(pencil): remove graphiteWidth option` | Chore | +| `BREAKING CHANGE: The graphiteWidth option has been removed` | Breaking Release | + +source: + + +As this project uses the `squash and merge` strategy, ensure to apply +the commit message format to the PR's title. diff --git a/docs/example.ipynb b/docs/example.ipynb new file mode 100644 index 0000000..b18fdcc --- /dev/null +++ b/docs/example.ipynb @@ -0,0 +1,56 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Feedback Linker\n", + "\n", + "Feedback Linker is Python library that aims to do ...\n", + "\n", + "## Getting Started\n", + "\n", + "First, check our documentation about the [installation](/installation).\n", + "\n", + "Now, let's import our library:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import feedback_linker" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c2648358c2e87b6cf126d43993cd30f0cb52c60d GIT binary patch literal 72342 zcmeI52YgP~8^@pfW{Qz0K~byyx2mnSTB`olg_gEPtuCtkTh(C|Evm#QH7eBJK_rBT zM8wwDjFn*|ks*SbwMC31^8Y>e=H?ypjtuc8@3}rl?s@M$_nv#c=XuUK&sjtg{tcf< z_`eo;Rh6g`5!Hno4p&K1e#dF9N5F7;cIjYp&h?`Bu_UK}Q%U|f@ogdn3YY>Kgm(H##)zqkqzj`Uqd$>gNLL6xO2}g>(?L=GROVI=$ zPZ~J(IT|_R9U3#I0reVO8X-`M;^@}96#X#ld78Ai7llO3p!j=B!F?4Nhr(^jcH8iG z3f$vz82)eC3RiG{4cv!=`+8j4oPDnvrttrUpIrp*Tm=w^aSFWEvO1W&PJbtLgzOFg zlW}zH?o2v#-IpS-gwVe8ztX(5%+Em%I?THq{kgOSZ9cXH*H2Pn<^}M(0(S#0E!(Bz zFSDXVQ}}$iG%%}xY9R+D$v5P+N4c>kz&%` zfQ&YyxH})x)`WhvEpY}N{5OmeGLpa|74AMxy#(W z41Ag91b3j-`-af&(@UVw_aWU=;Lbu`FHvH~HT=B=|2?=&I9b-?>iKh7+Hlkpb@68 zZVTK?qi+2??K{_rj@}wUd;STcsH-uMX>Lblp^k1{nQe}<@ioEy(Q%))(v5;6o{gO?7WUB)(*!Y{k6d8Xk0aJf(McP4MYDk#UE6jSbrrZ}oS<{o!!m6jzDj)7#L2ep>NivBVlM`pWv-8gQ7d!D#sEQEv9u{-yTzXLnWB(UbPyl8Cd#_S5> z7u@s0U81q`JSE?4Z7BNMa`3(cXB&2yyygMHJulonX!(wobToCHJZ`bLl;yeQ3BlbO z?hf?AD^+OSp-zxLF!dYO>^8<@t`&?hPb2}IEZp_}5 zwk0)0-}!X#PJ}BScGuk4n)^*~H{`x(eLakE9fr9nJcqP++GNb}R<8-}3il019WXbk z0_KbNL?6p8aDQ}T9#u9E^RM8pa9;UtUVRyGSuh}nmC(KQ(hjw~EHuI8Srt9Ykd*wcI zdR2^#>yG}GNV_T11$Uyr@c&{>i6Hd3@R%9v{aWAfxqZ%VT}o zkFh@ORtqu_j_Z2s(*~5rx{9qS{49?>EE@fpm-T5U`#w%PPWFH;_JoD+Do%C#`$+3_ zx$jU}i}o_guYcvSB0OHiw7Z17zmWX3wPZUr{-g1W+-ZJjE6mkjiEtN9guQ~_L*Z(2 z5c&9PMJbxP{5`C_wh-?=V@4aub~_5Ir9Z%(3I;!J5M#mxS}#(r>s9e z%-b2`hO1*8@@8hjb;Y&!;5eLejG66-y$Y()+Q>KY&d*GS<@j*CwkCdu`GiC9p1*Ma zMml`+B-rO|Jd-uto40YK?}k*R!#BLZoBP`G_72<3BfK`w3D}rdVBYmmv`afm(*hbw zoqDkT<*j+#;h2p%=ZfnJbDU(K^P1$P;LL^N>pbRE|KK(3`FZBT@ilk6urlKLADXbJ zF|CX4EU$xqBy|~d1&?jwb^FZabLE;f+^71NrWt{)FqR^)1Tepka_TBvBy{9-`ulhj z>hV)KaCejztkXyX$1z8JmhjrvAvJ3)Ndv}cX!=SA+Ht~x#?CEAe%e=HKbs8QAB}Ij zY*GHL;l3rlGS-6}gYgt`;9fNK4r}ske(p)kya_$JlEMymMLN}?O|fpEs^k2_Fp2t& za-hB=;k?Yd-ouS$mZ`Qf7^)h{B2MdtDq)1{_=W=9@zh>F2)KqN4;(!&SxR7FQyf{mg4Uc z6V4a9wGZ|e;<|v6{@)LEqrnqjA)k%EAdIQ7ez^7-Blkm zVU@T99hidlbRX2`T43Dr)5r&07tzUoNQ4K49qI~M-3Vs9R?LGs+7kC;cUz#oupVKH z^Sp-)>?d&$?W94dt5?oRBQ76j271ukkoRDV^2XjlQTWyi=vTfD-FCx*yM;A)e93oP zZ9GreUuN6_e%41=M>7OrZ<+{y8^3RctA%jNF~!df>oT^)e1m0(!&Nh3l{h7&^WG=t zF#qTvWO4#c3wW32uXCs1NG1Obp5TbGzZ$;zSCsR)*Y!+sf44P1qddOh2QcTg`ipZN zJ{__=7k$RB((>)PJuyS0+#wg;u%Ax=%KBuaqpq)k*Dbf?;?q}Se3|~fLa^I}eJrnu zZ9eQCJiCsH9o%?7DYo0JMmlF&e6LPF-i9tef%X0TDR5tByqoj1a{T9cCzNN6v0l&= z-m_7V_q^Ky_22O* z&l55dP`~{Yea97L?yWjG&{Oqlpk4Yan6vFO|8ux(i95^Yu2Z`)?(KiH=76p&n;-fN z>YsDbe|p*~?i*vu<6dv%lk){in@7o<-<@>-Fm!n%Fjw?@=dYcqdAkO%*G$7TL*19x zxY`nTw$mO>J%ce3UKkIfYa@D1uZ23^2$b`N{inIfd;4xKSo8aNlp8^EJtME_VO#e{ zp+BLXEbIIq&&~dB=$mN&EClz0t%qq#+?BQ(_b<(&kjU~m-`l-^Wg54j2V{7!MS1t# zR+U1dd!oJ*VQ{Z)@zj-7=HmtKSvu~Mm$_nZ-}g~p^(z#4H>}TR?-Tk6_lrDscQN{c zYk<2_#@Bwj0_u6Zz6_6Z*QHr*@_zWnOBh$N61E!NpGS9Z@jN%=K7FMtP4RDuvXJ-S zEtsB9NMDKal+#!#LnioS_rF`gJz0NWV7VQKeSyygAWj9VOC7uGi*=oA!NRi1gb9m4Ax6!A%w7}dG(igy{!0mV?FMjfk zC%*S{#EJJJ*2%Cw_s(B)8>h0||D2d<+P147-uBO2qJS-ht+-Vg=3WNxA)u(niu*S`D}8^N#o7O>Z6R_03BdH?RaeBo-;eZ=);__te2y% zHo=&t*~pLP`%v}y@j2(E#LU~UvH78I=Y3kZUO!ItkU#zLt)}fZ+GIzxZ@OcA#UA-N z#?LRFe%=E(9Cqwakj75L{oH&k-t+|e-u{R5*pEDJef^I2kWR=rfPPMXUTONOpYpt6 z-X0jM)dAPIUZj&NQ|C$duYhx3s{g7xeKSZYi(7RpBahLFP17R%t=IRmE&BE`b7^`D_bn&MgNoK;=C^yNM7juXZT4njSibxpzQm8QP8=zr%v$g7a~(e&-$ zTGV@lQkR;&#t~y->%gWx0{t41$QzdS73Jroq5RwlyXWW7=T*U7VLo=AhC-v>0rw?H zJL~MeT({6B&qo`pDg8E8r?9dka{=YBlP8P|V@D_QfI*_O5c!Y1@>e}QsUXUiJ?7mtH{ z5jyTW^vA6Rd!VUqGs%RGumd9suIcuE8 zx%rba(-9^d{kY$wzn;g{DaYv9F4$M89_rTLLRMEHPUrAEg}oXt{W!(mUX6U%i@c`4 zfCs4cIf*J%Dvh$JJHBfdp7EgW&~bN!U8e@hmw8sjJGXDK)!%qb`l0LRkj_hJb6jJZ z8_ab)MOOJ_c5o$W=gBsZWgoEJ1KUM3+#%V;+=@V-_*Tfw9F%__Q}ec-^g=_K`C|ww z{WAVBTC%wV^a;ip=b_!p{Q}WfFMyd3`pg>9WM5sJy7iIh{q~hmAN>&eVtg ziPO>4D8!S?f;qJBLOc4Zk0*UURMqV~PRyMGb~l9{SZ*>-Dt8`pGQp=k+8~=s9Cyx- zoX#<6Cs3c8gL%B~VvqQ87%L;Qm*$5$(3XVK(1}k#mYdNZ3%by_`Q2pay{sKA+0qF0 zuxhYtxPgZQwfcAke6uGA3k z!h2v3wx-DA^YLshuXB%6Hh0aQGnF;_&i9q#?i?tQ+&P`O&x*%a>^!*;>D&rqZ4_NC zCv?t#+(*yt>c_JkkIB;MVR-nEs#PnG=d_1@41qffU6H@~5ciA6q@_r)xBaDU2!=ZaU18cTrE*Wss-a2Fnj#&~ zN-nY7Rk=TW;}qm*A$YZxmTs+!^kUZ59Zj0KV|)zuqE70DJQRSm%CGH5U;ep%#`9}d zV9eZSXm4{_ZGD_7RxFD={2bbGpCS*jZhx>m=Wo|{+1&BYXYl(vIP+~!{jkOLTE|I@@|#wtqb=o^%>Cwvh)ekbO`!${m?fZ0X=aV?evG!=Hd1o>*#pIDelfn*#C#f z@BeAce`yHfG-T2pC&wxJ z+HAycDcTR~koQ>USZ6OVj<3o+IHE1`eL|6Zk8vDq$Im*O9qUPbr~Y0vybaSseYbvm z_+0%xe{oW|`)=(4otKQ~B7k%DS<$d{% zp_nIlTD0TrtR1)Jpcm|QyuLxv=RNVxqEB;G-a{fr3hs84=LPqomo;0^Z-TqvF3NZN z$pb>(?T58~`69Rr?n2MoPaY8RZa=Jrp0`idg1g`@^t^rM0l{5x7xj7j$pb>(?T59f z*V!j)!Ci0{dfq+F-Y;4ZieJ#U|RKyVk_MSb3W@_>+c z`(Z8Wb@s_xa2MQ#p103D5Pmj+W~~my8mD4jq`n?3>F0aT#e(}X!QGDL!g8KIaB&~j z+W$jvw=?dMSHiHz#g~G+opC>sx8fSS!9npR- z`SzQbvyI==T~5bb$a_g-&il_~%FNdv@uuZFnqWWDQnWQ**^iQ$^C9HDB(Z1asn{oUueANd z7?kI)Qy(whfA+yca4#v`d2i5U?0dFa+Vf9u%@qF^HNVd_)b0B81^430JrjEe-oqYn zx3HhwRqO#6jW+sU*o*!Rnz*>UW?F!gX0orLZWr8(D{Ec6le4hjVd^p37`qO2wsE9Q z?u>QCpToJqzQOva4bFojTH*eY&=FISf2Ig$!)b8*zBf3rn~ZhiM`MrUzBGGH2kQD` zeK2)5;A+XUXuT8m#dXEr)}@5A@if?X-W7XMyFi~igS#{KGj+m#v5qw0cMZMS(zH&` zB@eunWpHGSFz{w&fNk2iNzfSn%^@ zz5DMQ`Q^vyUB6>=U2DPbWq4hAobGwXe&zAH>nU<@onLFguUyW#U-=r{_hr8>=X&<* zO!`xdo@dGLWpux>Kl`%!J~cYl`qk*F_gnI(7`;!MN57ZR{lT(7LmQJp1!qyg0mrm+D8^%<}Y=>UUNAwdk7izAB3UCDret_+3^1+ls%8 zML(j%hgQ8mg?%}GM@o^s>UBpx;X{k>(-1k5m+V#jWmNy$%5?+315u9OgP@hX$W^`H zgYYyhd8qy>cr^UAl&5oF0#cwBy{-CjH{tG_!$FILsiD_u$wR*X`goIKkbW!3pl3;1b*k?(Q}?gy8NTEVw%acMa|k+?{Wp_x^&*TF^6e z^{MWvqx;m zA7@Yu zEIqyBxj45yJKN-pefI}w5IJXt+qS3!0AG&eB*oM{v(B>JJk<{IhP&lfi^@yRS(O=O z^9Su(&-q=@nc*M#_&pl@ZlgceV>HQU&Rf);_zg_r2hIpaROEV=(Mu9 z@3jAuj<^8;bE;$?1VgSzgasT8O1hm>{r3PE0P-k`#1UsGzPT!-|Goo*1;L86*dbI;U2uG%AbY5TyV z=LRyAk9cPIpN4=xn0{swVi4zQS>|cEkOBA?gvF z+N%PwVcpEg#w?UZ_V(5F{wypk##UBH=np41_H2f^=vz`I9630E0FjZwE7kl-vy+UO z9Lf+7GGzi{`+2I7Y6niB6(L+OCdF!mY-(&EK=d3}U0W%zxVU&@@MIk{KqJ^+*OB|f z@KdRS3Vb*f9jx)spi37taBhtVA2HfKKPCTi%?&KIC+!;?%_R@n z77VQDD&vQ=a&)M!rM zXma#$Db@(bWB7*eClMNsWrBkUALEH9Scih7G)V>eRk|^7Gy%%;^F{p7s9w@~fR{;o zGYWO_ru@?mN2AtRvSZ{BztrY2WCO4;v)F!}BWCv0jKd=2LEMaO$k!{OT*@&w+L1uY zQ5ZR(@8v_>i<1&tCFS$>SW@`#W(UHB!SQKL3&sQ!HFHZpkB60J(ydo;5wf9sbewI+ z)vbp;{#)}K_x^fjAnBMVQhbfm>h{6TFH*P!!bDt6M3GTJH!B+KpVNI{!sh1%LNsSN z@iL^kotr)l$QgH;7zQGazF`29BS&%gqm)gz4*m?+t#bM&>R zU#I4+vLG)HBa`zK>ga(J3-P%xu?|jr8>@E3Hv7KvhHmw&bm@ntlfu$EA#gHFIR-5a zoehtyO=E^=BZn0K-*J$N3A?|D8v@yI^et^=JGM}cNHR2(v?2X4obk=d2W1+0pN6d8 zESi|z@Nt9g9omh~wYIzbl0G3Uk3|mt(e39F>1vQwVO~UfI>81Je(Z<$XQSX~|NAe{ zLd18oHT-cR<22GiMp&>{xT1^;XPv+12qc`XDLEL=kA5qJ1dcfmB&FDF9m!5f#C*jE z!mmm;^gU~0N$Sh@45Pcat#-%b-T81n!AapKi}GY3(FQYPKD81!I?d^SBgvk`H&9Pc zQP~1{;rG43=Qey)eItaE(q_h`y#w~1^^$rnY4FyEH)la?V`M?jFoO~|`f9yOUfvwC zOeNG8tzTc}j$^i+@4xE1%P{4nsH9ErC@imHb-POi!@$%EV?^-bg)5OBa+lX&8ng1x z%DWg)Q=3R&-UWeSgM}Gb6c?=m__Ns~ia)iT#_@=sJ)7EYl@8FCZM_*#EMxB?w$oC- zICJm*Q>L1T7)w=VO%w(3xOP3+p57iz^oH`>Y#&W`q{hwn8iA(SXmN&Le0rvSIW?>N z=&p%tRE<@n?RW5^wg?hcMqmfBv72fe1;6ljEP5lZSKxm#rg-NQ>H1W%YK4a~JsAwA zz}onNQgtCX{WauXWdv!KFiEhMX8utSw;TE?&H4KzCKjxhI(}(zVji34Q8MHg&IQ8 z53QzCEhfqIR#I!nx_on5YoT5wZN=*Oi2&!jx`e#}sk7V>bWk4V#3Cq-o#iU-A>Prw zXv2IqiJgUXlWFnbL}LOe^$HDb&_3irDU8fE>{4tYtA zGAxL+Q=o+5@aH4q)l7Tt3^-u}(#PudAm)V`<-iD0kY+}*vE#0EUZWCl-I|LD`pwVj zSE<0dbzpfI6HCzaniUVYH^w-~3Cgf<2rSv9`X%*`sAAQAId!s1=5@vdIYSc?>=($O zZI)aOM|bBE-B^pU;I8*0V{=xKRAoKGm8iBSc`$pIt_EF-zh4%(x+j2Jrq~1fxukKV)MBkk<0<-|>wdf)1tT@y4DO3zMAF zA$wIYCoTk*4t-O0Mdp3=giBWh|2kE~(Kw)>*d<^NVf?}32?*jMGf8)jMS)KPS+4d) z?L&?2kUNG=Ub>)wLmE+tPf5N%!)BP8n};bCkbs^Qam_dgim!m#IxR3()X*8C%HeWE z;QXMA5ILvNDxS+HbI%`c1Z>(b6hFFt6`!Kzb!^+1{Sc>T0W0goP*#=(e!?(>%R{bZ zK@v?n`qF@AuN3wiOWPp@7!uwsj4?#hCA#xUb`7hnrxI?c=^LWNH0MBqH61WRl_2}; zRr~vQfo?2+#GnavQHSX7N8F#&)<2DZ&4Yeh4f_2+uQztKVl(2oWeK-xkL7^P*Dew&jc>0qGJb za23;yjmBVn^d}pZELHRk$rhF($X|A{*ndJ~d>BcV;+P!~Rs;W>UjZ9`{~mSOw*N|3 zNlt0#9D&Iu209EUDrZakSTM`~o$hfT+}L&ATiB%?-<(RP`3+BnHB}Tut2hZt8dDV0%|^ur_X5m&nV`v9Bm8vs7y=>#AtLx z)9RE7>3bE8BPPz^{3=74HVsi}cm6mnU$EX^Y}ZOv1On` z4y^0e)6zkKdvTOpF>(Z89yF@&Q{hQzgz;3bD;Og{A4!8_Um^sKl)SRCANCsGO)X-C z4(c*3r*+FC@ZrV#(~^vj_V|)CpWxu&TBr@+2x3gBbLcw4)rv(#sjYLy7!M8((q?)u zL!j@(Ov0Sj3SqrL1pQm_!k5>V`Ct5f<%!=}GNRF>utR?V>{_i^g;Rn5}|7^K5H3 zu_2SmLftb>Dn4dq(@$fQ~oA!}Cu%8fB1PoG+)BtLNFndTa8&;5P4uMfJju>bPGh*u9S z)g%;&Fyi)9&l*nWK3~&-N_N))S}?Vbmt4_Y-jg*o8rKfK;Lm-{Q0xgy7kK%G47M>w zl>xFnDBVB4(l>pB1Nm69l_uc7`L6ujkEu<;Od9L_xaEoZT$eOf9Gp2zwypmv0tr3G znqltah954>5bpQM+e{>>;IM|WZhn~IJyQ9>2dB4 zJ4j0tG8%Xz==@#oTkk9xrI^})GPgibN&ijCnqtU(a$^-TLj=Y!;MK8a6>NOQ25ndd z%MbiUUW@4rd?%5CXi=|`g#~!42LQVyui9Rf)3pC7nVz0e+TNdHG!$3_(sQIzikKJ# ze`5QIY#kydA^sOiE-7k0EomqD((a*Y)`sROvUWEh5HNI{L1)em=%vJQu?&9r4NA!- zK~Ju}feP~O|Dj%s_)b#7*qM@;jI4F#>>0(g@TVO1#}!-@%VDtP1uUSpt`4r>0qsXS zRG`15kF*2+ujukB5+EQR`@BH0WK$pu3YS>Hn`-$t)f#bUPknueGPSt90Z)$khE&cA zgSs5bEv-oZJ_c+sRBde@kDa821uil1Y3yp}%;--PCIwbs!S(%`FR-;@lw$Vwq{s@7 zo>@QReH0B}HOVDe8=p@1^rp)DGI2&LH!Rc8B6w8jH8_8DB**sEx65FR+K?M*)*=ed zkR*Yc`s1{Od>n5sC595uF88`hRpX|e182Q<>J<>TTx+_W7C0^?o38|Y;KarMLIZwP zRVAqTLRI%^68csQncn&UH{#xt#>RIx7TaUG*39uqxM8X!3b#&nkkN*rc@}vPZ_$T- zu$r>>9V75%{F^crgLsVfA|~&D-4x6$ELVqj>ZALrK?Koo=pMYG&A`5vcapk-8UZXY z-?py;rtns}TJ;o|64Q*sx=K|V>Fy=Sy6zS8>!khlT*oZ}Dp8o0ELwlHCm$n|jr;{a zd>jlu6slo=grre)#+0F^PADK6U41MeM;ft|WcA62m+GC*WA%;0h*LG&48%pe`Z+G|L878nh9dEn;S*EWSJbRI zs;#cZc6MPob|JM^c?ekiP|baebu*h-1~|oVQzH|F($flzX}2wg6_lwey9?UN$^gq5 z*$LJp12F@WFQ@TgVQ?n(BDS?pg&j4<_s)i5%((p}u1MrQ6$xcK^ZoLN9#|tWVl+9V z^C{Je<6b38?;%mc6XZ5H{=}xLXz-a{M!DJ4pqKtM1ksHvj zzB9tVX-Yg?heG_Dv@a?@%scw9tkv?D+aFaKm9X{nY;q{f2R{cY-uP?`d#BER?&tW` z+w?VuyFTpKIG1RPwCU&G$@b()61`hptfey4kXVAd7Zl&&L+^iohsMcNFlTi1caOFr zK*u;jRCz~@<%vS0M6xl#Dfclcb^{nJBU zfzQ(XlC+5w<=4M9o*gKNDRQl?C6ct*K)33WI%aZyS>RIrJMWh@Sa#JpPKHzm*%p2Q z<6E1VmW46sb_qMzjR36N=f@c45t*d1d7N)l7Ud>Ru|5SAX|Pg7SZ6}>W*jFL)6#bM zp2XJcXE}o+LF$^tUB*;bZni6B{~#)AY*S(~SBaFgDm1OoU2*AJBj@mYubcVuX@c{n zlG47q8h>sGD!sEs`?Ojhz6u-q#8e}zV-*JYjAFeIj=1PrsVou`SG%P%qGWFJw&=rd z-QUAF|Ar|4iN@ruWZvD~)|Zt)Ate!wyF6d-Cwx#~+dLA$$j+QWWJ6y$(m`o}xBU$a zhSc7@eL2;9m4%(dh0qL9@?|WVX7=`R4YSSk$>qrY-|AkMXGi0NrPP1ua2PNcL>uO9 z(ijx-0MT$PZO5Bt9fDgsf`<^Q)Mal|*SUrF!#19)(Pw1@G zwIJds+trWW2ZnjJ+?GfDN!6cBE`0LxQTvA*33I5Ud_~bylw7}~Uj=#TNG7wCZW-qw znn<&x>PJOFfq_V#aJ*D1lH;OmMWvv2UX(o-!ow|lT2>QnJ5}Azz<-ZM7N1M>{VN?+QS~tDkFwcTpGpQ#6{jYv6}8N$C!C@` zq`gc<(g;C$Sgd`Yyz%Vj8Q8}sR+2`r)Q?4`D;0mn2*lfym)POm#Tu$A|A?m>f9AM9 zLhEu1Hh8H)m$83K=;>E7-}#kH^D1~;-fdjgfPC8rmC7;Rt{#=E&_JeVW_}oGDRcJh zE&R`pZ`(!QoF^2<6#9C$fBSTSuxD&dd%Ke&`M&4I@B3A=;biNWbcxU9(9WT{(e&a*=OTPca{Rp0Cc41FvZUEZUIFX^n zeRXv7{bDD8r{&So(pG8rfGnIFfBkFDv9PQpOofexd+2a;njy7lJLXZXLspH%SYKT| z!jE8+Jj5>xI=hS)Sn=oQpXqlA#$#1>U-KvwtQGHVuUHuzssg%9M zV}=_vM>j%ljiHCK9X;!4h}(>%qq$XDg#-frU1FoWHXK@}L?m;R>e8Rjx_59nYeb<- zf4^4UY}D_Cw=jPD@-2Zp#|wkTxuJMB!HoD^JK}r>oEi{rFC5ndwToK|Qmdz#R+@u* zEK>!rrmp^KGU)0R9TLy8Mk;pJpwveER}t_dK@bI)iDu-K)zjEu4eIPpPHq&e{lbjP zd$eI^r@w2!h83y2&~x1&NhvMm#Sr?+>?n1(-S_r8l1$boW)&nmU=m2#-(#WHoO=~T z*-ivFAq2~3+D1f`{bg?8yvb8sWO@{jls*F+unk_%*QW{Vyxn$wHa544gcD*;@E26u z<;URNot-H}y$cDXJXc&M`e_URtVnq3bfL=v)H_~n0_AyyTT&K!Expp~9PxOS`NrF5 z6YVrdg&sksgr{?8W&Zqgx1l^I#ng;Ep@tAB6AtVYpdSm)tjsO5*1$pH+MIh5Oboer z?}`Zy78kNM%ldU{9UV`?t#I7QoIq8hE=>c;;QmzUNw?~W_d}LF&hYCxKY8?EFkIp) z4ly|RSJbmFzn8|UP@d)?G*p1#Aok8=MQCg+cgmgP06x=Q_x1=Q16JbCyBc*NM7!ZR z%2Z_$h!2(@ExYTyubo~K=uNu3L4)l2FtruCsj}JDh~lZBSM)VM78n@b-}}>%U5{&- zzCE$TWI{6D)9Zz@)0c_mHeLr~t59Jc&1c=x84hj3<(y`b*UW~jOsY!Z zo8)&Q`F#Rtd)yhLy0UesF*aLo(zJEBAZl0C%1rtR6T(;(^^_DHr% zlrQ`8pe37mAvw4?$^g{vGE~VXD!!b+pq<#|-^6)J3&4__n4=Q3MylMT!V$GWjp)3-= zCn|xQ`vHI_xMu}keX<zbw}`dxvy%pObaC2i5i&`frRn&E zhtvDPu7wqOFpTe2Yt6}&AFXD$Rknt)sZF@;O^p>qBvik$vTT*{6GuAG>eD=PerxUD{`J_^Sj#%?n0nu{>sKyty)T79w?PcA$NFHkTJzP>%8fGZ*V z*=x628yvE<%P?QY8Ye9DL!~ualO%&lIvWcH;5;yrH0FvO9)#o~n;+2JXU6bD54JSA88kbA%(7x`6a{zbS{I(0 zh&nzoZZx$EiIeaa7R-?5MJ!4W5n+g_j?ve9&R?Gva_wIVp=E9-$HA!xcW8(;wKpVi zIGZq`|E%WnKbzcwDZV+A0NkiN0gf(iAwKt_X4)hLk_IJP$Xid>p#`(U2~99%nCj85 z9R1ppBdMdc+==lgw~>0}`hcxudpApNcOZOUD>Lj$?abl?{Ck%3qZeQA)Nr(TRb&6! z8nHv;@1a%rKA}+nv42+hF&81cjSUgvN@dR^E9^k~_qoDW<6CgPa#~5OxcUnAa$}#?@y}%|k zlTi4Vkg~^F6%^0$k5_eCn>n zQcYcmNpV4yoOg97fXh=a!D{ui>X+}4ieMEFM$Gqare%+F9!AEa5e4;3w0z;mwbt#1 z>)o7W3SovY@z|5$-jPpM8PdTf3yk7_a z4=p8F_1HqGCRTRK*{uSI&jq6jbw4$rYj*Z_ zfYbtgrhrt%t(lnu91{=F%>9fg*79jF#-3R|Av`N@eM1zrqb-(b>-Z>J|MrzKU+J#O z|D=x2^K!9I-bfw!38U+x{Rfo&5Dkb?Q>)Kk_oMyoQjSV*hltK5qTcojmzG%M^W5VK z4B0aZY1Ayw)7^5sjB1;s?Pds-r=_6Iqp9N0vbU%YTlLLg9=x(qB2S#LB)0sn?7llr z<;_vEh>$ua)!NTkgi}+b`zf*C3>YFiI$X8P)G*RqTieru=Ca*?_9YW7mZH{L(7Pc| zxbIs6hyUlrL$2->al4^)#=iZ(@!u0SDgMA2Sex8+jU49JrmaGMtAK!)V2AdnKMACH z5V?J3Kp21?;|J>g{(i&B^vW~IqtnZ5MF?#g=P5=c`gA4S8*B5&>jPr)04o4+uzs++ z;^M8U;>c&KSf~2-kw~9xyxr)0`Q#hbFbiDD+m5IaL`eJc-yQc90VP`bh?KC7t5yf= zcZ~xNfek+uOEl2k1|kj$T&4kO=`%#T^tjRNy!l?9R950yvQcq#zY`K=rgZ#ydGBz& z)#?y^9Ew%)V%=fG?!=34qxt3z$vc5V$E7NAAF}(q28ZmX4AvlfY(5R9e1TR)txkYQ1O%=-*^nocvH5<5BdyNyhc-t^P0L@Dke-mDyOl zW#hXDws1~Y(#S&S^|iBNs|CLB6?n8}WnWhIYq6fa2hl`loW-GwQ`mkg z(6QYKyHV}WpPG*{x#_pT471v>FHL;cn5xgx_@WOk)!B8@E68b>K?TCY*!$0H3FJzX z#wCe#gU&b&rsCy7KY7eHE5xQB5#+!!k*~ zk6f@Ff8Ew!AR5Oaa*aBH$Bl-=A8L~B+}k_YNel-kh)zyF3Cn?bX?~t;CQ{a&WtyhC(FzKcv%podEG{nAHjyIMwXl(HbVj)6SO!*@yH=Vzru~BKmCL>j#!pN% z2PH@+(h7rgigdle2MS>J+e2AhFZ8uQPkn^(hgw?DEb{(@5bhi>uBqDbpf#Arw10gC zMpF~K|5Oy$eZEhnAZ~$@@q;E|`+d$+QmP?Okv6mHiL{@I6|orKRSG-?^8}$;yuQqg zj|0yNd?aZ9g~iN2kTbUh0X8&6y(8@xfY6CNK!79FK68gqdDZ2jHlzg!>3wV&T>t

=G>z&E&(?Z7+!JnyiDc|wVUTo=YnVKs4&H%~N+VX=Fn&GyT6V9JtI zf|+MBPfF>7C-9>CPxFXa)Fa__7!kk%uWg}#IFT18KO%s>`083D=1VZp$#8H&($%8+CK`Vl5a;xC7Ek#isXxV>Lgtiv; zP$1e9MP9Vj()unyAjO<<&L-T)-He8ocYfxJ84!{EybWncR!RzPt$Dpz7qyVqN*o^o zB{C#7znY+R{lVaB%|+Q(yp6bA43dUrWPd8AmSQ!33m#@W`to~ejh>mg?=al-ShN3V zTP=%wxg)^`*m(S;9R-7TE=rAcTqFX`&BX+XRxiKt3Tx9m2bBj`qpi%4v=IeD_Y)9r~Vuv6LsxosduJf0dLTQ#qw zxpZ}mo94raEd{Smnxvd+VS^7K@46NA@9s#k=T?luUdr8>_&W$`Q+%h?dU{rPcry(` zjJ8iItf-K8;6VIR_>Bl2@TayVaPyo+%8Gzsn%^q2BGzF^IJSZEaCI+aqIV~Tr`1o-Uu^TvkV^gt z?|4n?WXZz?X)Vj?JfYfpU$Xz40}9l1Hu(?bNc(D~} z8$U|oO|j{d5ddnx$FElFe9iWMWLba#?7&8hG1%ny<{!WxeC?#G4^l4tnG!#mA^l>hjr(Eq~-(NAMt9)I2XOS0uR$t%ZBBe-nhL zbm@ZqyW#`_whTlFjty@|d&o6#p&reH8qll|IN|*YQT>!DCI_#6UtGO}p<*Z;eT3&n zC-UYL?4KV#7RQ{@DfhZM&}L<4(WhqSnDcS&=@EdK`!N&aEh%{8Plc2tzs}ji^yF8z zJ4xKuv$k{LGk=@>K1C3owne&i<>^`}fwXiX*pnm4&}6QXxnBQyZg3XZ-AxLlqdVEf zi!<~J6e|}T-LEM86dlwWXMVIfOKOlaKUI`L1mUH$dbMgl+EO(j3@*~}r01>Dd8o%zM0=2`p@qFdF&@8SKQ zquti4dNGaGQ4=g+riD3H%^3U5%@0i;_O)&QSWp;F&D4ap5ubmLKA%Zw_=AfGiztv5 zElKsbzS&^0?Kf=edNxf6X&ky%nmQj1f&e{nK4X_1x4(qjAe=1bzUdqx=$UQ(n0wmJ zmrJC{5*US@ks%I!;^UJD`!jK?#DHxBLGMArr8j}0s4%OX78R&A+NB&J_Or(d$J`r?8pb%))%vSd|s6IO0l%^opo7QKWAIROBJE zx~EXA3jCZFvvgmfWqPGHUB-5McvZQQY1sF;cQ0oZ== zf}+h_3(_gP!&44bXBHB06`AWVn`Qe5&R}+IA|e#X z&*Re*grXy_s;UY)uZqM#mRi1@Bb1bpnXCF~kav5WnVA_~e>iD8mtv@5P~GkF97|5=*J zmq37Mwve@4S$s?k&~~-s)38#G#^3wZSb-MzT{J-aZH@!r#KIM8^cjkI$81wM|VWk{42&EWK|C zN&oRYgWSC0ZYrJAa$gz>pjo9ICG2Nl;~0|OzfI0qL6X9vAM8aI zKppUQ`ZT89?%ug^=NGoK@u^h_F?1*&um49vLJ*6{2dxO1kO@65>m#(gySvU^zYz%J zHS44O{zh55asUVvn6JhD6h1)w?}^N1r5k^lVlng`2G<{!skeijDk{kwv_$o zISJISTqd6(;U{mCKp5S_L@bGzv;=ybZ)mKF<~*MNU34&nY8jTQ^VFvaU8Z|7ZiWYB;cEsj;$8tPy1) zekeH^(&r5sH0hn4#j*_xQdYx^l!T1_AzxfuTMHornDn!0C ze+@W$dAKNSh(XU=?PRB>)=pjj54C%Hc@TRov4iLcul=G8zPy4$o%e)JOKYpi=u6^0 z@7;R4N1e-3Zj0wro9c^ZJ_SCn1r9tq=^A~f{aQz4p-lYlK)H)NgYdGgi=yw#m)na? ztwQCGc8`ngfJ`3Klb1vcY-|_+B=;ZadPBH;sqRh|wEy*sd8Sa;vg2hE78VYUTmAxE z+t4tu73Al1`)H+(Tmd?MvwCz{LhCH;pl4+4ODG(^e3|S%8K{?Rc8G4?^vBK?a8q58 z@%I-_Vbu-l@-yh~34-DJNkm2#uU0x`G}Vo;_i(`^jYbyZ|Mr|00{?=Wo}RAV33w#y~l96{6y#Me6w%&jTzV5^T( z`nMIY>!lT27uWM;sNLaMv6uU^#*>@g5ClHI1LFI0ks_SbzQ(Ps*qNF_huo#<3Jsqt{+H9NRR(&*7=-Hm>J+ty;0iJ?6LvKj1&{=#=&;lQiL?aBx*GsLq!~>Et#(lcGGfj4XrT^V@1bVUY=0h&g>(b_>d?@{}8@MM4fCe<#>mWdk{XYjU z6PK;J(p=L_XEBi#mm}n3U8JC+L+JnEhj-5~{*%z8?{OxL?@E99@86{te@MC4c6UlA z((QF7k|pGWYqR8_?wvP3{AWAl>b%{de{4*Fs*`*BdVefU=)vX5D|cg^eg934pI02v zb9htd@B#JfZ|yg`zt!2Mmy>@TC>a?mHXc0ASHl|rRla=ocrKGyYT4N_vE6(pa(}z4 z(P`}m-wwy1m-dg0l=t!^mMSI=D`yEiA&!iUJZ_e-TrUw8NT}P4XMC0@47dOGjo|&= zhnI)R^Nm|l{jaR={jne{JbZ<1YHDium(1p^t!-?^zfe7yyVAxGeRkuQee}36VAj2& z0vN6QteL4X6yetT{#8>G_kHDt&405|*Y59zQ2Ta>l^RBwiQ(D%+e?~Y<0^^I`&v3a zhZ*Rej4vWKvzdWlZk12qv(e~YX+LJK(J!xR7b=u~zdf0O+sHYWw|gKFjPlC$jhmfSJ&LNlxxIBsqeG<+9!WSogRr7XF$h zU`gB8r_(zz@ulm0`0yqHa-%*E7p8adA25K|=f7l^4>yB(Vj(AIoS#1vY_fb~6v@3j zAUfKtVe9DVFfuiTfe-mF5dZ$oF)%c=vvell_-_(Mufvx_#Nh?5-ojtqp*!le#hwbn zitqaN$e`0~kLuy!QQSBPd1x~W3zNBnsuu4zo*nGOI?oY~UwU=wmR1r6Q@P<=KgfT* z=F^JWojre9Zt)@-iXn{4+ahnUS|>CxFo;cQH66Zi-W@inyF6RUrDA3Vl9G~M(}*U> zhC&{3U{GkdU~r5aC+c#r&jFLvVEwD^e=z|v#nY@F#I~=7juI~K?!poi6K6XUe*O#& zK|mi%L|`@ejWd?QszNl<>GszF5ZGETA~_t#etC5j)Y{tm>IVt70|Sz;ZEw6;9~X=O zqiwC|TIa!8?-y1!w&4eb)Ui}C-7v7x>io#%&0Sb&X{p{}MFk@@9o_CoBHg3E*WIb# z&K~;ZQuUcz3a5*+^X1#@{`E~lKZ5DebnoT;y=KNrN>UOzJ3D6k#}i)Z)O&cxtN3e+ zJ4x5OHAV69*9Hnm;!ftzMPx;=5Hj%Y=WVjq!tUtm8X6sqGCnc!UsP9C4um-U(?>tN zYJ=`rfd|r^${M`wc86}27ov{_r^+>Q=GPr;Ny*Uur6sL*0hBQDqT=G+$#ntkI%^WZ zR_a;%*2Uq{7RO;qZa@NAH>WZ4^TR(BJUl$dRy{Ax2*^ff33|y3m!-4Ykg{e8p%D>f zmiSpAAKA&st-F@j&A-=47Jy3~0DI`!=9>ZGi&a$r&e*uPaPsr>|BQ{L#U#kQH+;K~hga(vkqsC#{-Icx9m-xi4QdRt4>miO~>DhhxM%wefKSy7k`E z!^9J2Wd(XR5!7gv=Dm@&u6Bo)TlTd83)0&cB27y+dlM5A+lPk;A|fK%-@c8ewv3LB z65h2oeKf(O)@a86hDSe*mfG~yxVW&jvx}{*1%-x%K>_Yb`GPB#985RgVLT7dkQmKz za6g$Y#*};htK$9Vw+R6#t8^Osg4w!zo<1tpy&Trsq9I%o?+18RgN}&A(y6g2E!GcG zNQLFL>ICWCexHG6(P7gJZL@%O-qT11^LiDQ7(IRc?UR$J zygc$4d~UR2bKh5wV?t6v&p=k|->}^r$lbPYNOr3Y*> z0800_U>-Z2rKdMYaw_OgUm_tep7ErT4YgExX#M>=y`Yd#-A=f5NFm?RG@Q3WWWJ4* zp~~M~@kExNY$BPuH7aEjGMvJaA6*NXU`Y1C=TvZ>VdL%Hrh0O0HCIx0mfFAlpQ&8m z^*SdFn+$Eq$(vZsNzSsLF~|Nb?d(c<>`j7}>2t9iXwg8N}@ zM+)VH8@Um3zod`IpT^+s)#cnX_w|i{$AuNO)78qXFiw1648aZj{p*_m+UXk#q)$S4T*&}Ibe>0pD@H`>C^5J5@$-AXpsQ~_ zQW=oZiWq*(9q{wMXSZ{YnOADD1{D?-O8YfKsz|+P>q7Va)h~PWun7w?59jM#CwPEo zNKz^CRbI23X9d4Sk9D=#AwCSHYhXo9rq=q&1OKz$N;DIb0J=hI&CvAHuo0y4IKeSi zrKX~S(W+ig%C%|RMpqZPEFJ%sF`tle}9M2@*uXTs;Ho#w&FkC z<4?#|e|xf^=Kp$WydHZpIx7G4XQMYK9Fos2TFqhnS2xkpmQ03RjU<*{@1mD}35Gxj z(r;m*^QWeeUJn&Fw}z>H*+6eE6xmdIEV^)7$(6L1wliOw1DlibxN&RUd(-+Nof zqiOW4S9|+-lmY^}$8XD)3q)R!*OK|&Ps^OTA?U|Nhfr6^MTg+0s(dH=f>k?$=>Q#P z()jR=P9M|WU^d6+0SbsRz?({=YgyO3ztN88a5hgqJRlD)4abudCo>tUMZ{LEM38XF za(Zayd9Q5WZAW3QRdie0rQG18%6ziH?%4Te`|H*7l!(d|OD%8ta5`Utt7~#~m9W!$ zp1JP(%g6U4SMT(98eXMFI&y%dbf^tC01XjQR59FoY0m|Zb%DT@(Yxd9{ek@b+iyvdrx4GmocpPs?wI4o4e}L)r%IP6*Lm`g#}6{Wphfd&4s%Cmuoz z2vNtv4HJ!@@<8onWy)u*v&&q&cQoPSvNh%5+lQr@Z-sbl6-&Rfvxytf3}VzBc*a)5E zDs{#tmu&bM@c8m##X?0%NnARmscR#(q*$)bs3bLE#^HE(!d><3he>8Qmx?y$bz~zV zqZ=F?Tw?qE*E_|3pwXka(vy94FB)z#wF7-TFd?Z4qR|^a2<2Fa`x{TtTujELH zm0HS_0z*Tz8&Vw_+1P}9Bh#{6Snt|Z>fpUvhj;k?<-y7|Xa}oz{PPXbdVC|d+YmE* zoyBHEVaP42YPmY;TV`6)YSBfn^14RQ$wJsdWxzl$)d|c)nYa_}w_FuBXXn0>SR%)r z0XgPJ@4DB!s=w|=|C&eu6@}$XueIV<-7-s%X2GN960Dn`A7N#I`L3M$ax0mzdZJ=1_*vkR3T6UM>9u z0sgo)8cH&G@PQXEo`BIbR^3m$f!ok45Khukbh9*hK z2!{;O!``4o(n3vzOPrFVC7h8kH4W{o_9|drW4SQObXbX=AS`$BBQTDg-THyL`Mu*y z4OmQr%inHoZ4KnR3<8qz03lib^4+i@pZiYC>l*~zGycdoUH>QJfjLopCuCY$~Z--h-eiXvQsJvJN?=W^z=PSL;>*1 z+2c1g`rQR;!*To``3~db3T2?9`628Ysc2m7*LBJ)L3hAdif?Rnw#`reuP=@~5X7-z zQR)-_!~fNA=J8OjeHb6cp~aeG9b4%P6VZY+O-4l7WzQNq_Ut0VAj*-gDCD)2rNLN| zC1sE;l6^4Np(tyavCo*{z3052_n-M==JU*RKl9vkKiBVjUH4C4e6R&h4*~iciHUyF z>7vM>+tFwx0@|vRXwyYuTNu#&k^6jK2kr@}>56f4ehe0 z!Lnf_t$HGeo1Ry8k_|0g-Y@5#5K*iHF&CIt>%&B<{%5aNW)D)=%?L;tWBqy)fk1O-^ZP?Nf zCVIN-ih8$n<00Jve>yV1_t3sjrIkvh?gcp1vANXrfrWZGWU|(iXqzYgx}oUL7RH8m zt{P9kVJmcv++=qz{BNWfD(q>5a`D>|(c{9{3N@zoJ%A^xzv0BS$q2YUVp_Vyn6qBf z8A2r%0^|ZNPW%+CDcZB#kvSpSruh zBPo8U?9kxPhS^ywO`k$2DjIsawK%miHNkA>;*`gz}H&CZ`TL=h1Xz4FhNAlUB^PoEP2t z%)^;Aw4hPWy3wChqI$V~o$ZfUP+@Yp&gRHF>4tkxFNG7$Y;0{^`q#2+C+wnVl2i4} zg5iBVc)Bx5I_l2Gb1|C_;ZUsA%?fW1N*Ky#qQMx8{qV5j9ACBSF8_V$l;fRs~s zU6jo?_QwxSvz$PkbK9JRMC}|-u}E&s5m`yjgIUP*m+F9Trxu? zL6LA0?KmBs|%%K;t(%}ki?kC`8;~#=D^(XZ7^!7Z)y7Yxk zSe_4F;^)}2hUL%84LDQNiqE*)i-`eDrPudvfN~PLvWEFnL_}Nu&y(j7Lg_ec9GD=b zgsu~Uf-ks@o@TokP8Phco#N%?RS%>%fzteHN`86$w@l+ANz(Q4wsXgQrNLix>QOAl_DyRTtKY?wgC>XY@wDbo!E;;~#9I+gID;Pkll| zeCjU)U#hIEj9+9|f{RbuGVrf0OPiG8$Dl0j&2?b7-Az2u~nnXHPu_IBoXUM$M&eqvdmmQ35^tJe5!YFMNK+11ABOOvPm)Ii%a#} zCHw$&v|ydh0@g%eLp@;RBH0YH{;1htfq zHdQU?4$$z>dlNt4i?&V^=*>b$IKt>#V%Ujh^X9k_1pFA_hvh`~MB$TJ7rl{Vm9wWw zw1v`(OQb43P&aqO^28YVNIBfQ-=v0$_Fqn6Y>p#n09AUDbi3le&x2 zFBxPGAS*29I|7}Q_Vf?tEA4mAZftB;du?+jSNu!3&CY_G)J&{(0NO~td%9FMgkk?<4#F`!{wmQ7KRbKlF3z;IxKQbX7b%vf z?TV~zk0;{1o+B9y+HsqA!NtRGTFwAxgi-6OT2QburcXYY^4s%t-A2=2GaF$9_aZZd zalS@!=WNx;N^=f66#h9GX19*HHKh6#8qdcl92KX;y_frb+a5JNlM}Eoc)&R|(-0si z-JgRGXvw`&-^E3IGjdct;*qI*$7}8QYM&pe;=4zenmHB~9O{`H3fPIt@Hi)F!C9$P z;4pR%7#J8(S9fOLZl*@5d~6Qt+Sy~&(A2}%)awc5PjSDaD7`N)+HB2SNM4slJ1H~X z2EOb`YQB*o(Rpe1EU^R)IpR0Up4fc5Bp~CC(a==VHKk9z+`xs?+4Y^F%(X)8o9;{Q z^GTV*!i0&hB^_611apxgKEv?iiJff?)viPJMlgx1_a*jsq$_vh*MJswxSxpk zF)KfwNzEX?wHT0|6MVCuG&Wvw8^}&zWA1-j8TAI2RW#g-Q?s(l5JkOJMusF^11eFq zKV}4PZ2~af{2KVrU^kM6ODvwT%y7ypwx!`>gIFoas#87SR+Uxy?_-3jj6qx!vaPn9~e&+s|$DYvQPbwKKP=l(48nPo3%xr0#|e#?6m!Hh;ENB8z>vZnMl|A&WJDe=XNzS#F{69W51*cl9V#|(pC&vIlrzrDuwKI(0nQ_;{z@4c`n#M+pQ;+% zPkOC!{gl8kk@)O+4)@EgAIOu?I~TJ20|ImbiAz883DGZVRb^lBe6^*aK{QqBY8pmi zd~#AoUEK!n^JW{s)@ONnppsNM^61n#QEt%jCN4hSQ>fd_-k$r)l`9pQXF6QY>+9=p z<%P;$y(+2f*zuKw1v*(Y$xB%_D(Vov*blTxcTbO&nOPd+(s%#yBRo8#d(IZNwwy{N zE&RemfVF@r@K17bI-jk&gc^_>bx(Sf&Ypdd89K@d^hjHq7D#V+L{u2UwcXr)e$DP5 zC5?|;SfBRsxlgj5jndL$?{GN^GNs^P;3w;O8Y?)MgQu+sl?vAUIKwlsFU5|>#Kcf2 z6jS3E*%t*MaDkFC!DDWN4e9{+0JDle5x*{d{5Y?cmR2oU(6Xqu_Rj=8J$ZR~2y_$^ z4URR!I+n%?x4-hU5c@aHQlbmu1FU??IphE5R}3Lo;{?~sQ^Co(4BT>HRz1qLuoGZ!Asoe|OR2cqzwDMQ6%qWQ!plQ9H zz%`iIdZ*|jTQqou?!`FfvQDlkizd|mq55oqo744pbO?{57AtmR{`ZUl4D!lXKdBQ^ ze+$pYI=RPPSXn+Z@Hsb#Ny|ehm%Dpe?`A_+ArS`~6Uw;Oj`ja_Hq%rq0`6z)$Uir> z^V&dHGmF~-3&rn;RUqpRJ1(;_J=N)tS1563tJJbrwF}K|DaXz|hA#afb4*hP!UZu> xAnCA%5P9-iNZJU81x^FWH#hfnT=fM*+Pv9^oFg;{vhZL(gpTp`O07E&{|o35TDAZH literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..5180fd7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,48 @@ +![LOGO](/images/logo.png) + +# Feedback Linker + +The main objective of this platform is to offer a space to handle and organize feedback between two people + +* License: BSD 3 Clause +* Documentation: https://feedback-linker.github.io + +## Features + +* The security of our code: Bandit is a powerful tool that we use in our Python + project to ensure its security. This tool analyzes the code and detects + potential vulnerabilities. Some of the key features of Bandit are its ease of + use, its ability to integrate with other tools, and its support for multiple + Python versions. If you want to know about bandit you can check its + [documentation](https://bandit.readthedocs.io/en/latest/). + +* Finds unused code: [Vulture](https://github.com/jendrikseipp/vulture) + is useful for cleaning up and finding errors in large code bases in + Python. + +* Complexity of functions and modules: We use +[McCabe](https://github.com/PyCQA/mccabe) to identify the complexity in our +Python code that may be difficult to maintain or understand. By identifying +complex code at the outset, we as developers can refactor it to make it easier +to maintain and understand. In summary, McCabe helps us to improve the quality +of our code and make it easier to maintain. If you would like to learn more +about McCabe and code complexity, you can visit [McCabe - Code Complexity +Checker](https://here-be-pythons.readthedocs.io/en/latest/python/mccabe.html). +This tool is included with [Flake8](https://flake8.pycqa.org/en/latest/). + +* Integration with DevOps tools: We use Docker because it allows us to create an + isolated environment for our application that includes all the necessary + dependencies, libraries and configurations. This makes it easier to manage and + reproduce our development and production environments without any conflicts or + inconsistencies. + + With Docker, we can easily share our application with others and deploy it to + different environments. This streamlines our development, testing, deployment, + and collaboration workflows, making the entire process more efficient. +* TODO + +## Credits + +This package was created with Cookieninja and the +[osl-incubator/scicookie](https://github.com/osl-incubator/scicookie) +project template. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..e55bf55 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,41 @@ +# Installation + +## Stable release + +To install Feedback Linker, run this command in your +terminal: + +```bash +$ pip install feedback-linker +``` + +This is the preferred method to install Feedback Linker, +as it will always install the most recent stable release. + +If you don't have [pip](https://pip.pypa.io) installed, this +[Python installation guide](http://docs.python-guide.org/en/latest/starting/installation/) +can guide you through the process. + +## From sources + +The sources for Feedback Linker can be downloaded from +the [Github repo](https://github.com/xmnlab/feedback-linker.git). + +You can either clone the public repository: + +```bash +$ git clone https://github.com/xmnlab/feedback-linker.git +``` + +Or download the +[tarball](https://github.com/xmnlab/feedback-linker.git/tarball/main): + +```bash +$ curl -OJL https://github.com/xmnlab/feedback-linker.git/tarball/main +``` + +Once you have a copy of the source, you can install it with: + +```bash +$ poetry install +``` diff --git a/docs/mkdocs.yaml b/docs/mkdocs.yaml new file mode 100644 index 0000000..6281afc --- /dev/null +++ b/docs/mkdocs.yaml @@ -0,0 +1,150 @@ +site_name: Feedback Linker +site_url: https://opensciencelabs.github.io/feedback-linker +repo_url: https://github.com/xmnlab/feedback-linker.git +docs_dir: ./ +site_dir: ../build +# extra_css: +# - stylesheets/extra.css +# Page tree +nav: + - index.md + - Installation: installation.md + - Changelog: changelog.md + - Contributing: contributing.md + - API: api/references.md + - Notebook page: example.ipynb +theme: + name: material + features: + - content.code.annotate + - content.tabs.link + - header.autohide + - navigation.indexes + - navigation.instant + - search.highlight + - search.share + - search.suggest + icon: + repo: fontawesome/brands/github + logo: /images/logo.png + favicon: /images/favicon.png + palette: + scheme: slate + primary: white + accent: lime + # custom_dir: docs/overrides +plugins: + - search + - macros + # - autorefs + # - exclude: + # glob: + # - backends/template.md + - mkdocstrings: + enable_inventory: true + handlers: + python: + paths: [../src] + + import: + - https://docs.python.org/3/objects.inv + options: + docstring_style: numpy + filters: + - "!^Bounds" + - "!^__class__" + - "!^_filter_with_like" + - "!^_find_backends" + - "!^_key$" + - "!^_literal_value_hash_key" + - "!^_log" + - "!^_nbytes" + - "!^_safe_name$" + - "!^_schema_from_csv" + - "!^_to_geodataframe" + - "!^_tuplize" + - "!^ast_schema" + - "!^backend_table_type" + - "!^bounds$" + - "!^column$" + - "!^compiler$" + - "!^context_class" + - "!^database_class" + - "!^do_connect" + - "!^fetch_from_cursor" + - "!^get_schema" + - "!^largest$" + - "!^reconnect" + - "!^select_builder_class" + - "!^select_class" + - "!^table_class$" + - "!^table_expr_class" + - "!^translator_class" + - "!^Options$" + show_category_heading: true + show_root_full_path: false + show_root_heading: true + show_root_toc_entry: true + show_source: false + show_modules: true + - mkdocs-jupyter: + execute: true + ignore: + - "*.py" + # execute_ignore: "tutorial/*Geospatial*.ipynb" + include_source: true + theme: dark + - literate-nav +markdown_extensions: + - admonition + - attr_list + - codehilite + - def_list + - footnotes + - md_in_html + - meta + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + options: + custom_icons: + - docs/static/icons + - pymdownx.details + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.magiclink: + provider: github + repo_url_shortener: true + - pymdownx.saneheaders + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - tables + - toc +extra: + project_name: "Feedback Linker" + team: + - name: "Active maintainers" + members: + - xmnlab + - name: "Former maintainers" + members: + - xmnlab + social: + - icon: fontawesome/brands/twitter + link: https://twitter.com/xmnlab + - icon: fontawesome/brands/github + link: https://github.com/xmnlab/feedback-linker.git + support_levels: + supported: + icon: :material-check-decagram:{ .verified } + description: Tested in CI. If this doesn't work for you, please [file an issue](https://github.com/xmnlab/feedback-linker.git/issues/new). + bug: + icon: :material-bug:{ .bug } + description: Should work but doesn't because upstream is broken. Supported on a best-effort basis. + unsupported: + icon: :material-cancel:{ .cancel } + description: Unlikely to ever be supported or no upstream support. + +copyright: "Copyright © 2022, Feedback Linker Team" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e8d0e9d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,106 @@ +[tool.poetry] +name = "feedback-linker" +version = "0.1.0" # semantic-release +description = "The main objective of this platform is to offer a space to handle and organize feedback between two people" +authors = ["Ivan Ogasawara "] +packages = [ + {include = "feedback_linker", from="src"}, +] +license = "BSD 3 Clause" +exclude = [ + ".git/*", + ".env*", +] +include = ["src/feedback_linker/py.typed"] + +[tool.poetry.dependencies] +python = "^3.8.1" + +[tool.poetry.dev-dependencies] +urllib3 = "<2" # fix poetry issues +pytest = "^7.3.2" +pytest-cov = "^4.1.0" +coverage = "^7.2.7" +black = "^23.3.0" +pre-commit = "^3.3.2" +ruff = "^0.0.278" +mypy = "^1.3.0" +bandit = "^1.7.5" +vulture = "^2.7" +mccabe = "^0.6.1" +# if you want to use docker-compose from your system, remove compose-go here +compose-go = "^2.18.1" +ipython = "<8" +ipykernel = ">=6.0.0" +Jinja2 = "^3.1.2" +mkdocs = "^1.4.3" +mkdocs-exclude = "^1.0.2" +mkdocs-jupyter = "^0.24.1" +mkdocs-literate-nav = "^0.6.0" +mkdocs-macros-plugin = ">=0.7.0,<1" +mkdocs-material = "^9.1.15" +mkdocstrings = "^0.21.2" +mkdocstrings-python = "^1.1.2" +makim ="^1.8.3" + + + +[tool.pytest.ini_options] +testpaths = [ + "tests", +] + +[tool.black] +line-length = 79 +target-version = ["py38"] +force-exclude = '''(?x)( + docs/* + | .*\\.egg-info +)''' # TOML's single-quoted strings do not require escaping backslashes + +[tool.bandit] +exclude_dirs = ["tests"] +targets = "./" + +[tool.vulture] +exclude = ["tests"] +ignore_decorators = [] +ignore_names = [] +make_whitelist = true +min_confidence = 80 +paths = ["./"] +sort_by_size = true +verbose = false + +[tool.ruff] +line-length = 79 +force-exclude = true +src = ["./"] +exclude = [ + 'docs', +] +select = [ + "E", # pycodestyle + "F", # pyflakes + "D", # pydocstyle + "YTT", # flake8-2020 + "RUF", # Ruff-specific rules + "I001", # isort +] +fixable = ["I001"] + +[tool.ruff.pydocstyle] +convention = "numpy" + +[tool.ruff.isort] +# Use a single line between direct and from import +lines-between-types = 1 + +[tool.mypy] +python_version = "3.8" +check_untyped_defs = true +strict = true +ignore_missing_imports = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true diff --git a/src/feedback_linker/__init__.py b/src/feedback_linker/__init__.py new file mode 100644 index 0000000..2a57c92 --- /dev/null +++ b/src/feedback_linker/__init__.py @@ -0,0 +1,19 @@ +"""Feedback Linker.""" +# mypy: disable-error-code="attr-defined" + +from importlib import metadata as importlib_metadata + + +def get_version(): + """Return the program version.""" + try: + return importlib_metadata.version(__name__) + except importlib_metadata.PackageNotFoundError: # pragma: no cover + return "0.1.0" # semantic-release + + +version = get_version() + +__version__ = version +__author__ = "Ivan Ogasawara" +__email__ = "ivan.ogasawara@gmail.com" diff --git a/src/feedback_linker/feedback_linker.py b/src/feedback_linker/feedback_linker.py new file mode 100644 index 0000000..dd0b80e --- /dev/null +++ b/src/feedback_linker/feedback_linker.py @@ -0,0 +1 @@ +"""Main module.""" diff --git a/src/feedback_linker/py.typed b/src/feedback_linker/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..9e14800 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Unit test package for feedback-linker.""" diff --git a/tests/test_feedback_linker.py b/tests/test_feedback_linker.py new file mode 100644 index 0000000..08c3d84 --- /dev/null +++ b/tests/test_feedback_linker.py @@ -0,0 +1,13 @@ +"""Tests for feedback_linker package.""" +import pytest + + +@pytest.fixture +def response_pytest(): + """Sample pytest fixture.""" + return True + + +def test_content_pytest(): + """Test with pytest.""" + assert True From b882b0c5f8c26e0596833c12a327d0abf5741f57 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Sun, 18 Feb 2024 21:52:53 -0400 Subject: [PATCH 02/12] add initial code --- .github/ISSUE_TEMPLATE/bug-report.yml | 282 +- .github/ISSUE_TEMPLATE/config.yml | 24 +- .../ISSUE_TEMPLATE/documentation-report.yml | 88 +- .github/ISSUE_TEMPLATE/feature-request.yml | 117 +- .github/PULL_REQUEST_TEMPLATE.md | 10 +- .github/workflows/main.yaml | 51 +- .github/workflows/release.yaml | 6 +- .pre-commit-config.yaml | 134 +- .prettierignore | 2 + .prettierrc.yaml | 6 + .releaserc.json | 1 - CODE_OF_CONDUCT.md | 29 +- README.md | 79 +- conda/dev.yaml | 2 +- containers/compose.yaml | 4 +- docs/contributing.md | 101 +- docs/index.md | 45 +- docs/installation.md | 11 +- docs/mkdocs.yaml | 2 +- poetry.lock | 3112 +++++++++++++++++ pyproject.toml | 79 +- src/feedback_linker/__init__.py | 6 +- .../{feedback_linker.py => app.py} | 0 23 files changed, 3683 insertions(+), 508 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.yaml create mode 100644 poetry.lock rename src/feedback_linker/{feedback_linker.py => app.py} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9a179ed..6d422d7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -4,146 +4,146 @@ description: Create a report to help us improve labels: ["bug", "needs-triage"] body: -- type: markdown - attributes: - value: > - **Thank you for wanting to report a bug in Feedback Linker!** - - - ⚠ - Verify first that your issue is not [already reported on - GitHub][issue search]. - - - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Abug - -- type: textarea - attributes: - label: Summary - description: Explain the problem briefly below. - placeholder: >- - When I try to do X with {{ cookiecutter.project_name }} and the following workspace, Y breaks or - Z happens in an unexpected manner. - Here are all the details I know about this problem. - validations: - required: true - -- type: textarea - attributes: - label: OS / Environment - description: >- - Provide information on your operating system. - Something like the output of `cat /etc/os-release` on Linux or - `system_profiler -detailLevel mini SPSoftwareDataType` on macOS. - render: console - placeholder: | - # Linux - $ cat /etc/os-release - NAME="Ubuntu" - VERSION="20.04.2 LTS (Focal Fossa)" - ID=ubuntu - ID_LIKE=debian - PRETTY_NAME="Ubuntu 20.04.2 LTS" - VERSION_ID="20.04" - HOME_URL="https://www.ubuntu.com/" - SUPPORT_URL="https://help.ubuntu.com/" - BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" - PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" - VERSION_CODENAME=focal - UBUNTU_CODENAME=focal - - # macOS - $ system_profiler -detailLevel mini SPSoftwareDataType | head -n 6 - Software: - - System Software Overview: - - System Version: macOS 10.15.7 (19H1323) - Kernel Version: Darwin 19.6.0 - validations: - required: true - -- type: textarea - attributes: - label: Steps to Reproduce - description: >- - Describe exactly how to reproduce the problem, using a minimal test-case. - It would *really* help us understand your problem if you paste in the Python code - that you're running. - - - **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. - value: | - - ```python (paste below) - - ``` - - - ```console (paste below) - - ``` - validations: - required: true - -- type: textarea - attributes: - label: File Upload (optional) - description: >- - If your steps to reproduce your minimal failing example require either a spec or a - workspace file, please upload it by attaching it to the text area here. - - - **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. - placeholder: >- - Attach any files or compressed archives by dragging & dropping, selecting, - or pasting them here. - validations: - required: false - -- type: textarea - attributes: - label: Expected Results - description: >- - Describe what you expected to happen when running the steps above. - placeholder: >- - I expected X to happen because I assumed Y. - validations: - required: true - -- type: textarea - attributes: - label: Actual Results - description: >- - Paste verbatim program or command output. - Don't wrap it with triple backticks — your whole input will be - turned into a code snippet automatically. - render: console - validations: - required: true - -- type: textarea - attributes: - # label: Feedback Linker version - # description: >- - # Paste verbatim output from `Feedback Linker --version` below, under the prompt line. - # Don't wrap it with triple backticks — your whole input will be - # turned into a code snippet automatically. - render: console - placeholder: | - python -m pip show Feedback Linker - validations: - required: true - -- type: checkboxes - attributes: - label: Code of Conduct - description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. - - [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md - options: - - label: I agree to follow the Code of Conduct + - type: markdown + attributes: + value: > + **Thank you for wanting to report a bug in Feedback Linker!** + + + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + + + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Abug + + - type: textarea + attributes: + label: Summary + description: Explain the problem briefly below. + placeholder: >- + When I try to do X with {{ cookiecutter.project_name }} and the following workspace, Y breaks or + Z happens in an unexpected manner. + Here are all the details I know about this problem. + validations: + required: true + + - type: textarea + attributes: + label: OS / Environment + description: >- + Provide information on your operating system. + Something like the output of `cat /etc/os-release` on Linux or + `system_profiler -detailLevel mini SPSoftwareDataType` on macOS. + render: console + placeholder: | + # Linux + $ cat /etc/os-release + NAME="Ubuntu" + VERSION="20.04.2 LTS (Focal Fossa)" + ID=ubuntu + ID_LIKE=debian + PRETTY_NAME="Ubuntu 20.04.2 LTS" + VERSION_ID="20.04" + HOME_URL="https://www.ubuntu.com/" + SUPPORT_URL="https://help.ubuntu.com/" + BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" + PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" + VERSION_CODENAME=focal + UBUNTU_CODENAME=focal + + # macOS + $ system_profiler -detailLevel mini SPSoftwareDataType | head -n 6 + Software: + + System Software Overview: + + System Version: macOS 10.15.7 (19H1323) + Kernel Version: Darwin 19.6.0 + validations: required: true -... + + - type: textarea + attributes: + label: Steps to Reproduce + description: >- + Describe exactly how to reproduce the problem, using a minimal test-case. + It would *really* help us understand your problem if you paste in the Python code + that you're running. + + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + value: | + + ```python (paste below) + + ``` + + + ```console (paste below) + + ``` + validations: + required: true + + - type: textarea + attributes: + label: File Upload (optional) + description: >- + If your steps to reproduce your minimal failing example require either a spec or a + workspace file, please upload it by attaching it to the text area here. + + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + placeholder: >- + Attach any files or compressed archives by dragging & dropping, selecting, + or pasting them here. + validations: + required: false + + - type: textarea + attributes: + label: Expected Results + description: >- + Describe what you expected to happen when running the steps above. + placeholder: >- + I expected X to happen because I assumed Y. + validations: + required: true + + - type: textarea + attributes: + label: Actual Results + description: >- + Paste verbatim program or command output. + Don't wrap it with triple backticks — your whole input will be + turned into a code snippet automatically. + render: console + validations: + required: true + + - type: textarea + attributes: + # label: Feedback Linker version + # description: >- + # Paste verbatim output from `Feedback Linker --version` below, under the prompt line. + # Don't wrap it with triple backticks — your whole input will be + # turned into a code snippet automatically. + render: console + placeholder: | + python -m pip show Feedback Linker + validations: + required: true + + - type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + + [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +--- This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d423150..6a118aa 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,15 +1,15 @@ # Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser blank_issues_enabled: true contact_links: -- name: 🙋 Usage Questions - url: https://github.com/xmnlab/feedback-linker.git/discussions - about: | - Use Feedback Linker's GitHub Discussions to ask "How do I do X with Feedback Linker?". -- name: 📖 Tutorial - url: https://github.com/xmnlab/feedback-linker.git - about: | - The Feedback Linker tutorial is continually updated and provides an in depth walkthrough - of how to use the latest release of Feedback Linker. -- name: 📝 Feedback Linker Code of Conduct - url: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md - about: Expectations for how people will interact with each other on Feedback Linker's GitHub. + - name: 🙋 Usage Questions + url: https://github.com/xmnlab/feedback-linker.git/discussions + about: | + Use Feedback Linker's GitHub Discussions to ask "How do I do X with Feedback Linker?". + - name: 📖 Tutorial + url: https://github.com/xmnlab/feedback-linker.git + about: | + The Feedback Linker tutorial is continually updated and provides an in depth walkthrough + of how to use the latest release of Feedback Linker. + - name: 📝 Feedback Linker Code of Conduct + url: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + about: Expectations for how people will interact with each other on Feedback Linker's GitHub. diff --git a/.github/ISSUE_TEMPLATE/documentation-report.yml b/.github/ISSUE_TEMPLATE/documentation-report.yml index c77d7fa..600dd83 100644 --- a/.github/ISSUE_TEMPLATE/documentation-report.yml +++ b/.github/ISSUE_TEMPLATE/documentation-report.yml @@ -4,52 +4,50 @@ description: Create a report for problems with the docs labels: ["docs", "needs-triage"] body: -- type: markdown - attributes: - value: > - **Thank you for wanting to report a problem with Feedback Linker's documentation!** - - - ⚠ - Verify first that your issue is not [already reported on - GitHub][issue search]. - - - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Adocs - -- type: textarea - attributes: - label: Summary - description: >- - Explain the problem briefly below, add suggestions to wording or structure. - If there are external references that are related please link them here - as well. - placeholder: >- - I was reading the Feedback Linker documentation for Feedback Linker version X and I'm having - problems understanding Y. - It would be very helpful if that got rephrased as Z. - validations: - required: true - -- type: input - attributes: - label: Documentation Page Link - description: | - Paste the link to the documentation webpage that you have a question on. - validations: - required: true - -- type: checkboxes - attributes: - label: Code of Conduct - description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. - - [CoC]: https://github.com/xmnlab/feedback-linker.git/blob/main/CODE_OF_CONDUCT.md - options: - - label: I agree to follow the Code of Conduct + - type: markdown + attributes: + value: > + **Thank you for wanting to report a problem with Feedback Linker's documentation!** + + + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + + + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Adocs + + - type: textarea + attributes: + label: Summary + description: >- + Explain the problem briefly below, add suggestions to wording or structure. + If there are external references that are related please link them here + as well. + placeholder: >- + I was reading the Feedback Linker documentation for Feedback Linker version X and I'm having + problems understanding Y. + It would be very helpful if that got rephrased as Z. + validations: required: true -... + - type: input + attributes: + label: Documentation Page Link + description: | + Paste the link to the documentation webpage that you have a question on. + validations: + required: true + + - type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + [CoC]: https://github.com/xmnlab/feedback-linker.git/blob/main/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +--- This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 3fe58d3..e5b54ca 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -4,80 +4,77 @@ description: Suggest an idea for this project labels: ["feat/enhancement ", "needs-triage"] body: -- type: markdown - attributes: - value: > - **Thank you for wanting to suggest a feature for Feedback Linker!** + - type: markdown + attributes: + value: > + **Thank you for wanting to suggest a feature for Feedback Linker!** - ⚠ - Verify first that your issue is not [already reported on - GitHub][issue search]. - Make sure to check the closed issues as well as it may - already be implemented in a development release. + ⚠ + Verify first that your issue is not [already reported on + GitHub][issue search]. + Make sure to check the closed issues as well as it may + already be implemented in a development release. - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Afeat%2Fenhancement + [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Afeat%2Fenhancement + - type: textarea + attributes: + label: Summary + description: > + Describe the new feature/improvement you would like briefly below. -- type: textarea - attributes: - label: Summary - description: > - Describe the new feature/improvement you would like briefly below. + What's the problem this feature will solve? + What are you trying to do, that you are unable to achieve + with the **latest** release of Feedback Linker? - What's the problem this feature will solve? - What are you trying to do, that you are unable to achieve - with the **latest** release of Feedback Linker? + * Provide examples of real-world use cases that this would enable + and how it solves the problem you described. - * Provide examples of real-world use cases that this would enable - and how it solves the problem you described. + * How do you solve this now? - * How do you solve this now? + * Have you tried to work around the problem? - * Have you tried to work around the problem? + * Could there be a different approach to solving this issue? - * Could there be a different approach to solving this issue? - - If there are external references or other GitHub Issues that are related - please link them here as well. - placeholder: >- - I am trying to do X with Feedback Linker version x.y.z and I think that implementing - new feature Y would be very helpful for me and every other user because of Z. - validations: - required: true - -- type: textarea - attributes: - label: Additional Information - description: | - If you can, describe how the feature would be used in a mock code example. - - **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. - value: | - - - - ```python (paste below) - - ``` - validations: - required: false - -- type: checkboxes - attributes: - label: Code of Conduct - description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. - - [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md - options: - - label: I agree to follow the Code of Conduct + If there are external references or other GitHub Issues that are related + please link them here as well. + placeholder: >- + I am trying to do X with Feedback Linker version x.y.z and I think that implementing + new feature Y would be very helpful for me and every other user because of Z. + validations: required: true -... - + - type: textarea + attributes: + label: Additional Information + description: | + If you can, describe how the feature would be used in a mock code example. + + **HINT:** You can paste [GitHub Gist](https://gist.github.com) links for larger files. + value: | + + + + ```python (paste below) + + ``` + validations: + required: false + + - type: checkboxes + attributes: + label: Code of Conduct + description: | + Read the [`Feedback Linker` Code of Conduct][CoC] first. + + [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + options: + - label: I agree to follow the Code of Conduct + required: true +--- This template has been adopted from [pyhf](https://github.com/scikit-hep/pyhf/tree/main/.github/ISSUE_TEMPLATE)'s excellent bug report template. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 16e9048..d9a7b67 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,5 @@ ## Pull Request description + -* ```...``` +- `...` + ## Pull Request checklists This PR is a: + - [ ] bug-fix - [ ] new feature - [ ] maintenance About this PR: + - [ ] it includes tests. - [ ] the tests are executed on CI. - [ ] the tests generate log file(s) (path). @@ -32,8 +36,10 @@ About this PR: - [ ] this PR requires a project documentation update. Author's checklist: + - [ ] I have reviewed the changes and it contains no misspelling. -- [ ] The code is well commented, especially in the parts that contain more complexity. +- [ ] The code is well commented, especially in the parts that contain more + complexity. - [ ] New and old tests passed locally. ## Additional information diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a3c78e8..550cbd4 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -15,29 +15,28 @@ jobs: shell: bash -l {0} steps: - - uses: actions/checkout@v3 - - - uses: conda-incubator/setup-miniconda@v2 - with: - miniconda-version: "latest" - mamba-version: "1.*" - environment-file: conda/dev.yaml - channels: conda-forge,nodefaults - channel-priority: true - activate-environment: feedback-linker - use-mamba: true - miniforge-variant: Mambaforge - - - - name: Install dependencies - run: | - poetry install - - - name: Run tests - run: | - makim tests.unit - - - name: Run style checks - run: | - pre-commit install - makim tests.linter + - uses: actions/checkout@v3 + + - uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + mamba-version: "1.*" + environment-file: conda/dev.yaml + channels: conda-forge,nodefaults + channel-priority: true + activate-environment: feedback-linker + use-mamba: true + miniforge-variant: Mambaforge + + - name: Install dependencies + run: | + poetry install + + - name: Run tests + run: | + makim tests.unit + + - name: Run style checks + run: | + pre-commit install + makim tests.linter diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 147c2e1..b4ef35e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -3,9 +3,9 @@ name: Release on: workflow_dispatch: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] permissions: contents: write @@ -25,7 +25,6 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: conda-incubator/setup-miniconda@v2 with: miniconda-version: "latest" @@ -37,7 +36,6 @@ jobs: use-mamba: true miniforge-variant: Mambaforge - - name: Install deps run: | poetry install diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f7b549..25dd97f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,79 +6,77 @@ repos: hooks: - id: end-of-file-fixer - - repo: local + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.0.2" hooks: - # NOTE: This is a total unnecessary check, just used as part of the - # template. Remove this after creating the template. - - id: check-python - name: check-python - entry: python --version - language: system - pass_filenames: no + - id: prettier - - id: black - name: black - entry: black - language: system - pass_filenames: true - types: - - python + - repo: local + hooks: + - id: ruff-format + name: ruff-format + entry: ruff format + exclude: | + (?x)( + docs + ) + language: system + pass_filenames: true + types: + - python - - id: ruff - name: ruff - entry: ruff --fix - language: system - pass_filenames: true - files: "./" - types: - - python + - id: ruff-linter + name: ruff-linter + entry: ruff check + language: system + exclude: "docs/" + pass_filenames: true + types: + - python - - id: mypy - name: mypy - entry: mypy - language: system - files: "src/feedback_linker" - pass_filenames: true - types: - - python + - id: mypy + name: mypy + entry: mypy . + language: system + pass_filenames: false - - id: shellcheck - name: shellcheck - entry: shellcheck - language: system - types_or: - - sh - - shell - - ash - - bash - - bats - - dash - - ksh + - id: shellcheck + name: shellcheck + entry: shellcheck + language: system + types_or: + - sh + - shell + - ash + - bash + - bats + - dash + - ksh - - id: bandit - name: bandit - entry: bandit - language: system - args: ['--configfile', 'pyproject.toml', '-iii', '-lll'] - pass_filenames: true - types: - - python + - id: bandit + name: bandit + entry: bandit + language: system + args: ["--configfile", "pyproject.toml", "-iii", "-lll"] + pass_filenames: true + types: + - python - - id: vulture - name: vulture - entry: vulture - language: system - files: "src/feedback_linker" - description: Find unused Python code. - pass_filenames: true - types: - - python + - id: vulture + name: vulture + entry: vulture --min-confidence 80 + language: system + files: "src/feedback-linker" + description: Find unused Python code. + pass_filenames: true + types: + - python - - id: mccabe - name: mccabe - entry: python -m mccabe --min 10 - language: system - files: "src/feedback_linker" - pass_filenames: true - types: - - python + - id: mccabe + name: mccabe + entry: python -m mccabe --min 10 + language: system + files: "src/feedback-linker" + pass_filenames: true + types: + - python diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6618446 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +.makim.yaml +docs/changelog.md diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..24d3b9f --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,6 @@ +overrides: + - files: "*.md" + options: + parser: remark + proseWrap: always + printWidth: 80 diff --git a/.releaserc.json b/.releaserc.json index 48b8450..bdcca18 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -51,7 +51,6 @@ { "prepareCmd": "poetry build", "publishCmd": "poetry publish" - } ], [ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 35b5167..e74303a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -59,9 +59,8 @@ representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. +reported to the community leaders responsible for enforcement at [INSERT CONTACT +METHOD]. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. @@ -118,8 +117,8 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at diff --git a/README.md b/README.md index a766f1b..02c4ff0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,78 @@ -# Feedback Linker +# Feedback-Linker -The main objective of this platform is to offer a space to handle and organize feedback between two people +Feedback-Linker is a simplified feedback exchange platform designed to +facilitate periodic feedback between individuals within specific projects. It +aims to streamline communication and insights sharing, enhancing project +collaboration and personal development. -* Free software: BSD 3 Clause -* Documentation: https://opensciencelabs.github.io/feedback-linker +- Documentation: ## Features -TBD +Feedback-Linker includes several key features to manage feedback efficiently: -## Credits +### Projects -This package was created with -[scicookie](https://github.com/osl-incubator/scicookie) project template. +- **Creation and Management**: Admin users can create and manage projects, each + with specific goals and participants. +- **Global Access Control**: For each project, certain individuals can be + designated to have global access to all feedback related to that project, + ensuring oversight and support where needed. + +### People + +- **Profile Management**: Admin users can create profiles for individuals who + will be associated with zero or more projects. The email used will serve as a + constraint for login; only individuals registered by an admin can log into the + platform. New users will receive a random password via email. +- **Project Association**: From the people form, users can select projects to + associate with an individual, allowing for flexible project participation. +- **Login**: Only individuals registered by an admin can log into the platform. + +### Links + +- **Feedback Pairing**: The platform allows the creation of links between two + individuals, enabling them to exchange feedback periodically. Only an admin + can create this link. The admin must define the periodicity (daily, weekly, + monthly) and a number that specifies "Every X times" it will be repeated. +- **Supervisor Assignment**: Each link can have designated supervisors who are + granted access to read all the feedback exchanged within that link, ensuring + appropriate oversight and support. + +### Feedback + +- **Periodic Exchange**: Feedback is exchanged periodically (e.g., weekly) + between linked individuals, fostering continuous growth and development. +- **Visibility and Access**: Feedback visibility is controlled through project + associations, link-specific supervisors, and global access roles, ensuring + information is shared with the right people. + +## Usage + +- **Managing Projects**: Navigate to the Projects section to create, view, and + edit projects. +- **Adding People**: In the People form, you can add individuals to the system + and associate them with projects. +- **Creating Links**: Use the Link form to connect two people for feedback + exchange and assign supervisors to oversee the feedback. +- **Exchanging Feedback**: Linked individuals can start exchanging feedback as + per the defined periodic schedule. + +## Contributing + +Feedback-Linker is open for contributions. Whether it's feature requests, bug +reports, or code contributions, we welcome your input to make Feedback-Linker +better. Please see our contributing guidelines for more information. + +## License + +Feedback-Linker is licensed under the BSD 3-Clause License. + +## Contact + +For support or to get in touch with the developers, please open an issue. + +--- + +We hope Feedback-Linker enhances your project collaboration and personal +development efforts! diff --git a/conda/dev.yaml b/conda/dev.yaml index b396ecd..80165ab 100644 --- a/conda/dev.yaml +++ b/conda/dev.yaml @@ -6,5 +6,5 @@ dependencies: - python >=3.8.1,<4 - pip - poetry - - nodejs # used by semantic-release + - nodejs # used by semantic-release - shellcheck diff --git a/containers/compose.yaml b/containers/compose.yaml index fe46e35..589d11b 100644 --- a/containers/compose.yaml +++ b/containers/compose.yaml @@ -1,4 +1,4 @@ -version: '3.9' +version: "3.9" services: web: @@ -8,7 +8,7 @@ services: volumes: - .:/code - postgres-db: + postgres-db: image: postgres environment: POSTGRES_USER: ${POSTGRES_USER} diff --git a/docs/contributing.md b/docs/contributing.md index 89870b7..2d87526 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,9 +1,8 @@ # Contributing -In order to be able to contribute, it is important that you understand -the project layout. -This project uses the *src layout*, which means that the package code is located -at `./src/feedback_linker`. +In order to be able to contribute, it is important that you understand the +project layout. This project uses the _src layout_, which means that the package +code is located at `./src/feedback_linker`. For my information, check the official documentation: https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/ @@ -29,27 +28,25 @@ Report bugs at https://github.com/xmnlab/feedback-linker.git/issues. If you are reporting a bug, please include: - - Your operating system name and version. - - Any details about your local setup that might be helpful in - troubleshooting. - - Detailed steps to reproduce the bug. +- Your operating system name and version. +- Any details about your local setup that might be helpful in troubleshooting. +- Detailed steps to reproduce the bug. ### Fix Bugs -Look through the GitHub issues for bugs. Anything tagged with “bug” and -“help wanted” is open to whoever wants to implement it. +Look through the GitHub issues for bugs. Anything tagged with “bug” and “help +wanted” is open to whoever wants to implement it. ### Implement Features -Look through the GitHub issues for features. Anything tagged with -“enhancement” and “help wanted” is open to whoever wants to implement -it. +Look through the GitHub issues for features. Anything tagged with “enhancement” +and “help wanted” is open to whoever wants to implement it. ### Write Documentation -Feedback Linker could always use more documentation, -whether as part of the official Feedback Linker docs, -in docstrings, or even on the web in blog posts, articles, and such. +Feedback Linker could always use more documentation, whether as part of the +official Feedback Linker docs, in docstrings, or even on the web in blog posts, +articles, and such. ### Submit Feedback @@ -58,15 +55,15 @@ https://github.com/xmnlab/feedback-linker.git/issues. If you are proposing a feature: - - Explain in detail how it would work. - - Keep the scope as narrow as possible, to make it easier to - implement. - - Remember that this is a volunteer-driven project, and that - contributions are welcome :) +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that contributions are + welcome :) ## Get Started! -Ready to contribute? Here’s how to set up `feedback-linker` for local development. +Ready to contribute? Here’s how to set up `feedback-linker` for local +development. 1. Fork the `feedback-linker` repo on GitHub. @@ -75,12 +72,11 @@ Ready to contribute? Here’s how to set up `feedback-linker` for local developm $ git clone git@github.com:your_name_here/feedback-linker.git 3. Install your local copy into a virtualenv. Assuming you have - virtualenvwrapper installed, this is how you set up your fork for - local development:: + virtualenvwrapper installed, this is how you set up your fork for local + development:: - $ mkvirtualenv feedback-linker - $ cd feedback-linker/ - $ python setup.py develop + $ mkvirtualenv feedback-linker $ cd feedback-linker/ $ python setup.py + develop 4. Create a branch for local development:: @@ -88,18 +84,17 @@ Ready to contribute? Here’s how to set up `feedback-linker` for local developm Now you can make your changes locally. -5. When you’re done making changes, check that your changes pass flake8 - and the tests, including testing other Python versions with tox:: +5. When you’re done making changes, check that your changes pass flake8 and the + tests, including testing other Python versions with tox:: - $ make lint - $ make test + $ make lint $ make test To get flake8 and tox, just pip install them into your virtualenv. 6. Commit your changes and push your branch to GitHub:: - $ git add . $ git commit -m “Your detailed description of your - changes.” $ git push origin name-of-your-bugfix-or-feature + $ git add . $ git commit -m “Your detailed description of your changes.” $ + git push origin name-of-your-bugfix-or-feature 7. Submit a pull request through the GitHub website. @@ -108,50 +103,50 @@ Ready to contribute? Here’s how to set up `feedback-linker` for local developm Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. -2. If the pull request adds functionality, the docs should be updated. - Put your new functionality into a function with a docstring, and add - the feature to the list in README.rst. +2. If the pull request adds functionality, the docs should be updated. Put your + new functionality into a function with a docstring, and add the feature to + the list in README.rst. 3. The pull request should work for Python >= 3.8. ## Tips To run a subset of tests:: + ``` $ pytest tests.test_feedback_linker ``` - ## Release -This project uses semantic-release in order to cut a new release -based on the commit-message. +This project uses semantic-release in order to cut a new release based on the +commit-message. ### Commit message format -**semantic-release** uses the commit messages to determine the consumer -impact of changes in the codebase. Following formalized conventions for -commit messages, **semantic-release** automatically determines the next +**semantic-release** uses the commit messages to determine the consumer impact +of changes in the codebase. Following formalized conventions for commit +messages, **semantic-release** automatically determines the next [semantic version](https://semver.org) number, generates a changelog and publishes the release. -By default, **semantic-release** uses [Angular Commit Message -Conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format). -The commit message format can be changed with the `preset` or `config` -options_ of the +By default, **semantic-release** uses +[Angular Commit Message Conventions](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format). +The commit message format can be changed with the `preset` or `config` options\_ +of the [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer#options) and [@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator#options) plugins. Tools such as [commitizen](https://github.com/commitizen/cz-cli) or -[commitlint](https://github.com/conventional-changelog/commitlint) can -be used to help contributors and enforce valid commit messages. +[commitlint](https://github.com/conventional-changelog/commitlint) can be used +to help contributors and enforce valid commit messages. -The table below shows which commit message gets you which release type -when `semantic-release` runs (using the default configuration): +The table below shows which commit message gets you which release type when +`semantic-release` runs (using the default configuration): | Commit message | Release type | -|----------------------------------------------------------------|------------------| +| -------------------------------------------------------------- | ---------------- | | `fix(pencil): stop graphite breaking when pressure is applied` | Fix Release | | `feat(pencil): add 'graphiteWidth' option` | Feature Release | | `perf(pencil): remove graphiteWidth option` | Chore | @@ -160,5 +155,5 @@ when `semantic-release` runs (using the default configuration): source: -As this project uses the `squash and merge` strategy, ensure to apply -the commit message format to the PR's title. +As this project uses the `squash and merge` strategy, ensure to apply the commit +message format to the PR's title. diff --git a/docs/index.md b/docs/index.md index 5180fd7..04600f6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,35 +2,35 @@ # Feedback Linker -The main objective of this platform is to offer a space to handle and organize feedback between two people +The main objective of this platform is to offer a space to handle and organize +feedback between two people -* License: BSD 3 Clause -* Documentation: https://feedback-linker.github.io +- License: BSD 3 Clause +- Documentation: https://feedback-linker.github.io ## Features -* The security of our code: Bandit is a powerful tool that we use in our Python +- The security of our code: Bandit is a powerful tool that we use in our Python project to ensure its security. This tool analyzes the code and detects potential vulnerabilities. Some of the key features of Bandit are its ease of use, its ability to integrate with other tools, and its support for multiple Python versions. If you want to know about bandit you can check its [documentation](https://bandit.readthedocs.io/en/latest/). -* Finds unused code: [Vulture](https://github.com/jendrikseipp/vulture) - is useful for cleaning up and finding errors in large code bases in - Python. - -* Complexity of functions and modules: We use -[McCabe](https://github.com/PyCQA/mccabe) to identify the complexity in our -Python code that may be difficult to maintain or understand. By identifying -complex code at the outset, we as developers can refactor it to make it easier -to maintain and understand. In summary, McCabe helps us to improve the quality -of our code and make it easier to maintain. If you would like to learn more -about McCabe and code complexity, you can visit [McCabe - Code Complexity -Checker](https://here-be-pythons.readthedocs.io/en/latest/python/mccabe.html). -This tool is included with [Flake8](https://flake8.pycqa.org/en/latest/). - -* Integration with DevOps tools: We use Docker because it allows us to create an +- Finds unused code: [Vulture](https://github.com/jendrikseipp/vulture) is + useful for cleaning up and finding errors in large code bases in Python. + +- Complexity of functions and modules: We use + [McCabe](https://github.com/PyCQA/mccabe) to identify the complexity in our + Python code that may be difficult to maintain or understand. By identifying + complex code at the outset, we as developers can refactor it to make it easier + to maintain and understand. In summary, McCabe helps us to improve the quality + of our code and make it easier to maintain. If you would like to learn more + about McCabe and code complexity, you can visit + [McCabe - Code Complexity Checker](https://here-be-pythons.readthedocs.io/en/latest/python/mccabe.html). + This tool is included with [Flake8](https://flake8.pycqa.org/en/latest/). + +- Integration with DevOps tools: We use Docker because it allows us to create an isolated environment for our application that includes all the necessary dependencies, libraries and configurations. This makes it easier to manage and reproduce our development and production environments without any conflicts or @@ -39,10 +39,11 @@ This tool is included with [Flake8](https://flake8.pycqa.org/en/latest/). With Docker, we can easily share our application with others and deploy it to different environments. This streamlines our development, testing, deployment, and collaboration workflows, making the entire process more efficient. -* TODO + +- TODO ## Credits This package was created with Cookieninja and the -[osl-incubator/scicookie](https://github.com/osl-incubator/scicookie) -project template. +[osl-incubator/scicookie](https://github.com/osl-incubator/scicookie) project +template. diff --git a/docs/installation.md b/docs/installation.md index e55bf55..bae32c5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -2,15 +2,14 @@ ## Stable release -To install Feedback Linker, run this command in your -terminal: +To install Feedback Linker, run this command in your terminal: ```bash $ pip install feedback-linker ``` -This is the preferred method to install Feedback Linker, -as it will always install the most recent stable release. +This is the preferred method to install Feedback Linker, as it will always +install the most recent stable release. If you don't have [pip](https://pip.pypa.io) installed, this [Python installation guide](http://docs.python-guide.org/en/latest/starting/installation/) @@ -18,8 +17,8 @@ can guide you through the process. ## From sources -The sources for Feedback Linker can be downloaded from -the [Github repo](https://github.com/xmnlab/feedback-linker.git). +The sources for Feedback Linker can be downloaded from the +[Github repo](https://github.com/xmnlab/feedback-linker.git). You can either clone the public repository: diff --git a/docs/mkdocs.yaml b/docs/mkdocs.yaml index 6281afc..1bfc531 100644 --- a/docs/mkdocs.yaml +++ b/docs/mkdocs.yaml @@ -45,7 +45,7 @@ plugins: handlers: python: paths: [../src] - + import: - https://docs.python.org/3/objects.inv options: diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..6d21c60 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,3112 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "bandit" +version = "1.7.7" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"}, + {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "24.2.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "bleach" +version = "6.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.3)"] + +[[package]] +name = "blinker" +version = "1.7.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "comm" +version = "0.2.1" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.1-py3-none-any.whl", hash = "sha256:87928485c0dfc0e7976fd89fc1e187023cf587e7c353e4a9b417555b44adf021"}, + {file = "comm-0.2.1.tar.gz", hash = "sha256:0bc91edae1344d39d3661dcbc36937181fdaddb304790458f8b044dbc064b89a"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "compose-go" +version = "2.24.6" +description = "Docker Compose v2 (GoLang) wrapped in a Python Package" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "compose_go-2.24.6-cp312-cp312-manylinux_2_35_x86_64.whl", hash = "sha256:8bc62a7fcca108bc4234fb9c344cb0d8597418d6ba63ed80b040c9b5779867ae"}, + {file = "compose_go-2.24.6.tar.gz", hash = "sha256:79c055e0c44aa885992723fd59c8c7faea4debb64c0f107c874cea5fe4e03a2e"}, +] + +[[package]] +name = "containers-sugar" +version = "1.11.1" +description = "Simplify the usage of containers" +optional = false +python-versions = ">=3.8.1,<4" +files = [ + {file = "containers_sugar-1.11.1-py3-none-any.whl", hash = "sha256:f6c4f3c47352d90beac0073dc9e663aae1d55abc59253649e6ef6022eeabc368"}, + {file = "containers_sugar-1.11.1.tar.gz", hash = "sha256:14c32e25fbb5b0375f45dee0824690523de89e532c7b1090f96936517dcf5dc7"}, +] + +[package.dependencies] +colorama = ">=0.4.6" +Jinja2 = ">=2" +plotille = ">=5" +python-dotenv = ">=0.21.1" +pyyaml = ">=6" +rich = ">=13.7" +sh = ">=2.0.0" +textual = ">=0.48" + +[[package]] +name = "coverage" +version = "7.4.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.0.post1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.0.post1-py3-none-any.whl", hash = "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637"}, + {file = "email_validator-2.1.0.post1.tar.gz", hash = "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "flask" +version = "3.0.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, + {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-wtf" +version = "1.2.1" +description = "Form rendering, validation, and CSRF protection for Flask with WTForms." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_wtf-1.2.1-py3-none-any.whl", hash = "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287"}, + {file = "flask_wtf-1.2.1.tar.gz", hash = "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695"}, +] + +[package.dependencies] +flask = "*" +itsdangerous = "*" +wtforms = "*" + +[package.extras] +email = ["email-validator"] + +[[package]] +name = "fuzzywuzzy" +version = "0.18.0" +description = "Fuzzy string matching in python" +optional = false +python-versions = "*" +files = [ + {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"}, + {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"}, +] + +[package.extras] +speedup = ["python-levenshtein (>=0.12)"] + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffe" +version = "0.40.1" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "griffe-0.40.1-py3-none-any.whl", hash = "sha256:5b8c023f366fe273e762131fe4bfd141ea56c09b3cb825aa92d06a82681cfd93"}, + {file = "griffe-0.40.1.tar.gz", hash = "sha256:66c48a62e2ce5784b6940e603300fcfb807b6f099b94e7f753f1841661fd5c7c"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "identify" +version = "2.5.35" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "importlib-metadata" +version = "7.0.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "importlib-resources" +version = "6.1.1" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, + {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "ipykernel" +version = "6.29.2" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.2-py3-none-any.whl", hash = "sha256:50384f5c577a260a1d53f1f59a828c7266d321c9b7d00d345693783f66616055"}, + {file = "ipykernel-6.29.2.tar.gz", hash = "sha256:3bade28004e3ff624ed57974948116670604ac5f676d12339693f3142176d3f0"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (==0.23.4)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "7.34.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, + {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.7" +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.21.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +jsonschema-specifications = ">=2023.03.6" +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-client" +version = "8.6.0" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.0-py3-none-any.whl", hash = "sha256:909c474dbe62582ae62b758bca86d6518c85234bdee2d908c778db6d72f39d99"}, + {file = "jupyter_client-8.6.0.tar.gz", hash = "sha256:0642244bb83b4764ae60d07e010e15f0e2d275ec4e918a8f7b80fbbef3ca60c7"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.1" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.1-py3-none-any.whl", hash = "sha256:c65c82126453a723a2804aa52409930434598fd9d35091d63dfb919d2b765bb7"}, + {file = "jupyter_core-5.7.1.tar.gz", hash = "sha256:de61a9d7fc71240f688b2fb5ab659fbb56979458dc66a71decd098e03c79e218"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +description = "Pygments theme using JupyterLab CSS variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] + +[[package]] +name = "jupytext" +version = "1.16.1" +description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupytext-1.16.1-py3-none-any.whl", hash = "sha256:796ec4f68ada663569e5d38d4ef03738a01284bfe21c943c485bc36433898bd0"}, + {file = "jupytext-1.16.1.tar.gz", hash = "sha256:68c7b68685e870e80e60fda8286fbd6269e9c74dc1df4316df6fe46eabc94c99"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0" +mdit-py-plugins = "*" +nbformat = "*" +packaging = "*" +pyyaml = "*" +toml = "*" + +[package.extras] +dev = ["jupytext[test-cov,test-external]"] +docs = ["myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"] +test = ["pytest", "pytest-randomly", "pytest-xdist"] +test-cov = ["jupytext[test-integration]", "pytest-cov (>=2.6.1)"] +test-external = ["autopep8", "black", "flake8", "gitpython", "isort", "jupyter-fs (<0.4.0)", "jupytext[test-integration]", "pre-commit", "sphinx-gallery (<0.8)"] +test-functional = ["jupytext[test]"] +test-integration = ["ipykernel", "jupyter-server (!=2.11)", "jupytext[test-functional]", "nbconvert"] +test-ui = ["calysto-bash"] + +[[package]] +name = "levenshtein" +version = "0.25.0" +description = "Python extension for computing string edit distances and similarities." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Levenshtein-0.25.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3065b26f62e6340bd437875018e417c3b7bb8461ab4447ab58519843f42b6514"}, + {file = "Levenshtein-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dad142561e62f8f3af68533cf79411ccb29ceda4bd9e223d47b63219688c1bc6"}, + {file = "Levenshtein-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26111ab69e08379b6fbafe84e4ae1b5f6388f649d95a99b21871aee6ac29a7cf"}, + {file = "Levenshtein-0.25.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da13060a78ed723de33757aeddec163a25964748867c3dff01842e48661bc359"}, + {file = "Levenshtein-0.25.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e54dc81c1040acab1456756c217bb998bb5276c1fe32534d543b152bc53ee95a"}, + {file = "Levenshtein-0.25.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf8f068a382ba52f2845f75dac84ba8908add5352a883a76aeead28e8021954"}, + {file = "Levenshtein-0.25.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f65744adc3bbb677c04b9eebb48f7a783a84cea2cc9a407d8e6991a80bc2cfb0"}, + {file = "Levenshtein-0.25.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5036edc7bcff3570105bad59c77d959b84413b3556329dbd17fa98a92ad77a5e"}, + {file = "Levenshtein-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e04ba617a4c6f62468aaa30f5a72fbca993b8713718034aa307eb8ab482a3584"}, + {file = "Levenshtein-0.25.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1ed0eea12fa60418572e639e5c0e076833d33834b473d2d419a0bba39644f91a"}, + {file = "Levenshtein-0.25.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:8ce34cae24199b85424e057982c327157e2728c5278551371c65aff932733f04"}, + {file = "Levenshtein-0.25.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3c3d013b109fb3190db16658b3217feb3ed0251d0b4bcc4092834b5487c444d3"}, + {file = "Levenshtein-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:34fe20af42dbe594957ba0e9311eefb744a06958f020798450e7d570c04145a3"}, + {file = "Levenshtein-0.25.0-cp310-cp310-win32.whl", hash = "sha256:2f8046b7ffc9eac4ce1539f2d4083f9ad0fc9ab9d7a74d74eb2275743f0e7628"}, + {file = "Levenshtein-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac6f976d943f7f5255651a6885bfad7a2e11862fa3adfc121b02fbe45ac81fa1"}, + {file = "Levenshtein-0.25.0-cp310-cp310-win_arm64.whl", hash = "sha256:ca0cb57e17af9ff3625811de1401aa3c21194badf33fedce442a422c212508b8"}, + {file = "Levenshtein-0.25.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fb22f81e8a5b22506635acd57fe6b04d4ae5606fb795fc2c4d294dd6fa0d1a85"}, + {file = "Levenshtein-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3987cbcc947e92627b7eba5cbaba31f1bc7e6f09b4367b9e82b45fe243ddb761"}, + {file = "Levenshtein-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c73b92d9a6f01e595ce63268f654e094f5c8c98dd1c84c161fab011999f75651"}, + {file = "Levenshtein-0.25.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3657ad0ec8426ade2580d92b60b7b057de7fbc8973a0115ff63d0705e873ef4f"}, + {file = "Levenshtein-0.25.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6e4d8f245478f21329f8e3b29caac7a8a177fd176e2e159606b12d58ffd3bf8"}, + {file = "Levenshtein-0.25.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d444f6e9e461e948d7262fd25fd1a0692c413ebd6f6a64eaaa7724b8646405eb"}, + {file = "Levenshtein-0.25.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0936cbef6b3d0643c24295565a9eb8f29315fdf38ceda6c61eaa84b9d0822bf5"}, + {file = "Levenshtein-0.25.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea837e7b5756b1f1a6d4b333899e372d0a3cf6e7d7b29523f78875d609b49b33"}, + {file = "Levenshtein-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f74f0b1bcf248d2385d367d18d140f644854b979b010a38e25676c50efb8900c"}, + {file = "Levenshtein-0.25.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:874b6da533198d84a35e1bc18161b2ad0df09a80a3464b0714de4069637ebd1b"}, + {file = "Levenshtein-0.25.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:13985784b12f726df39eed340b5ba883271856da3419e98823c5c46cdc1f6ea9"}, + {file = "Levenshtein-0.25.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f02ff8f80458737060ccdb23666fc5f8335641e0131794419ab590d808f2e22f"}, + {file = "Levenshtein-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8b8f4ff207714188e56327f1d590be406035c6211863402f4c9c08b8e8c59839"}, + {file = "Levenshtein-0.25.0-cp311-cp311-win32.whl", hash = "sha256:62e2b57793cc1af53dd046e950987b4f27f138bdb48965bb088eea400570031c"}, + {file = "Levenshtein-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:719b3a09214702ac9dd54c4dee4446a218e40948bedef248077e2c90890c2b06"}, + {file = "Levenshtein-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:84d9ab58f663efad5af9bbf1f83d7d86f7a28485a47c1ae689bf768bf1cf62a5"}, + {file = "Levenshtein-0.25.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9011755a7e6dd4528ebb4c6f3aacd083b3b40392629b5ca12c74dd86094ede84"}, + {file = "Levenshtein-0.25.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:699eec3d4c755c23c7a9fa24980a1fe9d81978253f75a502d0ad8c9b6521b514"}, + {file = "Levenshtein-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f60e15a2b0a16222414206f63e47f18863c9a98941815d6e80abdfe05e2082a1"}, + {file = "Levenshtein-0.25.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4f6efdbb7381177f80fd24be7622d45c20144cdf6495302b413628710ce91c5"}, + {file = "Levenshtein-0.25.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a7005d21dca111dd9ed9a5f40fa3a17411914717e5a23d6b1fa87bf7f98bbf"}, + {file = "Levenshtein-0.25.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40e48975f31e560c6f7f6e8d79ea4a7b4b090987e89da133f8fa90d9eddcae0b"}, + {file = "Levenshtein-0.25.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62dd3c4fb48699f7aa8de7cd664c8e4e15288273c1a46aa0279d7387b5b7820b"}, + {file = "Levenshtein-0.25.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d507bd0cb47fcf41ddbfb0df746f35354c6af4ebccb4fd1a646d6848da42133e"}, + {file = "Levenshtein-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4016d7665c9bf7735d954e9bdb332745fd28b913ea01be7add705d1f458b5c9e"}, + {file = "Levenshtein-0.25.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a45c0a2c699cee760c03d0a77b320dc3c271b6644a294e317361fb5612dfe76"}, + {file = "Levenshtein-0.25.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:08ef2e2d2a2e4d645e431f61e402285b076c2b694dfc9dbbd8b3fb6cc226ef30"}, + {file = "Levenshtein-0.25.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:026f817fad032c41e177416082150eb15617607045616e71ed18915e80a715e1"}, + {file = "Levenshtein-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:79b21bdbdb22fb40ea01676b3a92875f1bef268f5ced15672a8ad916563ace70"}, + {file = "Levenshtein-0.25.0-cp312-cp312-win32.whl", hash = "sha256:28f45bc68e23e21f56e981a4aa9c493eff8b50047c50dbfa6a12efb6bad16d12"}, + {file = "Levenshtein-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:78f16e25acc64f9c65ede1fba24baa8df0827d8eb93e68a2c7863ca429bc4297"}, + {file = "Levenshtein-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:8894dba28c8b29e4dff9e31c5fac99e600e8deb5d757ae2ad1f36a517cb517a6"}, + {file = "Levenshtein-0.25.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0490e763b6f8f681c780609fd8d214afc30155f8e51d9abe84b04b4cf22872a4"}, + {file = "Levenshtein-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:515402d377332699c729e112237ec7dc04b735a76cff03d19b1d16b121bcb71d"}, + {file = "Levenshtein-0.25.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:084c6cdf659d7eca1d4ea4d15893c3c04a9bed8f78b598d8e55ec5ee6ea2f761"}, + {file = "Levenshtein-0.25.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:507d9d50cacedaceada4f97a24d5cdf8a28fa38c78e16b9e67e77f0c9bdd1fb1"}, + {file = "Levenshtein-0.25.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cc00c35bb38e1e3214bb92f515c16742d6d90d26bd46a6faa5d2d3b7fafc3fef"}, + {file = "Levenshtein-0.25.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2534c9e583eae4831fdaa6f7fd129ebcf7f9a01129531d86bb08a467f3f23752"}, + {file = "Levenshtein-0.25.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12de26c3f694bfb0757897a7e555cd1569dda2b680e57748f5f7c0a546cb0e02"}, + {file = "Levenshtein-0.25.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdcdb36b11988785698739172e08f97d6e0ccfbdd87031352f91ad06008d8b1e"}, + {file = "Levenshtein-0.25.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:46fc85b0093c6c4ee522c786ca778e1e807670e3208a286b53221047d9f2bb22"}, + {file = "Levenshtein-0.25.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7c965677e8c0725de4e70b6de3d4cb06e5f9a4c508a7c76b32bb0e5a8bda527e"}, + {file = "Levenshtein-0.25.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ae37fdb9ac362ba7f84ca6702ef13136b3cb649b34718365a67c43d66a912238"}, + {file = "Levenshtein-0.25.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0bf1e19761c7261e72f8bea0e1593fbd4d7463aebf6484b640217aec2555049b"}, + {file = "Levenshtein-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b6c150ba7fd5243ebfd65b88779504c8000a2ca53f693417db7ad36fed1368c2"}, + {file = "Levenshtein-0.25.0-cp38-cp38-win32.whl", hash = "sha256:8ec94b31f47be07005c63b6feaee64208326daefed2245f2818d557c0f8e6dc2"}, + {file = "Levenshtein-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:6ec9e60a0619d7a8e1f8003932088a22c39c03b5f1c4eb6525a012c54057b123"}, + {file = "Levenshtein-0.25.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b13cead5b3cfb14072f19e929853e74f24aff38d7e95a4602cd7e45dfe4b0fa9"}, + {file = "Levenshtein-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:39ca459608c5d21b32514a1c038e18107a78df656d466a002ee0b630b6110dcc"}, + {file = "Levenshtein-0.25.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:244341d18fc980b4fc18b395c17951a0342aba7cb9fda627e4d072b4053163a6"}, + {file = "Levenshtein-0.25.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f198eccfdaf17b805ea036109195c5151e5467f0e60b506765e9fb87928c6fa"}, + {file = "Levenshtein-0.25.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cea491c614c1107f216cd972adfee4c1f9f7bf96eb223b0940a5918d55091b1"}, + {file = "Levenshtein-0.25.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b9cdd6ccf91a94a17716b515bbc00d4303b6923bafd55737e905b88ae08f946"}, + {file = "Levenshtein-0.25.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff9bb0f3e0f9d0b8c2f3df803d90142d9cf01cf3507017d9d5044ae12735179e"}, + {file = "Levenshtein-0.25.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa126fcfc60e98dcad7bb6c668aa02d95a0359194c13011084eb990e80a1b607"}, + {file = "Levenshtein-0.25.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb25fb5aff64e83bb5e343afcf1dc4921622c87d149ecb6596e6e359976f3865"}, + {file = "Levenshtein-0.25.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:884e9adde78d88238ec15eb316e9a55a50c36c4a049c89ebd0f861efdbfb0b74"}, + {file = "Levenshtein-0.25.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3a7161fca7a45c8137f745ea3fa739603f1afbf25c44c6b702f2c7a9c1d58102"}, + {file = "Levenshtein-0.25.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9be8e3eac7d01bdd744afd323acce48c747330bfc89b710d074f6ec2e58bc9cf"}, + {file = "Levenshtein-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6a770e1da094ebd0fae4e4b10d7cf7bc3213df8eed467fcdecbc6772089581f"}, + {file = "Levenshtein-0.25.0-cp39-cp39-win32.whl", hash = "sha256:7c534fc44b677d0a526c75f11e43178ca54a210dd06e6d25274f4a64574fe580"}, + {file = "Levenshtein-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:f499a2edb00bb2650f0fe2403ff551fca05b2ba6c7e4af7576c79ab15c9ece88"}, + {file = "Levenshtein-0.25.0-cp39-cp39-win_arm64.whl", hash = "sha256:02cc9698fd7160c1950a8bb3c58a7fb4c9dbfd5e2f252c79e2e4d0386094db78"}, + {file = "Levenshtein-0.25.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:719a7c859dc35722399c71e76dcbc6d1300ba023777755a1d26b77bf3243e537"}, + {file = "Levenshtein-0.25.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72dd10b20cd6608804afe3dddee43966722d957e976d605e562fb21e44968829"}, + {file = "Levenshtein-0.25.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f42e79bf98ffca3dbb16740969604f75cbd14e32cbecb2183f8d4ffd7cdbb1"}, + {file = "Levenshtein-0.25.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:992434407f85cfb2516ac1624f1471435a1479b1021fcdd3d0bab9b36613ab85"}, + {file = "Levenshtein-0.25.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2ca472bffa83e68e9d73f96eb4fc67527614522d43c3be1a74f36ea12163c671"}, + {file = "Levenshtein-0.25.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ca0f9966ff84acd779a51d16f8a46565f14b0a3292eb98b11c12537e92fc91f2"}, + {file = "Levenshtein-0.25.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39089d9283dbf86f69e701121060e5e3fa05984032e743a75adba6479b2e2b5c"}, + {file = "Levenshtein-0.25.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af9dfbf0e7d7968782bd6a2676df825f37ef533b4a6cb1c8e8397aa12e80c8e2"}, + {file = "Levenshtein-0.25.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e05e387bde5e456e95c077b648f730597b98c3e99a5143a268e0750152b5843"}, + {file = "Levenshtein-0.25.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0166fd91039d1d17329e59523b35bf6783f7a1719d1df06910cc4b6f2af9271"}, + {file = "Levenshtein-0.25.0.tar.gz", hash = "sha256:0bca15031e6b684f82003c9a399172fac6e215410d385f026a07165c69e75fd5"}, +] + +[package.dependencies] +rapidfuzz = ">=3.1.0,<4.0.0" + +[[package]] +name = "linkify-it-py" +version = "2.0.3" +description = "Links recognition library with FULL unicode support." +optional = false +python-versions = ">=3.7" +files = [ + {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, + {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, +] + +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "makim" +version = "1.13.0" +description = "Simplify the usage of containers" +optional = false +python-versions = ">=3.8.1,<4" +files = [ + {file = "makim-1.13.0-py3-none-any.whl", hash = "sha256:60eb7dadadd468a9318a2b72180039372ace0754ecf1d7918a43a7bf01efe788"}, + {file = "makim-1.13.0.tar.gz", hash = "sha256:4a21ef7a38a171b5cfb3e3cef4db3d7fabf6cf16e3c6375fc189f91f2d962489"}, +] + +[package.dependencies] +fuzzywuzzy = ">=0.18.0" +jinja2 = ">=2.0" +python-dotenv = ">=0.21.1" +python-levenshtein = ">=0.23.0" +pyyaml = ">=5.0" +rich = ">=13.7.0" +sh = ">=2.0.0" +shellingham = ">=1.5.4" +typer = ">=0.9.0" +xonsh = ">=0.14.0" + +[[package]] +name = "markdown" +version = "3.5.2" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, + {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mistune" +version = "3.0.2" +description = "A sane and fast Markdown parser with useful plugins and renderers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, + {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, +] + +[[package]] +name = "mkdocs" +version = "1.5.3" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, + {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.2.1" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +pathspec = ">=0.11.1" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "0.5.0" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_autorefs-0.5.0-py3-none-any.whl", hash = "sha256:7930fcb8ac1249f10e683967aeaddc0af49d90702af111a5e390e8b20b3d97ff"}, + {file = "mkdocs_autorefs-0.5.0.tar.gz", hash = "sha256:9a5054a94c08d28855cfab967ada10ed5be76e2bfad642302a610b252c3274c0"}, +] + +[package.dependencies] +Markdown = ">=3.3" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-exclude" +version = "1.0.2" +description = "A mkdocs plugin that lets you exclude files or trees." +optional = false +python-versions = "*" +files = [ + {file = "mkdocs-exclude-1.0.2.tar.gz", hash = "sha256:ba6fab3c80ddbe3fd31d3e579861fd3124513708271180a5f81846da8c7e2a51"}, +] + +[package.dependencies] +mkdocs = "*" + +[[package]] +name = "mkdocs-jupyter" +version = "0.24.3" +description = "Use Jupyter in mkdocs websites" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_jupyter-0.24.3-py3-none-any.whl", hash = "sha256:904262a8678a5e5920b7c3c03b5010b36301a69d0a38f2fcbf430493adf6879e"}, + {file = "mkdocs_jupyter-0.24.3.tar.gz", hash = "sha256:3d81da9aea27480e93bab22438910c4f0b9630613e74f85b576590d78e0e8b14"}, +] + +[package.dependencies] +ipykernel = ">6.0.0,<7.0.0" +jupytext = ">1.13.8,<2" +mkdocs = ">=1.4.0,<2" +mkdocs-material = ">9.0.0" +nbconvert = ">=7.2.9,<8" +pygments = ">2.12.0" + +[package.extras] +test = ["coverage[toml]", "pymdown-extensions", "pytest", "pytest-cov"] + +[[package]] +name = "mkdocs-literate-nav" +version = "0.6.1" +description = "MkDocs plugin to specify the navigation in Markdown instead of YAML" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_literate_nav-0.6.1-py3-none-any.whl", hash = "sha256:e70bdc4a07050d32da79c0b697bd88e9a104cf3294282e9cb20eec94c6b0f401"}, + {file = "mkdocs_literate_nav-0.6.1.tar.gz", hash = "sha256:78a7ab6d878371728acb0cdc6235c9b0ffc6e83c997b037f4a5c6ff7cef7d759"}, +] + +[package.dependencies] +mkdocs = ">=1.0.3" + +[[package]] +name = "mkdocs-macros-plugin" +version = "0.7.0" +description = "Unleash the power of MkDocs with macros and variables" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mkdocs-macros-plugin-0.7.0.tar.gz", hash = "sha256:9e64e1cabcf6925359de29fe54f62d5847fb455c2528c440b87f8f1240650608"}, + {file = "mkdocs_macros_plugin-0.7.0-py3-none-any.whl", hash = "sha256:96bdabeb98b96139544f0048ea2f5cb80c7befde6b21e94c6d4596c22774cbcf"}, +] + +[package.dependencies] +jinja2 = "*" +mkdocs = ">=0.17" +python-dateutil = "*" +pyyaml = "*" +termcolor = "*" + +[package.extras] +test = ["mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material (>=6.2)"] + +[[package]] +name = "mkdocs-material" +version = "9.5.9" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material-9.5.9-py3-none-any.whl", hash = "sha256:a5d62b73b3b74349e45472bfadc129c871dd2d4add68d84819580597b2f50d5d"}, + {file = "mkdocs_material-9.5.9.tar.gz", hash = "sha256:635df543c01c25c412d6c22991872267723737d5a2f062490f33b2da1c013c6d"}, +] + +[package.dependencies] +babel = ">=2.10,<3.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.0,<4.0" +markdown = ">=3.2,<4.0" +mkdocs = ">=1.5.3,<1.6.0" +mkdocs-material-extensions = ">=1.3,<2.0" +paginate = ">=0.5,<1.0" +pygments = ">=2.16,<3.0" +pymdown-extensions = ">=10.2,<11.0" +regex = ">=2022.4" +requests = ">=2.26,<3.0" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.24.0" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings-0.24.0-py3-none-any.whl", hash = "sha256:f4908560c10f587326d8f5165d1908817b2e280bbf707607f601c996366a2264"}, + {file = "mkdocstrings-0.24.0.tar.gz", hash = "sha256:222b1165be41257b494a9d29b14135d2b7ca43f38161d5b10caae03b87bd4f7e"}, +] + +[package.dependencies] +click = ">=7.0" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.11.1" +Markdown = ">=3.3" +MarkupSafe = ">=1.1" +mkdocs = ">=1.4" +mkdocs-autorefs = ">=0.3.1" +platformdirs = ">=2.2.0" +pymdown-extensions = ">=6.3" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=0.5.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "1.8.0" +description = "A Python handler for mkdocstrings." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings_python-1.8.0-py3-none-any.whl", hash = "sha256:4209970cc90bec194568682a535848a8d8489516c6ed4adbe58bbc67b699ca9d"}, + {file = "mkdocstrings_python-1.8.0.tar.gz", hash = "sha256:1488bddf50ee42c07d9a488dddc197f8e8999c2899687043ec5dd1643d057192"}, +] + +[package.dependencies] +griffe = ">=0.37" +mkdocstrings = ">=0.20" + +[[package]] +name = "mypy" +version = "1.8.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nbclient" +version = "0.9.0" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "nbclient-0.9.0-py3-none-any.whl", hash = "sha256:a3a1ddfb34d4a9d17fc744d655962714a866639acd30130e9be84191cd97cd15"}, + {file = "nbclient-0.9.0.tar.gz", hash = "sha256:4b28c207877cf33ef3a9838cdc7a54c5ceff981194a82eac59d558f05487295e"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.4" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.16.0" +description = "Converting Jupyter Notebooks" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbconvert-7.16.0-py3-none-any.whl", hash = "sha256:ad3dc865ea6e2768d31b7eb6c7ab3be014927216a5ece3ef276748dd809054c7"}, + {file = "nbconvert-7.16.0.tar.gz", hash = "sha256:813e6553796362489ae572e39ba1bff978536192fb518e10826b0e8cadf03ec8"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +bleach = "!=5.0.0" +defusedxml = "*" +importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} +jinja2 = ">=3.0" +jupyter-core = ">=4.7" +jupyterlab-pygments = "*" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<4" +nbclient = ">=0.5.0" +nbformat = ">=5.7" +packaging = "*" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +tinycss2 = "*" +traitlets = ">=5.1" + +[package.extras] +all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] +docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["nbconvert[qtpng]"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest"] +webpdf = ["playwright"] + +[[package]] +name = "nbformat" +version = "5.9.2" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.9.2-py3-none-any.whl", hash = "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9"}, + {file = "nbformat-5.9.2.tar.gz", hash = "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192"}, +] + +[package.dependencies] +fastjsonschema = "*" +jsonschema = ">=2.6" +jupyter-core = "*" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "paginate" +version = "0.5.6" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +files = [ + {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +description = "Utilities for writing pandoc filters in python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "plotille" +version = "5.0.0" +description = "Plot in the terminal using braille dots." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "plotille-5.0.0.tar.gz", hash = "sha256:99e5ca51a2e4c922ead3a3b0863cc2c6a9a4b3f701944589df10f42ce02ab3dc"}, +] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pymdown-extensions" +version = "10.7" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"}, + {file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"}, +] + +[package.dependencies] +markdown = ">=3.5" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.12)"] + +[[package]] +name = "pytest" +version = "8.0.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.0.1-py3-none-any.whl", hash = "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"}, + {file = "pytest-8.0.1.tar.gz", hash = "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.3.0,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-levenshtein" +version = "0.25.0" +description = "Python extension for computing string edit distances and similarities." +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-Levenshtein-0.25.0.tar.gz", hash = "sha256:6b826b0f14f3191fd04cc6e151ec7aa5b4c2de0607091154995df81913893b65"}, + {file = "python_Levenshtein-0.25.0-py3-none-any.whl", hash = "sha256:fc4a985d664b307f3b7f245d25f28236ece41c039de894f0e26e77614b0eef68"}, +] + +[package.dependencies] +Levenshtein = "0.25.0" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "pyzmq" +version = "25.1.2" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"}, + {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"}, + {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"}, + {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"}, + {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"}, + {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"}, + {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"}, + {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"}, + {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"}, + {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"}, + {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"}, + {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"}, + {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "rapidfuzz" +version = "3.6.1" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win_arm64.whl", hash = "sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win32.whl", hash = "sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win_arm64.whl", hash = "sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win32.whl", hash = "sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win_arm64.whl", hash = "sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-win32.whl", hash = "sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win32.whl", hash = "sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win_arm64.whl", hash = "sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b"}, + {file = "rapidfuzz-3.6.1.tar.gz", hash = "sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7"}, +] + +[package.extras] +full = ["numpy"] + +[[package]] +name = "referencing" +version = "0.33.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, + {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + +[[package]] +name = "ruff" +version = "0.2.2" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, + {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, + {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, + {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, + {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, +] + +[[package]] +name = "setuptools" +version = "69.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "sh" +version = "2.0.6" +description = "Python subprocess replacement" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "sh-2.0.6-py3-none-any.whl", hash = "sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2"}, + {file = "sh-2.0.6.tar.gz", hash = "sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1"}, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "stevedore" +version = "5.1.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, + {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "termcolor" +version = "2.4.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.8" +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "textual" +version = "0.51.0" +description = "Modern Text User Interface framework" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "textual-0.51.0-py3-none-any.whl", hash = "sha256:c25c8d5f462ca169fa50add10f4d3604d98409b6a9f8dadff6a269cc7027516c"}, + {file = "textual-0.51.0.tar.gz", hash = "sha256:ca3d58c00a360ef1988a9be2dbb34d8a8526f2b9fe40c2ed7ac6687875422efd"}, +] + +[package.dependencies] +markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] + +[[package]] +name = "tinycss2" +version = "1.2.1" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, + {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["flake8", "isort", "pytest"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "traitlets" +version = "5.14.1" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, + {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "uc-micro-py" +version = "1.0.3" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, + {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, +] + +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.25.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "vulture" +version = "2.11" +description = "Find dead code" +optional = false +python-versions = ">=3.8" +files = [ + {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, + {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, +] + +[package.dependencies] +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "watchdog" +version = "4.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, + {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, + {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, + {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, + {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, + {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, + {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, + {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, + {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, + {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.1" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "wtforms" +version = "3.1.2" +description = "Form validation and rendering for Python web development." +optional = false +python-versions = ">=3.8" +files = [ + {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"}, + {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"}, +] + +[package.dependencies] +markupsafe = "*" + +[package.extras] +email = ["email-validator"] + +[[package]] +name = "xonsh" +version = "0.14.0" +description = "Python-powered, cross-platform, Unix-gazing shell" +optional = false +python-versions = ">=3.8" +files = [ + {file = "xonsh-0.14.0-py310-none-any.whl", hash = "sha256:72506c6dc494103df6d04467e127abddb1c6cbe05cc5903b6a4cbfbad217ff5d"}, + {file = "xonsh-0.14.0-py311-none-any.whl", hash = "sha256:678a65671bd0a62cdc43e932d6aecc8b1622aa18942e0afb388b8b8ae02f67a5"}, + {file = "xonsh-0.14.0-py38-none-any.whl", hash = "sha256:25976edc5695fb5806b8b23f384ff48e618e07f4596ec0806007f63122917a83"}, + {file = "xonsh-0.14.0-py39-none-any.whl", hash = "sha256:751b615726d2322d43c8166ad4bc5cbe65d03a3728f1837aa02380fa9fadb189"}, + {file = "xonsh-0.14.0.tar.gz", hash = "sha256:45a8aaabb17ce0d6d4eca9b709ecfd7ce1c8fb92162decd29a45bf88a60e9bf1"}, +] + +[package.extras] +bestshell = ["prompt-toolkit (>=3.0.29)", "pygments (>=2.2)"] +dev = ["pre-commit", "re-ver", "tomli", "xonsh[doc,test]"] +doc = ["doctr", "furo", "livereload", "matplotlib", "myst-parser", "numpydoc", "psutil", "pyzmq", "runthis-sphinxext", "sphinx (>=3.1,<5)", "tornado", "xonsh[bestshell]"] +full = ["distro", "gnureadline", "setproctitle", "ujson", "xonsh[ptk,pygments]"] +linux = ["distro"] +mac = ["gnureadline"] +proctitle = ["setproctitle"] +ptk = ["prompt-toolkit (>=3.0.29)", "pyperclip"] +pygments = ["pygments (>=2.2)"] +test = ["coverage (>=5.3.1)", "prompt-toolkit (>=3.0.29)", "pygments (>=2.2)", "pyte (>=0.8.0)", "pytest (>=7)", "pytest-cov", "pytest-mock", "pytest-rerunfailures", "pytest-subprocess", "pytest-timeout", "restructuredtext-lint", "virtualenv (>=20.16.2)", "xonsh[bestshell]"] + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.8.1,<4" +content-hash = "c7878ca990524224ddd99504c79d9f7b278186bc74b8fdff94789cab39c72589" diff --git a/pyproject.toml b/pyproject.toml index e8d0e9d..06c8aea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,57 +14,49 @@ exclude = [ include = ["src/feedback_linker/py.typed"] [tool.poetry.dependencies] -python = "^3.8.1" +python = ">=3.8.1,<4" +flask = ">=3.0.2" +flask-wtf = ">=1.2.1" +email-validator = ">=2.1.0.post1" [tool.poetry.dev-dependencies] -urllib3 = "<2" # fix poetry issues -pytest = "^7.3.2" -pytest-cov = "^4.1.0" -coverage = "^7.2.7" -black = "^23.3.0" -pre-commit = "^3.3.2" -ruff = "^0.0.278" -mypy = "^1.3.0" -bandit = "^1.7.5" -vulture = "^2.7" -mccabe = "^0.6.1" -# if you want to use docker-compose from your system, remove compose-go here -compose-go = "^2.18.1" +pytest = ">=7.3.2" +pytest-cov = ">=4.1.0" +coverage = ">=7.2.7" +black = ">=23.3.0" +pre-commit = ">=3.3.2" +ruff = ">=0.2.2" +mypy = ">=1.8.0" +bandit = ">=1.7.5" +vulture = ">=2.7" +mccabe = ">=0.6.1" +compose-go = ">=2.18.1" ipython = "<8" ipykernel = ">=6.0.0" -Jinja2 = "^3.1.2" -mkdocs = "^1.4.3" -mkdocs-exclude = "^1.0.2" -mkdocs-jupyter = "^0.24.1" -mkdocs-literate-nav = "^0.6.0" +Jinja2 = ">=3.1.2" +mkdocs = ">=1.4.3" +mkdocs-exclude = ">=1.0.2" +mkdocs-jupyter = ">=0.24.1" +mkdocs-literate-nav = ">=0.6.0" mkdocs-macros-plugin = ">=0.7.0,<1" -mkdocs-material = "^9.1.15" -mkdocstrings = "^0.21.2" -mkdocstrings-python = "^1.1.2" -makim ="^1.8.3" - - +mkdocs-material = ">=9.1.15" +mkdocstrings = ">=0.21.2" +mkdocstrings-python = ">=1.1.2" +makim = "1.13.0" +containers-sugar = "1.11.1" [tool.pytest.ini_options] testpaths = [ "tests", ] -[tool.black] -line-length = 79 -target-version = ["py38"] -force-exclude = '''(?x)( - docs/* - | .*\\.egg-info -)''' # TOML's single-quoted strings do not require escaping backslashes - [tool.bandit] exclude_dirs = ["tests"] targets = "./" [tool.vulture] exclude = ["tests"] -ignore_decorators = [] +ignore_decorators = ["@abc.abstractmethod"] ignore_names = [] make_whitelist = true min_confidence = 80 @@ -79,23 +71,30 @@ src = ["./"] exclude = [ 'docs', ] +fix = true + +[tool.ruff.lint] +ignore = ["PLR0913"] select = [ "E", # pycodestyle "F", # pyflakes "D", # pydocstyle "YTT", # flake8-2020 + "PL", # PL "RUF", # Ruff-specific rules - "I001", # isort + "I001", # isort ] -fixable = ["I001"] -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "numpy" -[tool.ruff.isort] +[tool.ruff.lint.isort] # Use a single line between direct and from import lines-between-types = 1 +[tool.ruff.format] +quote-style = "single" + [tool.mypy] python_version = "3.8" check_untyped_defs = true @@ -104,3 +103,7 @@ ignore_missing_imports = true warn_unused_ignores = true warn_redundant_casts = true warn_unused_configs = true + +[[tool.mypy.overrides]] +# module = [] +ignore_missing_imports = true diff --git a/src/feedback_linker/__init__.py b/src/feedback_linker/__init__.py index 2a57c92..17c23a5 100644 --- a/src/feedback_linker/__init__.py +++ b/src/feedback_linker/__init__.py @@ -9,11 +9,11 @@ def get_version(): try: return importlib_metadata.version(__name__) except importlib_metadata.PackageNotFoundError: # pragma: no cover - return "0.1.0" # semantic-release + return '0.1.0' # semantic-release version = get_version() __version__ = version -__author__ = "Ivan Ogasawara" -__email__ = "ivan.ogasawara@gmail.com" +__author__ = 'Ivan Ogasawara' +__email__ = 'ivan.ogasawara@gmail.com' diff --git a/src/feedback_linker/feedback_linker.py b/src/feedback_linker/app.py similarity index 100% rename from src/feedback_linker/feedback_linker.py rename to src/feedback_linker/app.py From 84cfe96e6c935db163896964a2287ac3743f199a Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Sun, 18 Feb 2024 23:06:22 -0400 Subject: [PATCH 03/12] Fix linter issues --- .makim.yaml | 15 +- poetry.lock | 219 +++++++++++++++++++- pyproject.toml | 20 +- src/feedback_linker/__init__.py | 2 +- src/feedback_linker/app.py | 91 +++++++- src/feedback_linker/forms.py | 114 ++++++++++ src/feedback_linker/models.py | 102 +++++++++ src/feedback_linker/templates/base.html | 25 +++ src/feedback_linker/templates/index.html | 4 + src/feedback_linker/templates/projects.html | 12 ++ tests/test_feedback_linker.py | 4 +- 11 files changed, 594 insertions(+), 14 deletions(-) create mode 100644 src/feedback_linker/forms.py create mode 100644 src/feedback_linker/models.py create mode 100644 src/feedback_linker/templates/base.html create mode 100644 src/feedback_linker/templates/index.html create mode 100644 src/feedback_linker/templates/projects.html diff --git a/.makim.yaml b/.makim.yaml index e65b4e5..ccc3587 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -2,23 +2,24 @@ version: 1.0 groups: clean: targets: - all: + tmp: help: Clean unnecessary temporary files + shell: bash run: | rm -fr build/ rm -fr dist/ rm -fr .eggs/ - find . -name '*.egg-info' -exec rm -fr {} + - find . -name '*.egg' -exec rm -f {} + - find . -name '*.pyc' -exec rm -f {} + - find . -name - find . -name '__pycache__' -exec rm -fr '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} +{} + rm -f .coverage rm -fr htmlcov/ rm -fr .pytest_cache rm -fr .mypy_cache rm -fr .ruff_cache + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -f {} + + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + find . -name '*~' -exec rm -f {} + docs: targets: diff --git a/poetry.lock b/poetry.lock index 6d21c60..5c9b1d2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -669,6 +669,36 @@ Werkzeug = ">=3.0.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +description = "Add SQLAlchemy support to your Flask application." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[package.dependencies] +flask = ">=2.2.5" +sqlalchemy = ">=2.0.16" + +[[package]] +name = "flask-sqlalchemy-stubs" +version = "0.2" +description = "flask_sqlalchemy stubs" +optional = false +python-versions = "*" +files = [ + {file = "flask_sqlalchemy-stubs-0.2.tar.gz", hash = "sha256:e4656bf4ddfcc4092a79dc75456978fd6761302f62f56526221ca867bd65c4b4"}, + {file = "flask_sqlalchemy_stubs-0.2-py3-none-any.whl", hash = "sha256:7d8e21571b4ee92b15b9ad0dad0d1f488543272a85d76a58580df0ba6685142f"}, +] + +[package.dependencies] +mypy = ">=0.720" +typing-extensions = ">=3.7.4" + [[package]] name = "flask-wtf" version = "1.2.1" @@ -719,6 +749,77 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "griffe" version = "0.40.1" @@ -2749,6 +2850,108 @@ files = [ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.27" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, + {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, + {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-stubs" +version = "0.4" +description = "SQLAlchemy stubs and mypy plugin" +optional = false +python-versions = "*" +files = [ + {file = "sqlalchemy-stubs-0.4.tar.gz", hash = "sha256:c665d6dd4482ef642f01027fa06c3d5e91befabb219dc71fc2a09e7d7695f7ae"}, + {file = "sqlalchemy_stubs-0.4-py3-none-any.whl", hash = "sha256:5eec7aa110adf9b957b631799a72fef396b23ff99fe296df726645d01e312aa5"}, +] + +[package.dependencies] +mypy = ">=0.790" +typing-extensions = ">=3.7.4" + [[package]] name = "stevedore" version = "5.1.0" @@ -2892,6 +3095,20 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2 doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +[[package]] +name = "types-wtforms" +version = "3.1.0.20240205" +description = "Typing stubs for WTForms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-WTForms-3.1.0.20240205.tar.gz", hash = "sha256:96b132466ecb5d04660678c2bef74d7fe0ff69dd9496c3cd23dbb27c0373ab5c"}, + {file = "types_WTForms-3.1.0.20240205-py3-none-any.whl", hash = "sha256:3ab23f561d55b0cc3e499a333a86241c42a0fbe5a631f108a28ee3134ccd795d"}, +] + +[package.dependencies] +MarkupSafe = "*" + [[package]] name = "typing-extensions" version = "4.9.0" @@ -3109,4 +3326,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4" -content-hash = "c7878ca990524224ddd99504c79d9f7b278186bc74b8fdff94789cab39c72589" +content-hash = "db487ad820ada919b9066198862714cf6a9290fd19ea4468c6eb67fc18005278" diff --git a/pyproject.toml b/pyproject.toml index 06c8aea..f6c6efe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,10 @@ python = ">=3.8.1,<4" flask = ">=3.0.2" flask-wtf = ">=1.2.1" email-validator = ">=2.1.0.post1" +sqlalchemy = ">=2.0.27" +flask-sqlalchemy = ">=3.1.1" +types-wtforms = "^3.1.0.20240205" +sqlalchemy-stubs = "^0.4" [tool.poetry.dev-dependencies] pytest = ">=7.3.2" @@ -104,6 +108,18 @@ warn_unused_ignores = true warn_redundant_casts = true warn_unused_configs = true +# [[tool.mypy.overrides]] +# module = [ +# "wtforms", +# ] +# ignore_missing_imports = true + + [[tool.mypy.overrides]] -# module = [] -ignore_missing_imports = true +module = "feedback_linker.models" +ignore_errors = true + + +[[tool.mypy.overrides]] +module = "feedback_linker.forms" +ignore_errors = true diff --git a/src/feedback_linker/__init__.py b/src/feedback_linker/__init__.py index 17c23a5..e6c2573 100644 --- a/src/feedback_linker/__init__.py +++ b/src/feedback_linker/__init__.py @@ -4,7 +4,7 @@ from importlib import metadata as importlib_metadata -def get_version(): +def get_version() -> str: """Return the program version.""" try: return importlib_metadata.version(__name__) diff --git a/src/feedback_linker/app.py b/src/feedback_linker/app.py index dd0b80e..459f680 100644 --- a/src/feedback_linker/app.py +++ b/src/feedback_linker/app.py @@ -1 +1,90 @@ -"""Main module.""" +"""App module.""" +from __future__ import annotations + +from typing import Union + +from flask import Flask, flash, redirect, render_template, url_for +from werkzeug.wrappers.response import Response + +from feedback_linker.forms import ( + FeedbackForm, + LinkForm, + LoginForm, + PersonForm, + ProjectForm, +) +from feedback_linker.models import ( + Feedback, + Link, + Person, + Project, + db, + init_app, +) + +app = Flask(__name__) +app.config['SECRET_KEY'] = 'your_secret_key' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///feedback_linker.db' +init_app(app) + + +@app.route('/') +def index() -> str: + """Define the view for the index page.""" + return render_template('index.html') + + +@app.route('/login', methods=['GET', 'POST']) +def login() -> str: + """Define the view for managing login.""" + form = LoginForm() + # Login logic here + return render_template('login.html', form=form) + + +@app.route('/projects', methods=['GET', 'POST']) +def manage_projects() -> Union[Response, str]: + """Define the view for managing projects.""" + form = ProjectForm() + if form.validate_on_submit(): + project = Project(name=form.name.data) + db.session.add(project) + db.session.commit() + flash('Project created successfully!') + return redirect(url_for('manage_projects')) + projects = Project.query.all() + return render_template('project_list.html', form=form, projects=projects) + + +@app.route('/people', methods=['GET', 'POST']) +def manage_people() -> str: + """Define the view for managing people.""" + form = PersonForm() + # Person creation logic here + people = Person.query.all() + return render_template('people_list.html', form=form, people=people) + + +@app.route('/links', methods=['GET', 'POST']) +def manage_links() -> str: + """Define the view for managing links.""" + form = LinkForm() + # Link creation logic here + links = Link.query.all() + return render_template('link_list.html', form=form, links=links) + + +@app.route('/feedback', methods=['GET', 'POST']) +def submit_feedback() -> str: + """Define the view for submitting feedback.""" + form = FeedbackForm() + # Feedback submission logic here + feedbacks = Feedback.query.all() + return render_template( + 'feedback_list.html', form=form, feedbacks=feedbacks + ) + + +if __name__ == '__main__': + db.create_all() + app.run(debug=True) diff --git a/src/feedback_linker/forms.py b/src/feedback_linker/forms.py new file mode 100644 index 0000000..3317ca8 --- /dev/null +++ b/src/feedback_linker/forms.py @@ -0,0 +1,114 @@ +"""Forms module.""" +from __future__ import annotations + +from flask_wtf import FlaskForm +from wtforms import ( + IntegerField, + PasswordField, + SelectField, + StringField, + SubmitField, + TextAreaField, +) +from wtforms.ext.sqlalchemy.fields import ( + QuerySelectField, + QuerySelectMultipleField, +) +from wtforms.validators import DataRequired, Email, Length + +from .models import Link, Person, Project + + +def project_choices(): + """Return the choices for project.""" + return Project.query.all() + + +def person_choices(): + """Return the choices for person.""" + return Person.query.all() + + +class LoginForm(FlaskForm): + """The login form.""" + + email = StringField('Email', validators=[DataRequired(), Email()]) + password = PasswordField( + 'Password', validators=[DataRequired(), Length(min=6)] + ) + submit = SubmitField('Log In') + + +class ProjectForm(FlaskForm): + """The project form.""" + + name = StringField('Project Name', validators=[DataRequired()]) + people = QuerySelectMultipleField( + 'Select People', query_factory=person_choices, get_label='name' + ) + submit = SubmitField('Submit') + + +class PersonForm(FlaskForm): + """The person form.""" + + name = StringField('Name', validators=[DataRequired()]) + email = StringField('Email', validators=[DataRequired(), Email()]) + projects = QuerySelectMultipleField( + 'Select Projects', query_factory=project_choices, get_label='name' + ) + submit = SubmitField('Submit') + + +class LinkForm(FlaskForm): + """The link form.""" + + person_one = QuerySelectField( + 'Person One', + query_factory=person_choices, + get_label='name', + validators=[DataRequired()], + ) + person_two = QuerySelectField( + 'Person Two', + query_factory=person_choices, + get_label='name', + validators=[DataRequired()], + ) + supervisor = QuerySelectField( + 'Supervisor', + query_factory=person_choices, + get_label='name', + validators=[DataRequired()], + ) + periodicity = SelectField( + 'Periodicity', + choices=[ + ('daily', 'Daily'), + ('weekly', 'Weekly'), + ('monthly', 'Monthly'), + ], + validators=[DataRequired()], + ) + times = IntegerField('Every X times', validators=[DataRequired()]) + submit = SubmitField('Submit') + + +class FeedbackForm(FlaskForm): + """The feedback form.""" + + content = TextAreaField('Feedback Content', validators=[DataRequired()]) + link = QuerySelectField( + 'Link', + query_factory=lambda: Link.query.all(), + get_label='id', + validators=[DataRequired()], + ) + submit = SubmitField('Submit') + + +def init_app(app) -> None: + """Initialize the app for forms.""" + # This function is here if you need to initialize anything specifically + # for the forms + pass diff --git a/src/feedback_linker/models.py b/src/feedback_linker/models.py new file mode 100644 index 0000000..4b97c13 --- /dev/null +++ b/src/feedback_linker/models.py @@ -0,0 +1,102 @@ +"""Models module.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + +if TYPE_CHECKING: + from flask_sqlalchemy.model import Model + + BaseModel = db.make_declarative_base(Model) +else: + BaseModel = db.Model + + +class Project(BaseModel): # type: ignore[name-defined] + """Project model.""" + + __tablename__ = 'project' + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + people = db.relationship( + 'Person', secondary='project_person', back_populates='projects' + ) + supervisors = db.relationship('Person', secondary='project_supervisor') + + +class Person(BaseModel): # type: ignore[name-defined] + """Person model.""" + + __tablename__ = 'person' + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + email = db.Column(db.String(100), unique=True, nullable=False) + projects = db.relationship( + 'Project', secondary='project_person', back_populates='people' + ) + links = db.relationship( + 'Link', secondary='person_link', back_populates='people' + ) + + +project_person = db.Table( + 'project_person', + db.Column( + 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True + ), + db.Column( + 'project_id', db.Integer, db.ForeignKey('project.id'), primary_key=True + ), +) + +project_supervisor = db.Table( + 'project_supervisor', + db.Column( + 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True + ), + db.Column( + 'project_id', db.Integer, db.ForeignKey('project.id'), primary_key=True + ), +) + + +class Link(BaseModel): # type: ignore[name-defined] + """Link model.""" + + __tablename__ = 'link' + id = db.Column(db.Integer, primary_key=True) + person_one_id = db.Column(db.Integer, db.ForeignKey('person.id')) + person_two_id = db.Column(db.Integer, db.ForeignKey('person.id')) + supervisor_id = db.Column(db.Integer, db.ForeignKey('person.id')) + periodicity = db.Column(db.String(50)) + times = db.Column(db.Integer) + feedback = db.relationship('Feedback', backref='link', lazy=True) + + +person_link = db.Table( + 'person_link', + db.Column( + 'link_id', db.Integer, db.ForeignKey('link.id'), primary_key=True + ), + db.Column( + 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True + ), +) + + +class Feedback(BaseModel): # type: ignore[name-defined] + """Feedback model.""" + + __tablename__ = 'feedback' + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text, nullable=False) + link_id = db.Column(db.Integer, db.ForeignKey('link.id')) + timestamp = db.Column(db.DateTime, server_default=db.func.now()) + + +def init_app(app) -> None: + """Initialize app for the database.""" + db.init_app(app) diff --git a/src/feedback_linker/templates/base.html b/src/feedback_linker/templates/base.html new file mode 100644 index 0000000..677c710 --- /dev/null +++ b/src/feedback_linker/templates/base.html @@ -0,0 +1,25 @@ + + + + + + Feedback-Linker + + + +

+ +
+ +
{% block content %}{% endblock %}
+ +
+ +
+ + + + diff --git a/src/feedback_linker/templates/index.html b/src/feedback_linker/templates/index.html new file mode 100644 index 0000000..541f6cb --- /dev/null +++ b/src/feedback_linker/templates/index.html @@ -0,0 +1,4 @@ +{% extends "base.html" %} {% block content %} +

Welcome to Feedback-Linker

+

This is the landing page.

+{% endblock %} diff --git a/src/feedback_linker/templates/projects.html b/src/feedback_linker/templates/projects.html new file mode 100644 index 0000000..a1d37eb --- /dev/null +++ b/src/feedback_linker/templates/projects.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} {% block content %} +
+

Create Project

+
+ {{ form.hidden_tag() }} +
+ {{ form.name.label }} {{ form.name(class="form-control") }} +
+
{{ form.submit(class="btn btn-primary") }}
+
+
+{% endblock %} diff --git a/tests/test_feedback_linker.py b/tests/test_feedback_linker.py index 08c3d84..00a0d81 100644 --- a/tests/test_feedback_linker.py +++ b/tests/test_feedback_linker.py @@ -3,11 +3,11 @@ @pytest.fixture -def response_pytest(): +def response_pytest() -> bool: """Sample pytest fixture.""" return True -def test_content_pytest(): +def test_content_pytest() -> None: """Test with pytest.""" assert True From 5b0e5d2b92a746731a58018e3951f9b729ed8f19 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Sun, 18 Feb 2024 23:31:40 -0400 Subject: [PATCH 04/12] change some code --- poetry.lock | 31 +++++++------------------ pyproject.toml | 6 +++-- src/feedback_linker/app.py | 2 +- src/feedback_linker/models.py | 2 ++ src/feedback_linker/templates/base.html | 19 ++++++++++++++- 5 files changed, 34 insertions(+), 26 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5c9b1d2..4e8bd0e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -684,21 +684,6 @@ files = [ flask = ">=2.2.5" sqlalchemy = ">=2.0.16" -[[package]] -name = "flask-sqlalchemy-stubs" -version = "0.2" -description = "flask_sqlalchemy stubs" -optional = false -python-versions = "*" -files = [ - {file = "flask_sqlalchemy-stubs-0.2.tar.gz", hash = "sha256:e4656bf4ddfcc4092a79dc75456978fd6761302f62f56526221ca867bd65c4b4"}, - {file = "flask_sqlalchemy_stubs-0.2-py3-none-any.whl", hash = "sha256:7d8e21571b4ee92b15b9ad0dad0d1f488543272a85d76a58580df0ba6685142f"}, -] - -[package.dependencies] -mypy = ">=0.720" -typing-extensions = ">=3.7.4" - [[package]] name = "flask-wtf" version = "1.2.1" @@ -3267,20 +3252,22 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "wtforms" -version = "3.1.2" -description = "Form validation and rendering for Python web development." +version = "2.3.3" +description = "A flexible forms validation and rendering library for Python web development." optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"}, - {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"}, + {file = "WTForms-2.3.3-py2.py3-none-any.whl", hash = "sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c"}, + {file = "WTForms-2.3.3.tar.gz", hash = "sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"}, ] [package.dependencies] -markupsafe = "*" +MarkupSafe = "*" [package.extras] email = ["email-validator"] +ipaddress = ["ipaddress"] +locale = ["Babel (>=1.3)"] [[package]] name = "xonsh" @@ -3326,4 +3313,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4" -content-hash = "db487ad820ada919b9066198862714cf6a9290fd19ea4468c6eb67fc18005278" +content-hash = "e93a67349b26077f6a64533c1fc3681017ee89c0a20ddf4989871a286165f3da" diff --git a/pyproject.toml b/pyproject.toml index f6c6efe..b0bebf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,11 +17,11 @@ include = ["src/feedback_linker/py.typed"] python = ">=3.8.1,<4" flask = ">=3.0.2" flask-wtf = ">=1.2.1" +wtforms = "<3" email-validator = ">=2.1.0.post1" sqlalchemy = ">=2.0.27" flask-sqlalchemy = ">=3.1.1" -types-wtforms = "^3.1.0.20240205" -sqlalchemy-stubs = "^0.4" + [tool.poetry.dev-dependencies] pytest = ">=7.3.2" @@ -48,6 +48,8 @@ mkdocstrings = ">=0.21.2" mkdocstrings-python = ">=1.1.2" makim = "1.13.0" containers-sugar = "1.11.1" +types-wtforms = ">=3.1.0.20240205" +sqlalchemy-stubs = ">=0.4" [tool.pytest.ini_options] testpaths = [ diff --git a/src/feedback_linker/app.py b/src/feedback_linker/app.py index 459f680..6635fc4 100644 --- a/src/feedback_linker/app.py +++ b/src/feedback_linker/app.py @@ -25,6 +25,7 @@ app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///feedback_linker.db' + init_app(app) @@ -86,5 +87,4 @@ def submit_feedback() -> str: if __name__ == '__main__': - db.create_all() app.run(debug=True) diff --git a/src/feedback_linker/models.py b/src/feedback_linker/models.py index 4b97c13..6595c17 100644 --- a/src/feedback_linker/models.py +++ b/src/feedback_linker/models.py @@ -100,3 +100,5 @@ class Feedback(BaseModel): # type: ignore[name-defined] def init_app(app) -> None: """Initialize app for the database.""" db.init_app(app) + with app.app_context(): + db.create_all() diff --git a/src/feedback_linker/templates/base.html b/src/feedback_linker/templates/base.html index 677c710..09cea92 100644 --- a/src/feedback_linker/templates/base.html +++ b/src/feedback_linker/templates/base.html @@ -5,9 +5,26 @@ Feedback-Linker + + +
From 381f7bf35327245fe2613b7863c407a03c61ac13 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 14:44:41 -0400 Subject: [PATCH 05/12] fix linter issues --- .dockerignore | 12 + .envs/.local/.django | 4 + .envs/.local/.postgres | 7 + .envs/.production/.django | 34 + .envs/.production/.postgres | 7 + .pre-commit-config.yaml | 52 +- .prettierignore | 1 + containers/compose.local.yml | 51 + containers/compose.production.yml | 61 + containers/django/Dockerfile | 82 + containers/django/Dockerfile.local | 62 + containers/django/entrypoint | 46 + containers/django/start | 29 + containers/nginx/Dockerfile | 2 + containers/nginx/default.conf | 7 + containers/postgres/Dockerfile | 6 + .../maintenance/_sourced/constants.sh | 5 + .../maintenance/_sourced/countdown.sh | 12 + .../postgres/maintenance/_sourced/messages.sh | 41 + .../postgres/maintenance/_sourced/yes_no.sh | 16 + containers/postgres/maintenance/backup | 38 + containers/postgres/maintenance/backups | 22 + containers/postgres/maintenance/restore | 55 + containers/postgres/maintenance/rmbackup | 36 + containers/traefik/Dockerfile | 5 + containers/traefik/traefik.yml | 73 + docs/mkdocs.yaml => mkdocs.yaml | 4 +- poetry.lock | 2233 +++++++++++++---- pyproject.toml | 276 +- .../py.typed => config/__init__.py} | 0 src/config/api_router.py | 11 + src/config/asgi.py | 43 + src/config/settings/__init__.py | 0 src/config/settings/base.py | 337 +++ src/config/settings/dev.py | 66 + src/config/settings/production.py | 206 ++ src/config/settings/test.py | 37 + src/config/urls.py | 77 + src/config/websocket.py | 13 + src/config/wsgi.py | 40 + src/feedback_linker/__init__.py | 24 +- src/feedback_linker/app.py | 90 - src/feedback_linker/conftest.py | 14 + src/feedback_linker/contrib/__init__.py | 5 + src/feedback_linker/contrib/sites/__init__.py | 5 + .../contrib/sites/migrations/0001_initial.py | 43 + .../migrations/0002_alter_domain_unique.py | 21 + .../0003_set_site_domain_and_name.py | 63 + .../0004_alter_options_ordering_domain.py | 21 + .../contrib/sites/migrations/__init__.py | 5 + src/feedback_linker/forms.py | 114 - src/feedback_linker/models.py | 104 - src/feedback_linker/static/css/project.css | 13 + src/feedback_linker/static/fonts/.gitkeep | 0 .../static/images/favicons/favicon.ico | Bin 0 -> 8348 bytes src/feedback_linker/static/js/project.js | 1 + src/feedback_linker/templates/403.html | 16 + src/feedback_linker/templates/403_csrf.html | 16 + src/feedback_linker/templates/404.html | 16 + src/feedback_linker/templates/500.html | 14 + .../templates/account/account_inactive.html | 12 + .../templates/account/base.html | 15 + .../templates/account/email.html | 97 + .../templates/account/email_confirm.html | 35 + .../templates/account/login.html | 58 + .../templates/account/logout.html | 21 + .../templates/account/password_change.html | 18 + .../templates/account/password_reset.html | 33 + .../account/password_reset_done.html | 21 + .../account/password_reset_from_key.html | 39 + .../account/password_reset_from_key_done.html | 12 + .../templates/account/password_set.html | 20 + .../templates/account/signup.html | 28 + .../templates/account/signup_closed.html | 12 + .../templates/account/verification_sent.html | 17 + .../account/verified_email_required.html | 30 + src/feedback_linker/templates/base.html | 164 +- src/feedback_linker/templates/index.html | 4 - .../templates/pages/about.html | 1 + src/feedback_linker/templates/pages/home.html | 1 + src/feedback_linker/templates/projects.html | 12 - .../templates/users/user_detail.html | 30 + .../templates/users/user_form.html | 21 + src/feedback_linker/users/__init__.py | 0 src/feedback_linker/users/adapters.py | 48 + src/feedback_linker/users/admin.py | 53 + src/feedback_linker/users/api/__init__.py | 0 src/feedback_linker/users/api/serializers.py | 16 + src/feedback_linker/users/api/views.py | 35 + src/feedback_linker/users/apps.py | 13 + .../users/context_processors.py | 8 + src/feedback_linker/users/forms.py | 45 + src/feedback_linker/users/managers.py | 57 + .../users/migrations/0001_initial.py | 112 + .../users/migrations/__init__.py | 0 src/feedback_linker/users/models.py | 38 + src/feedback_linker/users/tests/__init__.py | 0 src/feedback_linker/users/tests/factories.py | 40 + src/feedback_linker/users/tests/test_admin.py | 68 + .../users/tests/test_drf_urls.py | 21 + .../users/tests/test_drf_views.py | 36 + src/feedback_linker/users/tests/test_forms.py | 37 + .../users/tests/test_managers.py | 56 + .../users/tests/test_models.py | 5 + .../users/tests/test_swagger.py | 25 + src/feedback_linker/users/tests/test_urls.py | 20 + src/feedback_linker/users/tests/test_views.py | 104 + src/feedback_linker/users/urls.py | 14 + src/feedback_linker/users/views.py | 44 + src/locale/README.md | 46 + src/locale/en_US/LC_MESSAGES/django.po | 12 + src/locale/fr_FR/LC_MESSAGES/django.po | 335 +++ src/locale/pt_BR/LC_MESSAGES/django.po | 315 +++ src/manage.py | 32 + src/tests/__init__.py | 0 tests/__init__.py | 1 - tests/test_feedback_linker.py | 13 - 117 files changed, 6078 insertions(+), 966 deletions(-) create mode 100644 .dockerignore create mode 100644 .envs/.local/.django create mode 100644 .envs/.local/.postgres create mode 100644 .envs/.production/.django create mode 100644 .envs/.production/.postgres create mode 100644 containers/compose.local.yml create mode 100644 containers/compose.production.yml create mode 100644 containers/django/Dockerfile create mode 100644 containers/django/Dockerfile.local create mode 100644 containers/django/entrypoint create mode 100644 containers/django/start create mode 100644 containers/nginx/Dockerfile create mode 100644 containers/nginx/default.conf create mode 100644 containers/postgres/Dockerfile create mode 100644 containers/postgres/maintenance/_sourced/constants.sh create mode 100644 containers/postgres/maintenance/_sourced/countdown.sh create mode 100644 containers/postgres/maintenance/_sourced/messages.sh create mode 100644 containers/postgres/maintenance/_sourced/yes_no.sh create mode 100644 containers/postgres/maintenance/backup create mode 100644 containers/postgres/maintenance/backups create mode 100644 containers/postgres/maintenance/restore create mode 100644 containers/postgres/maintenance/rmbackup create mode 100644 containers/traefik/Dockerfile create mode 100644 containers/traefik/traefik.yml rename docs/mkdocs.yaml => mkdocs.yaml (99%) rename src/{feedback_linker/py.typed => config/__init__.py} (100%) create mode 100644 src/config/api_router.py create mode 100644 src/config/asgi.py create mode 100644 src/config/settings/__init__.py create mode 100644 src/config/settings/base.py create mode 100644 src/config/settings/dev.py create mode 100644 src/config/settings/production.py create mode 100644 src/config/settings/test.py create mode 100644 src/config/urls.py create mode 100644 src/config/websocket.py create mode 100644 src/config/wsgi.py delete mode 100644 src/feedback_linker/app.py create mode 100644 src/feedback_linker/conftest.py create mode 100644 src/feedback_linker/contrib/__init__.py create mode 100644 src/feedback_linker/contrib/sites/__init__.py create mode 100644 src/feedback_linker/contrib/sites/migrations/0001_initial.py create mode 100644 src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py create mode 100644 src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py create mode 100644 src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py create mode 100644 src/feedback_linker/contrib/sites/migrations/__init__.py delete mode 100644 src/feedback_linker/forms.py delete mode 100644 src/feedback_linker/models.py create mode 100644 src/feedback_linker/static/css/project.css create mode 100644 src/feedback_linker/static/fonts/.gitkeep create mode 100644 src/feedback_linker/static/images/favicons/favicon.ico create mode 100644 src/feedback_linker/static/js/project.js create mode 100644 src/feedback_linker/templates/403.html create mode 100644 src/feedback_linker/templates/403_csrf.html create mode 100644 src/feedback_linker/templates/404.html create mode 100644 src/feedback_linker/templates/500.html create mode 100644 src/feedback_linker/templates/account/account_inactive.html create mode 100644 src/feedback_linker/templates/account/base.html create mode 100644 src/feedback_linker/templates/account/email.html create mode 100644 src/feedback_linker/templates/account/email_confirm.html create mode 100644 src/feedback_linker/templates/account/login.html create mode 100644 src/feedback_linker/templates/account/logout.html create mode 100644 src/feedback_linker/templates/account/password_change.html create mode 100644 src/feedback_linker/templates/account/password_reset.html create mode 100644 src/feedback_linker/templates/account/password_reset_done.html create mode 100644 src/feedback_linker/templates/account/password_reset_from_key.html create mode 100644 src/feedback_linker/templates/account/password_reset_from_key_done.html create mode 100644 src/feedback_linker/templates/account/password_set.html create mode 100644 src/feedback_linker/templates/account/signup.html create mode 100644 src/feedback_linker/templates/account/signup_closed.html create mode 100644 src/feedback_linker/templates/account/verification_sent.html create mode 100644 src/feedback_linker/templates/account/verified_email_required.html delete mode 100644 src/feedback_linker/templates/index.html create mode 100644 src/feedback_linker/templates/pages/about.html create mode 100644 src/feedback_linker/templates/pages/home.html delete mode 100644 src/feedback_linker/templates/projects.html create mode 100644 src/feedback_linker/templates/users/user_detail.html create mode 100644 src/feedback_linker/templates/users/user_form.html create mode 100644 src/feedback_linker/users/__init__.py create mode 100644 src/feedback_linker/users/adapters.py create mode 100644 src/feedback_linker/users/admin.py create mode 100644 src/feedback_linker/users/api/__init__.py create mode 100644 src/feedback_linker/users/api/serializers.py create mode 100644 src/feedback_linker/users/api/views.py create mode 100644 src/feedback_linker/users/apps.py create mode 100644 src/feedback_linker/users/context_processors.py create mode 100644 src/feedback_linker/users/forms.py create mode 100644 src/feedback_linker/users/managers.py create mode 100644 src/feedback_linker/users/migrations/0001_initial.py create mode 100644 src/feedback_linker/users/migrations/__init__.py create mode 100644 src/feedback_linker/users/models.py create mode 100644 src/feedback_linker/users/tests/__init__.py create mode 100644 src/feedback_linker/users/tests/factories.py create mode 100644 src/feedback_linker/users/tests/test_admin.py create mode 100644 src/feedback_linker/users/tests/test_drf_urls.py create mode 100644 src/feedback_linker/users/tests/test_drf_views.py create mode 100644 src/feedback_linker/users/tests/test_forms.py create mode 100644 src/feedback_linker/users/tests/test_managers.py create mode 100644 src/feedback_linker/users/tests/test_models.py create mode 100644 src/feedback_linker/users/tests/test_swagger.py create mode 100644 src/feedback_linker/users/tests/test_urls.py create mode 100644 src/feedback_linker/users/tests/test_views.py create mode 100644 src/feedback_linker/users/urls.py create mode 100644 src/feedback_linker/users/views.py create mode 100644 src/locale/README.md create mode 100644 src/locale/en_US/LC_MESSAGES/django.po create mode 100644 src/locale/fr_FR/LC_MESSAGES/django.po create mode 100644 src/locale/pt_BR/LC_MESSAGES/django.po create mode 100755 src/manage.py create mode 100644 src/tests/__init__.py delete mode 100644 tests/__init__.py delete mode 100644 tests/test_feedback_linker.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a602416 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.editorconfig +.gitattributes +.github +.gitignore +.gitlab-ci.yml +.idea +.pre-commit-config.yaml +.readthedocs.yml +.travis.yml +venv +.git +.envs/ diff --git a/.envs/.local/.django b/.envs/.local/.django new file mode 100644 index 0000000..bcde257 --- /dev/null +++ b/.envs/.local/.django @@ -0,0 +1,4 @@ +# General +# ------------------------------------------------------------------------------ +USE_DOCKER=yes +IPYTHONDIR=/app/.ipython diff --git a/.envs/.local/.postgres b/.envs/.local/.postgres new file mode 100644 index 0000000..668477d --- /dev/null +++ b/.envs/.local/.postgres @@ -0,0 +1,7 @@ +# PostgreSQL +# ------------------------------------------------------------------------------ +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 +POSTGRES_DB=feedback_linker +POSTGRES_USER=debug +POSTGRES_PASSWORD=debug diff --git a/.envs/.production/.django b/.envs/.production/.django new file mode 100644 index 0000000..ac286a2 --- /dev/null +++ b/.envs/.production/.django @@ -0,0 +1,34 @@ +# General +# ------------------------------------------------------------------------------ +# DJANGO_READ_DOT_ENV_FILE=True +DJANGO_SETTINGS_MODULE=config.settings.production +DJANGO_SECRET_KEY=rzB5Sl2LAHyQOR5SAKqc0KeJTyUjNYVhd0ovG6H1dGrdvpd2FtZ9phEacFvFcbd0 +DJANGO_ADMIN_URL=Rz4fpz5g02YMT9VXPQ4QMu8J3hoXOIBa/ +DJANGO_ALLOWED_HOSTS=.https://opensciencelabs.github.io/feedback-linker + +# Security +# ------------------------------------------------------------------------------ +# TIP: better off using DNS, however, redirect is OK too +DJANGO_SECURE_SSL_REDIRECT=False + +# Email +# ------------------------------------------------------------------------------ +DJANGO_SERVER_EMAIL= + + +# django-allauth +# ------------------------------------------------------------------------------ +DJANGO_ACCOUNT_ALLOW_REGISTRATION=True + +# Gunicorn +# ------------------------------------------------------------------------------ +WEB_CONCURRENCY=4 + +# Sentry +# ------------------------------------------------------------------------------ +SENTRY_DSN= + + +# Redis +# ------------------------------------------------------------------------------ +REDIS_URL=redis://redis:6379/0 diff --git a/.envs/.production/.postgres b/.envs/.production/.postgres new file mode 100644 index 0000000..668477d --- /dev/null +++ b/.envs/.production/.postgres @@ -0,0 +1,7 @@ +# PostgreSQL +# ------------------------------------------------------------------------------ +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 +POSTGRES_DB=feedback_linker +POSTGRES_USER=debug +POSTGRES_PASSWORD=debug diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25dd97f..d9a14f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,9 +2,25 @@ default_stages: - commit repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.5.0 hooks: + - id: trailing-whitespace - id: end-of-file-fixer + - id: check-json + - id: check-toml + - id: check-xml + # - id: check-yaml + - id: debug-statements + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: detect-private-key + + - repo: https://github.com/Riverside-Healthcare/djLint + rev: v1.34.1 + hooks: + - id: djlint-reformat-django + - id: djlint-django - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.0.2" @@ -34,24 +50,24 @@ repos: types: - python - - id: mypy - name: mypy - entry: mypy . - language: system - pass_filenames: false + # - id: mypy + # name: mypy + # entry: mypy . + # language: system + # pass_filenames: false - - id: shellcheck - name: shellcheck - entry: shellcheck - language: system - types_or: - - sh - - shell - - ash - - bash - - bats - - dash - - ksh + # - id: shellcheck + # name: shellcheck + # entry: shellcheck + # language: system + # types_or: + # - sh + # - shell + # - ash + # - bash + # - bats + # - dash + # - ksh - id: bandit name: bandit diff --git a/.prettierignore b/.prettierignore index 6618446..f85de3c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ .makim.yaml docs/changelog.md +*.html diff --git a/containers/compose.local.yml b/containers/compose.local.yml new file mode 100644 index 0000000..f6ec67b --- /dev/null +++ b/containers/compose.local.yml @@ -0,0 +1,51 @@ +version: "3" + +volumes: + feedback_linker_local_postgres_data: {} + feedback_linker_local_postgres_data_backups: {} + +services: + django: + build: + context: . + dockerfile: ./compose/local/django/Dockerfile + image: feedback_linker_local_django + container_name: feedback_linker_local_django + depends_on: + - postgres + volumes: + - .:/app:z + env_file: + - ./.envs/.local/.django + - ./.envs/.local/.postgres + ports: + - "8000:8000" + command: /start + + postgres: + build: + context: . + dockerfile: ./compose/production/postgres/Dockerfile + image: feedback_linker_production_postgres + container_name: feedback_linker_local_postgres + volumes: + - feedback_linker_local_postgres_data:/var/lib/postgresql/data + - feedback_linker_local_postgres_data_backups:/backups + env_file: + - ./.envs/.local/.postgres + + docs: + image: feedback_linker_local_docs + container_name: feedback_linker_local_docs + build: + context: . + dockerfile: ./compose/local/docs/Dockerfile + env_file: + - ./.envs/.local/.django + volumes: + - ./docs:/docs:z + - ./config:/app/config:z + - ./feedback_linker:/app/feedback_linker:z + ports: + - "9000:9000" + command: /start-docs diff --git a/containers/compose.production.yml b/containers/compose.production.yml new file mode 100644 index 0000000..3e23c30 --- /dev/null +++ b/containers/compose.production.yml @@ -0,0 +1,61 @@ +version: "3" + +volumes: + production_postgres_data: {} + production_postgres_data_backups: {} + production_traefik: {} + production_django_media: {} + +services: + django: + build: + context: . + dockerfile: ./compose/production/django/Dockerfile + + image: feedback_linker_production_django + volumes: + - production_django_media:/app/feedback_linker/media + depends_on: + - postgres + - redis + env_file: + - ./.envs/.production/.django + - ./.envs/.production/.postgres + command: /start + + postgres: + build: + context: . + dockerfile: ./compose/production/postgres/Dockerfile + image: feedback_linker_production_postgres + volumes: + - production_postgres_data:/var/lib/postgresql/data + - production_postgres_data_backups:/backups + env_file: + - ./.envs/.production/.postgres + + traefik: + build: + context: . + dockerfile: ./compose/production/traefik/Dockerfile + image: feedback_linker_production_traefik + depends_on: + - django + volumes: + - production_traefik:/etc/traefik/acme + ports: + - "0.0.0.0:80:80" + - "0.0.0.0:443:443" + + redis: + image: docker.io/redis:6 + + nginx: + build: + context: . + dockerfile: ./compose/production/nginx/Dockerfile + image: feedback_linker_local_nginx + depends_on: + - django + volumes: + - production_django_media:/usr/share/nginx/media:ro diff --git a/containers/django/Dockerfile b/containers/django/Dockerfile new file mode 100644 index 0000000..206aab1 --- /dev/null +++ b/containers/django/Dockerfile @@ -0,0 +1,82 @@ + +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + +# Python build stage +FROM docker.io/python as python-build-stage + +ARG BUILD_ENVIRONMENT=production + +# Install apt packages +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential \ + # psycopg2 dependencies + libpq-dev + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements . + +# Create Python Dependency and Sub-Dependency Wheels. +RUN pip wheel --wheel-dir /usr/src/app/wheels \ + -r ${BUILD_ENVIRONMENT}.txt + + +# Python 'run' stage +FROM docker.io/python as python-run-stage + +ARG BUILD_ENVIRONMENT=production +ARG APP_HOME=/app + +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 +ENV BUILD_ENV ${BUILD_ENVIRONMENT} + +WORKDIR ${APP_HOME} + +RUN addgroup --system django \ + && adduser --system --ingroup django django + + +# Install required system dependencies +RUN apt-get update && apt-get install --no-install-recommends -y \ + # psycopg2 dependencies + libpq-dev \ + # Translations dependencies + gettext \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction +# copy python dependency wheels from python-build-stage +COPY --from=python-build-stage /usr/src/app/wheels /wheels/ + +# use wheels to install python dependencies +RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ + && rm -rf /wheels/ + + +COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint +RUN sed -i 's/\r$//g' /entrypoint +RUN chmod +x /entrypoint + + +COPY --chown=django:django ./compose/production/django/start /start +RUN sed -i 's/\r$//g' /start +RUN chmod +x /start + + +# copy application code to WORKDIR +COPY --chown=django:django . ${APP_HOME} + +# make django owner of the WORKDIR directory as well. +RUN chown django:django ${APP_HOME} + +USER django + +RUN DATABASE_URL="" \ + DJANGO_SETTINGS_MODULE="config.settings.test" \ + python manage.py compilemessages + +ENTRYPOINT ["/entrypoint"] diff --git a/containers/django/Dockerfile.local b/containers/django/Dockerfile.local new file mode 100644 index 0000000..87a1b24 --- /dev/null +++ b/containers/django/Dockerfile.local @@ -0,0 +1,62 @@ +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + + +# Python build stage +FROM docker.io/python as python-build-stage + +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential \ + # psycopg2 dependencies + libpq-dev \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements /requirements + +# create python dependency wheels +RUN pip wheel --no-cache-dir --wheel-dir /usr/src/app/wheels \ + -r /requirements/local.txt -r /requirements/production.txt \ + && rm -rf /requirements + + +# Python 'run' stage +FROM docker.io/python as python-run-stage + +ARG BUILD_ENVIRONMENT +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # To run the Makefile + make \ + # psycopg2 dependencies + libpq-dev \ + # Translations dependencies + gettext \ + # Uncomment below lines to enable Sphinx output to latex and pdf + # texlive-latex-recommended \ + # texlive-fonts-recommended \ + # texlive-latex-extra \ + # latexmk \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# copy python dependency wheels from python-build-stage +COPY --from=python-build-stage /usr/src/app/wheels /wheels + +# use wheels to install python dependencies +RUN pip install --no-cache /wheels/* \ + && rm -rf /wheels + +COPY ./compose/local/docs/start /start-docs +RUN sed -i 's/\r$//g' /start-docs +RUN chmod +x /start-docs + +WORKDIR /docs diff --git a/containers/django/entrypoint b/containers/django/entrypoint new file mode 100644 index 0000000..78164ab --- /dev/null +++ b/containers/django/entrypoint @@ -0,0 +1,46 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + + + + +if [ -z "${POSTGRES_USER}" ]; then + base_postgres_image_default_user='postgres' + export POSTGRES_USER="${base_postgres_image_default_user}" +fi +export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" + +python << END +import sys +import time + +import psycopg + +suggest_unrecoverable_after = 30 +start = time.time() + +while True: + try: + psycopg.connect( + dbname="${POSTGRES_DB}", + user="${POSTGRES_USER}", + password="${POSTGRES_PASSWORD}", + host="${POSTGRES_HOST}", + port="${POSTGRES_PORT}", + ) + break + except psycopg.OperationalError as error: + sys.stderr.write("Waiting for PostgreSQL to become available...\n") + + if time.time() - start > suggest_unrecoverable_after: + sys.stderr.write(" This is taking longer than expected. The following exception may be indicative of an unrecoverable error: '{}'\n".format(error)) + + time.sleep(1) +END + +>&2 echo 'PostgreSQL is available' + +exec "$@" diff --git a/containers/django/start b/containers/django/start new file mode 100644 index 0000000..22eb7e5 --- /dev/null +++ b/containers/django/start @@ -0,0 +1,29 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + + +python /app/manage.py collectstatic --noinput + +compress_enabled() { +python << END +import sys + +from environ import Env + +env = Env(COMPRESS_ENABLED=(bool, True)) +if env('COMPRESS_ENABLED'): + sys.exit(0) +else: + sys.exit(1) + +END +} + +if compress_enabled; then + # NOTE this command will fail if django-compressor is disabled + python /app/manage.py compress +fi +exec /usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker diff --git a/containers/nginx/Dockerfile b/containers/nginx/Dockerfile new file mode 100644 index 0000000..ec2ad35 --- /dev/null +++ b/containers/nginx/Dockerfile @@ -0,0 +1,2 @@ +FROM docker.io/nginx:1.17.8-alpine +COPY ./compose/production/nginx/default.conf /etc/nginx/conf.d/default.conf diff --git a/containers/nginx/default.conf b/containers/nginx/default.conf new file mode 100644 index 0000000..562dba8 --- /dev/null +++ b/containers/nginx/default.conf @@ -0,0 +1,7 @@ +server { + listen 80; + server_name localhost; + location /media/ { + alias /usr/share/nginx/media/; + } +} diff --git a/containers/postgres/Dockerfile b/containers/postgres/Dockerfile new file mode 100644 index 0000000..b7e3cd7 --- /dev/null +++ b/containers/postgres/Dockerfile @@ -0,0 +1,6 @@ +FROM docker.io/postgres:15 + +COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance +RUN chmod +x /usr/local/bin/maintenance/* +RUN mv /usr/local/bin/maintenance/* /usr/local/bin \ + && rmdir /usr/local/bin/maintenance diff --git a/containers/postgres/maintenance/_sourced/constants.sh b/containers/postgres/maintenance/_sourced/constants.sh new file mode 100644 index 0000000..6ca4f0c --- /dev/null +++ b/containers/postgres/maintenance/_sourced/constants.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + + +BACKUP_DIR_PATH='/backups' +BACKUP_FILE_PREFIX='backup' diff --git a/containers/postgres/maintenance/_sourced/countdown.sh b/containers/postgres/maintenance/_sourced/countdown.sh new file mode 100644 index 0000000..e6cbfb6 --- /dev/null +++ b/containers/postgres/maintenance/_sourced/countdown.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + + +countdown() { + declare desc="A simple countdown. Source: https://superuser.com/a/611582" + local seconds="${1}" + local d=$(($(date +%s) + "${seconds}")) + while [ "$d" -ge `date +%s` ]; do + echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r"; + sleep 0.1 + done +} diff --git a/containers/postgres/maintenance/_sourced/messages.sh b/containers/postgres/maintenance/_sourced/messages.sh new file mode 100644 index 0000000..f6be756 --- /dev/null +++ b/containers/postgres/maintenance/_sourced/messages.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + + +message_newline() { + echo +} + +message_debug() +{ + echo -e "DEBUG: ${@}" +} + +message_welcome() +{ + echo -e "\e[1m${@}\e[0m" +} + +message_warning() +{ + echo -e "\e[33mWARNING\e[0m: ${@}" +} + +message_error() +{ + echo -e "\e[31mERROR\e[0m: ${@}" +} + +message_info() +{ + echo -e "\e[37mINFO\e[0m: ${@}" +} + +message_suggestion() +{ + echo -e "\e[33mSUGGESTION\e[0m: ${@}" +} + +message_success() +{ + echo -e "\e[32mSUCCESS\e[0m: ${@}" +} diff --git a/containers/postgres/maintenance/_sourced/yes_no.sh b/containers/postgres/maintenance/_sourced/yes_no.sh new file mode 100644 index 0000000..fd9cae1 --- /dev/null +++ b/containers/postgres/maintenance/_sourced/yes_no.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + + +yes_no() { + declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message." + local arg1="${1}" + + local response= + read -r -p "${arg1} (y/[n])? " response + if [[ "${response}" =~ ^[Yy]$ ]] + then + exit 0 + else + exit 1 + fi +} diff --git a/containers/postgres/maintenance/backup b/containers/postgres/maintenance/backup new file mode 100644 index 0000000..f72304c --- /dev/null +++ b/containers/postgres/maintenance/backup @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + + +### Create a database backup. +### +### Usage: +### $ docker compose -f .yml (exec |run --rm) postgres backup + + +set -o errexit +set -o pipefail +set -o nounset + + +working_dir="$(dirname ${0})" +source "${working_dir}/_sourced/constants.sh" +source "${working_dir}/_sourced/messages.sh" + + +message_welcome "Backing up the '${POSTGRES_DB}' database..." + + +if [[ "${POSTGRES_USER}" == "postgres" ]]; then + message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." + exit 1 +fi + +export PGHOST="${POSTGRES_HOST}" +export PGPORT="${POSTGRES_PORT}" +export PGUSER="${POSTGRES_USER}" +export PGPASSWORD="${POSTGRES_PASSWORD}" +export PGDATABASE="${POSTGRES_DB}" + +backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz" +pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}" + + +message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'." diff --git a/containers/postgres/maintenance/backups b/containers/postgres/maintenance/backups new file mode 100644 index 0000000..a18937d --- /dev/null +++ b/containers/postgres/maintenance/backups @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + + +### View backups. +### +### Usage: +### $ docker compose -f .yml (exec |run --rm) postgres backups + + +set -o errexit +set -o pipefail +set -o nounset + + +working_dir="$(dirname ${0})" +source "${working_dir}/_sourced/constants.sh" +source "${working_dir}/_sourced/messages.sh" + + +message_welcome "These are the backups you have got:" + +ls -lht "${BACKUP_DIR_PATH}" diff --git a/containers/postgres/maintenance/restore b/containers/postgres/maintenance/restore new file mode 100644 index 0000000..c68f17d --- /dev/null +++ b/containers/postgres/maintenance/restore @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + + +### Restore database from a backup. +### +### Parameters: +### <1> filename of an existing backup. +### +### Usage: +### $ docker compose -f .yml (exec |run --rm) postgres restore <1> + + +set -o errexit +set -o pipefail +set -o nounset + + +working_dir="$(dirname ${0})" +source "${working_dir}/_sourced/constants.sh" +source "${working_dir}/_sourced/messages.sh" + + +if [[ -z ${1+x} ]]; then + message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again." + exit 1 +fi +backup_filename="${BACKUP_DIR_PATH}/${1}" +if [[ ! -f "${backup_filename}" ]]; then + message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again." + exit 1 +fi + +message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..." + +if [[ "${POSTGRES_USER}" == "postgres" ]]; then + message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." + exit 1 +fi + +export PGHOST="${POSTGRES_HOST}" +export PGPORT="${POSTGRES_PORT}" +export PGUSER="${POSTGRES_USER}" +export PGPASSWORD="${POSTGRES_PASSWORD}" +export PGDATABASE="${POSTGRES_DB}" + +message_info "Dropping the database..." +dropdb "${PGDATABASE}" + +message_info "Creating a new database..." +createdb --owner="${POSTGRES_USER}" + +message_info "Applying the backup to the new database..." +gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}" + +message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup." diff --git a/containers/postgres/maintenance/rmbackup b/containers/postgres/maintenance/rmbackup new file mode 100644 index 0000000..fdfd20e --- /dev/null +++ b/containers/postgres/maintenance/rmbackup @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +### Remove a database backup. +### +### Parameters: +### <1> filename of a backup to remove. +### +### Usage: +### $ docker-compose -f .yml (exec |run --rm) postgres rmbackup <1> + + +set -o errexit +set -o pipefail +set -o nounset + + +working_dir="$(dirname ${0})" +source "${working_dir}/_sourced/constants.sh" +source "${working_dir}/_sourced/messages.sh" + + +if [[ -z ${1+x} ]]; then + message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again." + exit 1 +fi +backup_filename="${BACKUP_DIR_PATH}/${1}" +if [[ ! -f "${backup_filename}" ]]; then + message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again." + exit 1 +fi + +message_welcome "Removing the '${backup_filename}' backup file..." + +rm -r "${backup_filename}" + +message_success "The '${backup_filename}' database backup has been removed." diff --git a/containers/traefik/Dockerfile b/containers/traefik/Dockerfile new file mode 100644 index 0000000..ea918e9 --- /dev/null +++ b/containers/traefik/Dockerfile @@ -0,0 +1,5 @@ +FROM docker.io/traefik:2.11.0 +RUN mkdir -p /etc/traefik/acme \ + && touch /etc/traefik/acme/acme.json \ + && chmod 600 /etc/traefik/acme/acme.json +COPY ./compose/production/traefik/traefik.yml /etc/traefik diff --git a/containers/traefik/traefik.yml b/containers/traefik/traefik.yml new file mode 100644 index 0000000..be6f742 --- /dev/null +++ b/containers/traefik/traefik.yml @@ -0,0 +1,73 @@ +log: + level: INFO + +entryPoints: + web: + # http + address: ":80" + http: + # https://doc.traefik.io/traefik/routing/entrypoints/#entrypoint + redirections: + entryPoint: + to: web-secure + + web-secure: + # https + address: ":443" + +certificatesResolvers: + letsencrypt: + # https://doc.traefik.io/traefik/https/acme/#lets-encrypt + acme: + email: "ivan.ogasawara@gmail.com" + storage: /etc/traefik/acme/acme.json + # https://doc.traefik.io/traefik/https/acme/#httpchallenge + httpChallenge: + entryPoint: web + +http: + routers: + web-secure-router: + rule: "Host(`https://opensciencelabs.github.io/feedback-linker`)" + entryPoints: + - web-secure + middlewares: + - csrf + service: django + tls: + # https://doc.traefik.io/traefik/routing/routers/#certresolver + certResolver: letsencrypt + + web-media-router: + rule: "Host(`https://opensciencelabs.github.io/feedback-linker`) && PathPrefix(`/media/`)" + entryPoints: + - web-secure + middlewares: + - csrf + service: django-media + tls: + certResolver: letsencrypt + + middlewares: + csrf: + # https://doc.traefik.io/traefik/master/middlewares/http/headers/#hostsproxyheaders + # https://docs.djangoproject.com/en/dev/ref/csrf/#ajax + headers: + hostsProxyHeaders: ["X-CSRFToken"] + + services: + django: + loadBalancer: + servers: + - url: http://django:5000 + + django-media: + loadBalancer: + servers: + - url: http://nginx:80 + +providers: + # https://doc.traefik.io/traefik/master/providers/file/ + file: + filename: /etc/traefik/traefik.yml + watch: true diff --git a/docs/mkdocs.yaml b/mkdocs.yaml similarity index 99% rename from docs/mkdocs.yaml rename to mkdocs.yaml index 1bfc531..251d896 100644 --- a/docs/mkdocs.yaml +++ b/mkdocs.yaml @@ -1,8 +1,8 @@ site_name: Feedback Linker site_url: https://opensciencelabs.github.io/feedback-linker repo_url: https://github.com/xmnlab/feedback-linker.git -docs_dir: ./ -site_dir: ../build +docs_dir: ./docs +site_dir: ./build # extra_css: # - stylesheets/extra.css # Page tree diff --git a/poetry.lock b/poetry.lock index 4e8bd0e..872cde1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,27 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + [[package]] name = "appnope" version = "0.1.4" @@ -11,6 +33,109 @@ files = [ {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, ] +[[package]] +name = "argon2-cffi" +version = "23.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "asgiref" +version = "3.7.2" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, + {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + [[package]] name = "attrs" version = "23.2.0" @@ -41,23 +166,9 @@ files = [ {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - [[package]] name = "bandit" version = "1.7.7" @@ -102,52 +213,6 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "black" -version = "24.2.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, - {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, - {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, - {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, - {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, - {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, - {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, - {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, - {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, - {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, - {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, - {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, - {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, - {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, - {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, - {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, - {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, - {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, - {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, - {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, - {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, - {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "bleach" version = "6.1.0" @@ -166,17 +231,6 @@ webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.3)"] -[[package]] -name = "blinker" -version = "1.7.0" -description = "Fast, simple object-to-object and broadcast signaling" -optional = false -python-versions = ">=3.8" -files = [ - {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, - {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, -] - [[package]] name = "certifi" version = "2024.2.2" @@ -503,6 +557,93 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "crispy-bootstrap5" +version = "2023.10" +description = "Bootstrap5 template pack for django-crispy-forms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "crispy-bootstrap5-2023.10.tar.gz", hash = "sha256:f16c44f1997310e5a89c0cf230402e7111cc1f942f64fb7e44603958b89b06a1"}, + {file = "crispy_bootstrap5-2023.10-py3-none-any.whl", hash = "sha256:9b5a6c9880f37cd32aa678c0d7576ae60b3e502c444c8712e582f8bd91659afb"}, +] + +[package.dependencies] +django = ">=4.2" +django-crispy-forms = ">=2" + +[package.extras] +test = ["pytest", "pytest-django"] + +[[package]] +name = "cryptography" +version = "42.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:de5086cd475d67113ccb6f9fae6d8fe3ac54a4f9238fd08bfdb07b03d791ff0a"}, + {file = "cryptography-42.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:935cca25d35dda9e7bd46a24831dfd255307c55a07ff38fd1a92119cffc34857"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20100c22b298c9eaebe4f0b9032ea97186ac2555f426c3e70670f2517989543b"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eb6368d5327d6455f20327fb6159b97538820355ec00f8cc9464d617caecead"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39d5c93e95bcbc4c06313fc6a500cee414ee39b616b55320c1904760ad686938"}, + {file = "cryptography-42.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d96ea47ce6d0055d5b97e761d37b4e84195485cb5a38401be341fabf23bc32a"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d1998e545081da0ab276bcb4b33cce85f775adb86a516e8f55b3dac87f469548"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93fbee08c48e63d5d1b39ab56fd3fdd02e6c2431c3da0f4edaf54954744c718f"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90147dad8c22d64b2ff7331f8d4cddfdc3ee93e4879796f837bdbb2a0b141e0c"}, + {file = "cryptography-42.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4dcab7c25e48fc09a73c3e463d09ac902a932a0f8d0c568238b3696d06bf377b"}, + {file = "cryptography-42.0.3-cp37-abi3-win32.whl", hash = "sha256:1e935c2900fb53d31f491c0de04f41110351377be19d83d908c1fd502ae8daa5"}, + {file = "cryptography-42.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:762f3771ae40e111d78d77cbe9c1035e886ac04a234d3ee0856bf4ecb3749d54"}, + {file = "cryptography-42.0.3-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3ec384058b642f7fb7e7bff9664030011ed1af8f852540c76a1317a9dd0d20"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35772a6cffd1f59b85cb670f12faba05513446f80352fe811689b4e439b5d89e"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04859aa7f12c2b5f7e22d25198ddd537391f1695df7057c8700f71f26f47a129"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3d1f5a1d403a8e640fa0887e9f7087331abb3f33b0f2207d2cc7f213e4a864c"}, + {file = "cryptography-42.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:df34312149b495d9d03492ce97471234fd9037aa5ba217c2a6ea890e9166f151"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:de4ae486041878dc46e571a4c70ba337ed5233a1344c14a0790c4c4be4bbb8b4"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0fab2a5c479b360e5e0ea9f654bcebb535e3aa1e493a715b13244f4e07ea8eec"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25b09b73db78facdfd7dd0fa77a3f19e94896197c86e9f6dc16bce7b37a96504"}, + {file = "cryptography-42.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d5cf11bc7f0b71fb71af26af396c83dfd3f6eed56d4b6ef95d57867bf1e4ba65"}, + {file = "cryptography-42.0.3-cp39-abi3-win32.whl", hash = "sha256:0fea01527d4fb22ffe38cd98951c9044400f6eff4788cf52ae116e27d30a1ba3"}, + {file = "cryptography-42.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:2619487f37da18d6826e27854a7f9d4d013c51eafb066c80d09c63cf24505306"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ead69ba488f806fe1b1b4050febafdbf206b81fa476126f3e16110c818bac396"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:20180da1b508f4aefc101cebc14c57043a02b355d1a652b6e8e537967f1e1b46"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5fbf0f3f0fac7c089308bd771d2c6c7b7d53ae909dce1db52d8e921f6c19bb3a"}, + {file = "cryptography-42.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c23f03cfd7d9826cdcbad7850de67e18b4654179e01fe9bc623d37c2638eb4ef"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db0480ffbfb1193ac4e1e88239f31314fe4c6cdcf9c0b8712b55414afbf80db4"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:6c25e1e9c2ce682d01fc5e2dde6598f7313027343bd14f4049b82ad0402e52cd"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9541c69c62d7446539f2c1c06d7046aef822940d248fa4b8962ff0302862cc1f"}, + {file = "cryptography-42.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b797099d221df7cce5ff2a1d272761d1554ddf9a987d3e11f6459b38cd300fd"}, + {file = "cryptography-42.0.3.tar.gz", hash = "sha256:069d2ce9be5526a44093a0991c450fe9906cdf069e0e7cd67d9dee49a62b9ebe"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cssbeautifier" +version = "1.15.1" +description = "CSS unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +jsbeautifier = "*" +six = ">=1.13.0" + [[package]] name = "debugpy" version = "1.8.1" @@ -568,140 +709,444 @@ files = [ ] [[package]] -name = "dnspython" -version = "2.6.1" -description = "DNS toolkit" +name = "django" +version = "5.0.2" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, + {file = "Django-5.0.2-py3-none-any.whl", hash = "sha256:56ab63a105e8bb06ee67381d7b65fe6774f057e41a8bab06c8020c8882d8ecd4"}, + {file = "Django-5.0.2.tar.gz", hash = "sha256:b5bb1d11b2518a5f91372a282f24662f58f66749666b0a286ab057029f728080"}, ] +[package.dependencies] +asgiref = ">=3.7.0,<4" +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] -trio = ["trio (>=0.23)"] -wmi = ["wmi (>=1.5.1)"] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] [[package]] -name = "email-validator" -version = "2.1.0.post1" -description = "A robust email address syntax and deliverability validation library." +name = "django-allauth" +version = "0.61.1" +description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "email_validator-2.1.0.post1-py3-none-any.whl", hash = "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637"}, - {file = "email_validator-2.1.0.post1.tar.gz", hash = "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44"}, + {file = "django-allauth-0.61.1.tar.gz", hash = "sha256:5b4ae515ea74f54f0041210692eee10c309ad15ddbbd03d3620693c75e3f7945"}, ] [package.dependencies] -dnspython = ">=2.0.0" -idna = ">=2.0.0" +Django = ">=3.2" +pyjwt = {version = ">=1.7", extras = ["crypto"]} +python3-openid = ">=3.0.8" +requests = ">=2.0.0" +requests-oauthlib = ">=0.3.0" + +[package.extras] +mfa = ["qrcode (>=7.0.0)"] +saml = ["python3-saml (>=1.15.0,<2.0.0)"] [[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" +name = "django-anymail" +version = "10.2" +description = "Django email backends and webhooks for Amazon SES, Brevo (Sendinblue), MailerSend, Mailgun, Mailjet, Mandrill, Postal, Postmark, Resend, SendGrid, and SparkPost" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "django_anymail-10.2-py3-none-any.whl", hash = "sha256:ca273abbc04b5ce06d1f3b3e5f28e2d79331519198fa6d6b5ba1b5aa5eea02f7"}, + {file = "django_anymail-10.2.tar.gz", hash = "sha256:1f3006d3b16874aaa32976078da6cdc9baa7f5274cf80e3fc51a288887617aaf"}, ] +[package.dependencies] +django = ">=2.0" +requests = ">=2.4.3" +urllib3 = ">=1.25.0" + [package.extras] -test = ["pytest (>=6)"] +amazon-ses = ["boto3"] +postal = ["cryptography"] +resend = ["svix"] [[package]] -name = "fastjsonschema" -version = "2.19.1" -description = "Fastest Python implementation of JSON schema" +name = "django-appconf" +version = "1.0.6" +description = "A helper class for handling configuration defaults of packaged apps gracefully." +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-appconf-1.0.6.tar.gz", hash = "sha256:cfe87ea827c4ee04b9a70fab90b86d704cb02f2981f89da8423cb0fabf88efbf"}, + {file = "django_appconf-1.0.6-py3-none-any.whl", hash = "sha256:c3ae442fba1ff7ec830412c5184b17169a7a1e71cf0864a4c3f93cf4c98a1993"}, +] + +[package.dependencies] +django = "*" + +[[package]] +name = "django-compressor" +version = "4.4" +description = "('Compresses linked and inline JavaScript or CSS into single cached files.',)" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, - {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, + {file = "django_compressor-4.4-py2.py3-none-any.whl", hash = "sha256:6e2b0c0becb9607f5099c2546a824c5b84a6918a34bc37a8a622ffa250313596"}, + {file = "django_compressor-4.4.tar.gz", hash = "sha256:1b0acc9cfba9f69bc38e7c41da9b0d70a20bc95587b643ffef9609cf46064f67"}, +] + +[package.dependencies] +django-appconf = ">=1.0.3" +rcssmin = "1.1.1" +rjsmin = "1.2.1" + +[[package]] +name = "django-cors-headers" +version = "4.3.1" +description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-cors-headers-4.3.1.tar.gz", hash = "sha256:0bf65ef45e606aff1994d35503e6b677c0b26cafff6506f8fd7187f3be840207"}, + {file = "django_cors_headers-4.3.1-py3-none-any.whl", hash = "sha256:0b1fd19297e37417fc9f835d39e45c8c642938ddba1acce0c1753d3edef04f36"}, +] + +[package.dependencies] +asgiref = ">=3.6" +Django = ">=3.2" + +[[package]] +name = "django-coverage-plugin" +version = "3.1.0" +description = "Django template coverage.py plugin" +optional = false +python-versions = "*" +files = [ + {file = "django_coverage_plugin-3.1.0-py3-none-any.whl", hash = "sha256:eb0ea8ffdb0db11a02994fc99be6500550efb496c350d709f418ff3d8e553a67"}, + {file = "django_coverage_plugin-3.1.0.tar.gz", hash = "sha256:223d34bf92bebadcb8b7b89932480e41c7bd98b44a8156934488fbe7f4a71f99"}, +] + +[package.dependencies] +coverage = "*" + +[[package]] +name = "django-crispy-forms" +version = "2.1" +description = "Best way to have Django DRY forms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-crispy-forms-2.1.tar.gz", hash = "sha256:4d7ec431933ad4d4b5c5a6de4a584d24613c347db9ac168723c9aaf63af4bb96"}, + {file = "django_crispy_forms-2.1-py3-none-any.whl", hash = "sha256:d592044771412ae1bd539cc377203aa61d4eebe77fcbc07fbc8f12d3746d4f6b"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "django-debug-toolbar" +version = "4.3.0" +description = "A configurable set of panels that display various debug information about the current request/response." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_debug_toolbar-4.3.0-py3-none-any.whl", hash = "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6"}, + {file = "django_debug_toolbar-4.3.0.tar.gz", hash = "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4"}, +] + +[package.dependencies] +django = ">=3.2.4" +sqlparse = ">=0.2" + +[[package]] +name = "django-environ" +version = "0.11.2" +description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "django-environ-0.11.2.tar.gz", hash = "sha256:f32a87aa0899894c27d4e1776fa6b477e8164ed7f6b3e410a62a6d72caaf64be"}, + {file = "django_environ-0.11.2-py2.py3-none-any.whl", hash = "sha256:0ff95ab4344bfeff693836aa978e6840abef2e2f1145adff7735892711590c05"}, ] [package.extras] -devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.dev0)", "pytest (>=4.6.11)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +docs = ["furo (>=2021.8.17b43,<2021.9.dev0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"] [[package]] -name = "filelock" -version = "3.13.1" -description = "A platform independent file lock." +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-model-utils" +version = "4.4.0" +description = "Django model mixins and utilities" optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "django-model-utils-4.4.0.tar.gz", hash = "sha256:7b73179480e4d4a737d0188e7c49da03776bbadedad569a534c4e9f1afc004d4"}, + {file = "django_model_utils-4.4.0-py3-none-any.whl", hash = "sha256:d57143e8b7345fd4719c5a95d07d7a50f7d11134da6a729aa6b73fb9674bec9d"}, ] +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-redis" +version = "5.4.0" +description = "Full featured redis cache backend for Django." +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-redis-5.4.0.tar.gz", hash = "sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42"}, + {file = "django_redis-5.4.0-py3-none-any.whl", hash = "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b"}, +] + +[package.dependencies] +Django = ">=3.2" +redis = ">=3,<4.0.0 || >4.0.0,<4.0.1 || >4.0.1" + [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] [[package]] -name = "flask" -version = "3.0.2" -description = "A simple framework for building complex web applications." +name = "django-stubs" +version = "4.2.7" +description = "Mypy stubs for Django" optional = false python-versions = ">=3.8" files = [ - {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, - {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, + {file = "django-stubs-4.2.7.tar.gz", hash = "sha256:8ccd2ff4ee5adf22b9e3b7b1a516d2e1c2191e9d94e672c35cc2bc3dd61e0f6b"}, + {file = "django_stubs-4.2.7-py3-none-any.whl", hash = "sha256:4cf4de258fa71adc6f2799e983091b9d46cfc67c6eebc68fe111218c9a62b3b8"}, ] [package.dependencies] -blinker = ">=1.6.2" -click = ">=8.1.3" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.1.2" -Jinja2 = ">=3.1.2" -Werkzeug = ">=3.0.0" +django = "*" +django-stubs-ext = ">=4.2.7" +mypy = {version = ">=1.7.0,<1.8.0", optional = true, markers = "extra == \"compatible-mypy\""} +tomli = {version = "*", markers = "python_version < \"3.11\""} +types-pytz = "*" +types-PyYAML = "*" +typing-extensions = "*" [package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] +compatible-mypy = ["mypy (>=1.7.0,<1.8.0)"] [[package]] -name = "flask-sqlalchemy" -version = "3.1.1" -description = "Add SQLAlchemy support to your Flask application." +name = "django-stubs-ext" +version = "4.2.7" +description = "Monkey-patching and extensions for django-stubs" optional = false python-versions = ">=3.8" files = [ - {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, - {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, + {file = "django-stubs-ext-4.2.7.tar.gz", hash = "sha256:519342ac0849cda1559746c9a563f03ff99f636b0ebe7c14b75e816a00dfddc3"}, + {file = "django_stubs_ext-4.2.7-py3-none-any.whl", hash = "sha256:45a5d102417a412e3606e3c358adb4744988a92b7b58ccf3fd64bddd5d04d14c"}, ] [package.dependencies] -flask = ">=2.2.5" -sqlalchemy = ">=2.0.16" +django = "*" +typing-extensions = "*" [[package]] -name = "flask-wtf" -version = "1.2.1" -description = "Form rendering, validation, and CSRF protection for Flask with WTForms." +name = "djangorestframework" +version = "3.14.0" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, + {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, +] + +[package.dependencies] +django = ">=3.0" +pytz = "*" + +[[package]] +name = "djangorestframework-stubs" +version = "3.14.5" +description = "PEP-484 stubs for django-rest-framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "djangorestframework-stubs-3.14.5.tar.gz", hash = "sha256:5dd6f638aa5291fb7863e6166128a6ed20bf4986e2fc5cf334e6afc841797a09"}, + {file = "djangorestframework_stubs-3.14.5-py3-none-any.whl", hash = "sha256:43d788fd50cda49b922cd411e59c5b8cdc3f3de49c02febae12ce42139f0269b"}, +] + +[package.dependencies] +django-stubs = [ + {version = ">=4.2.7"}, + {version = "*", extras = ["compatible-mypy"], optional = true, markers = "extra == \"compatible-mypy\""}, +] +mypy = {version = ">=1.7.0,<1.8.0", optional = true, markers = "extra == \"compatible-mypy\""} +requests = ">=2.0.0" +types-PyYAML = ">=5.4.3" +types-requests = ">=0.1.12" +typing-extensions = ">=3.10.0" + +[package.extras] +compatible-mypy = ["django-stubs[compatible-mypy]", "mypy (>=1.7.0,<1.8.0)"] +coreapi = ["coreapi (>=2.0.0)"] +markdown = ["types-Markdown (>=0.1.5)"] + +[[package]] +name = "djlint" +version = "1.34.1" +description = "HTML Template Linter and Formatter" +optional = false +python-versions = ">=3.8.0,<4.0.0" +files = [ + {file = "djlint-1.34.1-py3-none-any.whl", hash = "sha256:96ff1c464fb6f061130ebc88663a2ea524d7ec51f4b56221a2b3f0320a3cfce8"}, + {file = "djlint-1.34.1.tar.gz", hash = "sha256:db93fa008d19eaadb0454edf1704931d14469d48508daba2df9941111f408346"}, +] + +[package.dependencies] +click = ">=8.0.1,<9.0.0" +colorama = ">=0.4.4,<0.5.0" +cssbeautifier = ">=1.14.4,<2.0.0" +html-tag-names = ">=0.1.2,<0.2.0" +html-void-elements = ">=0.1.0,<0.2.0" +jsbeautifier = ">=1.14.4,<2.0.0" +json5 = ">=0.9.11,<0.10.0" +pathspec = ">=0.12.0,<0.13.0" +PyYAML = ">=6.0,<7.0" +regex = ">=2023.0.0,<2024.0.0" +tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} +tqdm = ">=4.62.2,<5.0.0" + +[[package]] +name = "drf-spectacular" +version = "0.27.1" +description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" +optional = false +python-versions = ">=3.6" +files = [ + {file = "drf-spectacular-0.27.1.tar.gz", hash = "sha256:452e0cff3c12ee057b897508a077562967b9e62717992eeec10e62dbbc7b5a33"}, + {file = "drf_spectacular-0.27.1-py3-none-any.whl", hash = "sha256:0a4cada4b7136a0bf17233476c066c511a048bc6a485ae2140326ac7ba4003b2"}, +] + +[package.dependencies] +Django = ">=2.2" +djangorestframework = ">=3.10.3" +inflection = ">=0.3.1" +jsonschema = ">=2.6.0" +PyYAML = ">=5.1" +uritemplate = ">=2.0.0" + +[package.extras] +offline = ["drf-spectacular-sidecar"] +sidecar = ["drf-spectacular-sidecar"] + +[[package]] +name = "editorconfig" +version = "0.12.4" +description = "EditorConfig File Locator and Interpreter for Python" +optional = false +python-versions = "*" +files = [ + {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "factory-boy" +version = "3.3.0" +description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." +optional = false +python-versions = ">=3.7" +files = [ + {file = "factory_boy-3.3.0-py2.py3-none-any.whl", hash = "sha256:a2cdbdb63228177aa4f1c52f4b6d83fab2b8623bf602c7dedd7eb83c0f69c04c"}, + {file = "factory_boy-3.3.0.tar.gz", hash = "sha256:bc76d97d1a65bbd9842a6d722882098eb549ec8ee1081f9fb2e8ff29f0c300f1"}, +] + +[package.dependencies] +Faker = ">=0.7.0" + +[package.extras] +dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "sqlalchemy-utils", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] + +[[package]] +name = "faker" +version = "23.2.1" +description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "flask_wtf-1.2.1-py3-none-any.whl", hash = "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287"}, - {file = "flask_wtf-1.2.1.tar.gz", hash = "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695"}, + {file = "Faker-23.2.1-py3-none-any.whl", hash = "sha256:0520a6b97e07c658b2798d7140971c1d5bc4bcd5013e7937fe075fd054aa320c"}, + {file = "Faker-23.2.1.tar.gz", hash = "sha256:f07b64d27f67b62c7f0536a72f47813015b3b51cd4664918454011094321e464"}, ] [package.dependencies] -flask = "*" -itsdangerous = "*" -wtforms = "*" +python-dateutil = ">=2.4" + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] [package.extras] -email = ["email-validator"] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "fuzzywuzzy" @@ -734,77 +1179,6 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - [[package]] name = "griffe" version = "0.40.1" @@ -819,6 +1193,225 @@ files = [ [package.dependencies] colorama = ">=0.4" +[[package]] +name = "gunicorn" +version = "21.2.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, + {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "hiredis" +version = "2.3.2" +description = "Python wrapper for hiredis" +optional = false +python-versions = ">=3.7" +files = [ + {file = "hiredis-2.3.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:742093f33d374098aa21c1696ac6e4874b52658c870513a297a89265a4d08fe5"}, + {file = "hiredis-2.3.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:9e14fb70ca4f7efa924f508975199353bf653f452e4ef0a1e47549e208f943d7"}, + {file = "hiredis-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d7302b4b17fcc1cc727ce84ded7f6be4655701e8d58744f73b09cb9ed2b13df"}, + {file = "hiredis-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed63e8b75c193c5e5a8288d9d7b011da076cc314fafc3bfd59ec1d8a750d48c8"}, + {file = "hiredis-2.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b4edee59dc089bc3948f4f6fba309f51aa2ccce63902364900aa0a553a85e97"}, + {file = "hiredis-2.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6481c3b7673a86276220140456c2a6fbfe8d1fb5c613b4728293c8634134824"}, + {file = "hiredis-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684840b014ce83541a087fcf2d48227196576f56ae3e944d4dfe14c0a3e0ccb7"}, + {file = "hiredis-2.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c4c0bcf786f0eac9593367b6279e9b89534e008edbf116dcd0de956524702c8"}, + {file = "hiredis-2.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66ab949424ac6504d823cba45c4c4854af5c59306a1531edb43b4dd22e17c102"}, + {file = "hiredis-2.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:322c668ee1c12d6c5750a4b1057e6b4feee2a75b3d25d630922a463cfe5e7478"}, + {file = "hiredis-2.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bfa73e3f163c6e8b2ec26f22285d717a5f77ab2120c97a2605d8f48b26950dac"}, + {file = "hiredis-2.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7f39f28ffc65de577c3bc0c7615f149e35bc927802a0f56e612db9b530f316f9"}, + {file = "hiredis-2.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:55ce31bf4711da879b96d511208efb65a6165da4ba91cb3a96d86d5a8d9d23e6"}, + {file = "hiredis-2.3.2-cp310-cp310-win32.whl", hash = "sha256:3dd63d0bbbe75797b743f35d37a4cca7ca7ba35423a0de742ae2985752f20c6d"}, + {file = "hiredis-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:ea002656a8d974daaf6089863ab0a306962c8b715db6b10879f98b781a2a5bf5"}, + {file = "hiredis-2.3.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:adfbf2e9c38b77d0db2fb32c3bdaea638fa76b4e75847283cd707521ad2475ef"}, + {file = "hiredis-2.3.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:80b02d27864ebaf9b153d4b99015342382eeaed651f5591ce6f07e840307c56d"}, + {file = "hiredis-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd40d2e2f82a483de0d0a6dfd8c3895a02e55e5c9949610ecbded18188fd0a56"}, + {file = "hiredis-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfa904045d7cebfb0f01dad51352551cce1d873d7c3f80c7ded7d42f8cac8f89"}, + {file = "hiredis-2.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28bd184b33e0dd6d65816c16521a4ba1ffbe9ff07d66873c42ea4049a62fed83"}, + {file = "hiredis-2.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f70481213373d44614148f0f2e38e7905be3f021902ae5167289413196de4ba4"}, + {file = "hiredis-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8797b528c1ff81eef06713623562b36db3dafa106b59f83a6468df788ff0d1"}, + {file = "hiredis-2.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02fc71c8333586871602db4774d3a3e403b4ccf6446dc4603ec12df563127cee"}, + {file = "hiredis-2.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0da56915bda1e0a49157191b54d3e27689b70960f0685fdd5c415dacdee2fbed"}, + {file = "hiredis-2.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e2674a5a3168349435b08fa0b82998ed2536eb9acccf7087efe26e4cd088a525"}, + {file = "hiredis-2.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:dc1c3fd49930494a67dcec37d0558d99d84eca8eb3f03b17198424538f2608d7"}, + {file = "hiredis-2.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:14c7b43205e515f538a9defb4e411e0f0576caaeeda76bb9993ed505486f7562"}, + {file = "hiredis-2.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bac7e02915b970c3723a7a7c5df4ba7a11a3426d2a3f181e041aa506a1ff028"}, + {file = "hiredis-2.3.2-cp311-cp311-win32.whl", hash = "sha256:63a090761ddc3c1f7db5e67aa4e247b4b3bb9890080bdcdadd1b5200b8b89ac4"}, + {file = "hiredis-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:70d226ab0306a5b8d408235cabe51d4bf3554c9e8a72d53ce0b3c5c84cf78881"}, + {file = "hiredis-2.3.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5c614552c6bd1d0d907f448f75550f6b24fb56cbfce80c094908b7990cad9702"}, + {file = "hiredis-2.3.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9c431431abf55b64347ddc8df68b3ef840269cb0aa5bc2d26ad9506eb4b1b866"}, + {file = "hiredis-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a45857e87e9d2b005e81ddac9d815a33efd26ec67032c366629f023fe64fb415"}, + {file = "hiredis-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e138d141ec5a6ec800b6d01ddc3e5561ce1c940215e0eb9960876bfde7186aae"}, + {file = "hiredis-2.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:387f655444d912a963ab68abf64bf6e178a13c8e4aa945cb27388fd01a02e6f1"}, + {file = "hiredis-2.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4852f4bf88f0e2d9bdf91279892f5740ed22ae368335a37a52b92a5c88691140"}, + {file = "hiredis-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d711c107e83117129b7f8bd08e9820c43ceec6204fff072a001fd82f6d13db9f"}, + {file = "hiredis-2.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92830c16885f29163e1c2da1f3c1edb226df1210ec7e8711aaabba3dd0d5470a"}, + {file = "hiredis-2.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:16b01d9ceae265d4ab9547be0cd628ecaff14b3360357a9d30c029e5ae8b7e7f"}, + {file = "hiredis-2.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5986fb5f380169270a0293bebebd95466a1c85010b4f1afc2727e4d17c452512"}, + {file = "hiredis-2.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:49532d7939cc51f8e99efc326090c54acf5437ed88b9c904cc8015b3c4eda9c9"}, + {file = "hiredis-2.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8f34801b251ca43ad70691fb08b606a2e55f06b9c9fb1fc18fd9402b19d70f7b"}, + {file = "hiredis-2.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7298562a49d95570ab1c7fc4051e72824c6a80e907993a21a41ba204223e7334"}, + {file = "hiredis-2.3.2-cp312-cp312-win32.whl", hash = "sha256:e1d86b75de787481b04d112067a4033e1ecfda2a060e50318a74e4e1c9b2948c"}, + {file = "hiredis-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:6dbfe1887ffa5cf3030451a56a8f965a9da2fa82b7149357752b67a335a05fc6"}, + {file = "hiredis-2.3.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:4fc242e9da4af48714199216eb535b61e8f8d66552c8819e33fc7806bd465a09"}, + {file = "hiredis-2.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e81aa4e9a1fcf604c8c4b51aa5d258e195a6ba81efe1da82dea3204443eba01c"}, + {file = "hiredis-2.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419780f8583ddb544ffa86f9d44a7fcc183cd826101af4e5ffe535b6765f5f6b"}, + {file = "hiredis-2.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6871306d8b98a15e53a5f289ec1106a3a1d43e7ab6f4d785f95fcef9a7bd9504"}, + {file = "hiredis-2.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb0b35b63717ef1e41d62f4f8717166f7c6245064957907cfe177cc144357c"}, + {file = "hiredis-2.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c490191fa1218851f8a80c5a21a05a6f680ac5aebc2e688b71cbfe592f8fec6"}, + {file = "hiredis-2.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4baf4b579b108062e91bd2a991dc98b9dc3dc06e6288db2d98895eea8acbac22"}, + {file = "hiredis-2.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e627d8ef5e100556e09fb44c9571a432b10e11596d3c4043500080ca9944a91a"}, + {file = "hiredis-2.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:ba3dc0af0def8c21ce7d903c59ea1e8ec4cb073f25ece9edaec7f92a286cd219"}, + {file = "hiredis-2.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:56e9b7d6051688ca94e68c0c8a54a243f8db841911b683cedf89a29d4de91509"}, + {file = "hiredis-2.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:380e029bb4b1d34cf560fcc8950bf6b57c2ef0c9c8b7c7ac20b7c524a730fadd"}, + {file = "hiredis-2.3.2-cp37-cp37m-win32.whl", hash = "sha256:948d9f2ca7841794dd9b204644963a4bcd69ced4e959b0d4ecf1b8ce994a6daa"}, + {file = "hiredis-2.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:cfa67afe2269b2d203cd1389c00c5bc35a287cd57860441fb0e53b371ea6a029"}, + {file = "hiredis-2.3.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bcbe47da0aebc00a7cfe3ebdcff0373b86ce2b1856251c003e3d69c9db44b5a7"}, + {file = "hiredis-2.3.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f2c9c0d910dd3f7df92f0638e7f65d8edd7f442203caf89c62fc79f11b0b73f8"}, + {file = "hiredis-2.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:01b6c24c0840ac7afafbc4db236fd55f56a9a0919a215c25a238f051781f4772"}, + {file = "hiredis-2.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1f567489f422d40c21e53212a73bef4638d9f21043848150f8544ef1f3a6ad1"}, + {file = "hiredis-2.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28adecb308293e705e44087a1c2d557a816f032430d8a2a9bb7873902a1c6d48"}, + {file = "hiredis-2.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27e9619847e9dc70b14b1ad2d0fb4889e7ca18996585c3463cff6c951fd6b10b"}, + {file = "hiredis-2.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a0026cfbf29f07649b0e34509091a2a6016ff8844b127de150efce1c3aff60b"}, + {file = "hiredis-2.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9de7586522e5da6bee83c9cf0dcccac0857a43249cb4d721a2e312d98a684d1"}, + {file = "hiredis-2.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e58494f282215fc461b06709e9a195a24c12ba09570f25bdf9efb036acc05101"}, + {file = "hiredis-2.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3a32b4b76d46f1eb42b24a918d51d8ca52411a381748196241d59a895f7c5c"}, + {file = "hiredis-2.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1979334ccab21a49c544cd1b8d784ffb2747f99a51cb0bd0976eebb517628382"}, + {file = "hiredis-2.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0c0773266e1c38a06e7593bd08870ac1503f5f0ce0f5c63f2b4134b090b5d6a4"}, + {file = "hiredis-2.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bd1cee053416183adcc8e6134704c46c60c3f66b8faaf9e65bf76191ca59a2f7"}, + {file = "hiredis-2.3.2-cp38-cp38-win32.whl", hash = "sha256:5341ce3d01ef3c7418a72e370bf028c7aeb16895e79e115fe4c954fff990489e"}, + {file = "hiredis-2.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:8fc7197ff33047ce43a67851ccf190acb5b05c52fd4a001bb55766358f04da68"}, + {file = "hiredis-2.3.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:f47775e27388b58ce52f4f972f80e45b13c65113e9e6b6bf60148f893871dc9b"}, + {file = "hiredis-2.3.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:9412a06b8a8e09abd6313d96864b6d7713c6003a365995a5c70cfb9209df1570"}, + {file = "hiredis-2.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3020b60e3fc96d08c2a9b011f1c2e2a6bdcc09cb55df93c509b88be5cb791df"}, + {file = "hiredis-2.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53d0f2c59bce399b8010a21bc779b4f8c32d0f582b2284ac8c98dc7578b27bc4"}, + {file = "hiredis-2.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57c0d0c7e308ed5280a4900d4468bbfec51f0e1b4cde1deae7d4e639bc6b7766"}, + {file = "hiredis-2.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d63318ca189fddc7e75f6a4af8eae9c0545863619fb38cfba5f43e81280b286"}, + {file = "hiredis-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e741ffe4e2db78a1b9dd6e5d29678ce37fbaaf65dfe132e5b82a794413302ef1"}, + {file = "hiredis-2.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb98038ccd368e0d88bd92ee575c58cfaf33e77f788c36b2a89a84ee1936dc6b"}, + {file = "hiredis-2.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:eae62ed60d53b3561148bcd8c2383e430af38c0deab9f2dd15f8874888ffd26f"}, + {file = "hiredis-2.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca33c175c1cf60222d9c6d01c38fc17ec3a484f32294af781de30226b003e00f"}, + {file = "hiredis-2.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c5f6972d2bdee3cd301d5c5438e31195cf1cabf6fd9274491674d4ceb46914d"}, + {file = "hiredis-2.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a6b54dabfaa5dbaa92f796f0c32819b4636e66aa8e9106c3d421624bd2a2d676"}, + {file = "hiredis-2.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e96cd35df012a17c87ae276196ea8f215e77d6eeca90709eb03999e2d5e3fd8a"}, + {file = "hiredis-2.3.2-cp39-cp39-win32.whl", hash = "sha256:63b99b5ea9fe4f21469fb06a16ca5244307678636f11917359e3223aaeca0b67"}, + {file = "hiredis-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a50c8af811b35b8a43b1590cf890b61ff2233225257a3cad32f43b3ec7ff1b9f"}, + {file = "hiredis-2.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e8bf4444b09419b77ce671088db9f875b26720b5872d97778e2545cd87dba4a"}, + {file = "hiredis-2.3.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bd42d0d45ea47a2f96babd82a659fbc60612ab9423a68e4a8191e538b85542a"}, + {file = "hiredis-2.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80441b55edbef868e2563842f5030982b04349408396e5ac2b32025fb06b5212"}, + {file = "hiredis-2.3.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec444ab8f27562a363672d6a7372bc0700a1bdc9764563c57c5f9efa0e592b5f"}, + {file = "hiredis-2.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f9f606e810858207d4b4287b4ef0dc622c2aa469548bf02b59dcc616f134f811"}, + {file = "hiredis-2.3.2-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c3dde4ca00fe9eee3b76209711f1941bb86db42b8a75d7f2249ff9dfc026ab0e"}, + {file = "hiredis-2.3.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4dd676107a1d3c724a56a9d9db38166ad4cf44f924ee701414751bd18a784a0"}, + {file = "hiredis-2.3.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce42649e2676ad783186264d5ffc788a7612ecd7f9effb62d51c30d413a3eefe"}, + {file = "hiredis-2.3.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e3f8b1733078ac663dad57e20060e16389a60ab542f18a97931f3a2a2dd64a4"}, + {file = "hiredis-2.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:532a84a82156a82529ec401d1c25d677c6543c791e54a263aa139541c363995f"}, + {file = "hiredis-2.3.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d59f88c4daa36b8c38e59ac7bffed6f5d7f68eaccad471484bf587b28ccc478"}, + {file = "hiredis-2.3.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91a14dd95e24dc078204b18b0199226ee44644974c645dc54ee7b00c3157330"}, + {file = "hiredis-2.3.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb777a38797c8c7df0444533119570be18d1a4ce5478dffc00c875684df7bfcb"}, + {file = "hiredis-2.3.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d47c915897a99d0d34a39fad4be97b4b709ab3d0d3b779ebccf2b6024a8c681e"}, + {file = "hiredis-2.3.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:333b5e04866758b11bda5f5315b4e671d15755fc6ed3b7969721bc6311d0ee36"}, + {file = "hiredis-2.3.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c8937f1100435698c18e4da086968c4b5d70e86ea718376f833475ab3277c9aa"}, + {file = "hiredis-2.3.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa45f7d771094b8145af10db74704ab0f698adb682fbf3721d8090f90e42cc49"}, + {file = "hiredis-2.3.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33d5ebc93c39aed4b5bc769f8ce0819bc50e74bb95d57a35f838f1c4378978e0"}, + {file = "hiredis-2.3.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a797d8c7df9944314d309b0d9e1b354e2fa4430a05bb7604da13b6ad291bf959"}, + {file = "hiredis-2.3.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e15a408f71a6c8c87b364f1f15a6cd9c1baca12bbc47a326ac8ab99ec7ad3c64"}, + {file = "hiredis-2.3.2.tar.gz", hash = "sha256:733e2456b68f3f126ddaf2cd500a33b25146c3676b97ea843665717bda0c5d43"}, +] + +[[package]] +name = "html-tag-names" +version = "0.1.2" +description = "List of known HTML tag names" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"}, + {file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"}, +] + +[[package]] +name = "html-void-elements" +version = "0.1.0" +description = "List of HTML void tag names." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"}, + {file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"}, +] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + [[package]] name = "identify" version = "2.5.35" @@ -845,42 +1438,16 @@ files = [ ] [[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "importlib-resources" -version = "6.1.1" -description = "Read resources from Python packages" +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.5" files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, + {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, + {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, ] -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -892,6 +1459,22 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "ipdb" +version = "0.13.13" +description = "IPython-enabled pdb" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, + {file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"}, +] + +[package.dependencies] +decorator = {version = "*", markers = "python_version > \"3.6\""} +ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} +tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} + [[package]] name = "ipykernel" version = "6.29.2" @@ -927,50 +1510,39 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "7.34.0" +version = "8.21.0" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" files = [ - {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, - {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, + {file = "ipython-8.21.0-py3-none-any.whl", hash = "sha256:1050a3ab8473488d7eee163796b02e511d0735cf43a04ba2a8348bd0f2eaf8a5"}, + {file = "ipython-8.21.0.tar.gz", hash = "sha256:48fbc236fbe0e138b88773fa0437751f14c3645fb483f1d4c5dee58b37e5ce73"}, ] [package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = "*" -setuptools = ">=18.5" -traitlets = ">=4.2" +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] -doc = ["Sphinx (>=1.3)"] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.23)", "pandas", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] - -[[package]] -name = "itsdangerous" -version = "2.1.2" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.7" -files = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, -] +test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath", "trio"] [[package]] name = "jedi" @@ -992,21 +1564,49 @@ qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] -name = "jinja2" -version = "3.1.3" -description = "A very fast and expressive template engine." +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsbeautifier" +version = "1.15.1" +description = "JavaScript unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +six = ">=1.13.0" + +[[package]] +name = "json5" +version = "0.9.14" +description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.7" +python-versions = "*" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "json5-0.9.14-py2.py3-none-any.whl", hash = "sha256:740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f"}, + {file = "json5-0.9.14.tar.gz", hash = "sha256:9ed66c3a6ca3510a976a9ef9b8c0787de24802724ab1860bc0153c7fdd589b02"}, ] -[package.dependencies] -MarkupSafe = ">=2.0" - [package.extras] -i18n = ["Babel (>=2.7)"] +dev = ["hypothesis"] [[package]] name = "jsonschema" @@ -1021,9 +1621,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} jsonschema-specifications = ">=2023.03.6" -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} referencing = ">=0.28.4" rpds-py = ">=0.7.1" @@ -1043,7 +1641,6 @@ files = [ ] [package.dependencies] -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" [[package]] @@ -1058,7 +1655,6 @@ files = [ ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" @@ -1285,9 +1881,6 @@ files = [ {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] @@ -1479,7 +2072,6 @@ files = [ click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} jinja2 = ">=2.11.1" markdown = ">=3.2.1" markupsafe = ">=2.0.1" @@ -1525,13 +2117,13 @@ mkdocs = "*" [[package]] name = "mkdocs-jupyter" -version = "0.24.3" +version = "0.24.6" description = "Use Jupyter in mkdocs websites" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "mkdocs_jupyter-0.24.3-py3-none-any.whl", hash = "sha256:904262a8678a5e5920b7c3c03b5010b36301a69d0a38f2fcbf430493adf6879e"}, - {file = "mkdocs_jupyter-0.24.3.tar.gz", hash = "sha256:3d81da9aea27480e93bab22438910c4f0b9630613e74f85b576590d78e0e8b14"}, + {file = "mkdocs_jupyter-0.24.6-py3-none-any.whl", hash = "sha256:56fb7ad796f2414a4143d54a966b805caf315c32413e97f85591623fa87dceca"}, + {file = "mkdocs_jupyter-0.24.6.tar.gz", hash = "sha256:89fcbe8a9523864d5416de1a60711640b6bc2972279d2adf46ed2776c2d9ff7c"}, ] [package.dependencies] @@ -1542,9 +2134,6 @@ mkdocs-material = ">9.0.0" nbconvert = ">=7.2.9,<8" pygments = ">2.12.0" -[package.extras] -test = ["coverage[toml]", "pymdown-extensions", "pytest", "pytest-cov"] - [[package]] name = "mkdocs-literate-nav" version = "0.6.1" @@ -1582,13 +2171,13 @@ test = ["mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material [[package]] name = "mkdocs-material" -version = "9.5.9" +version = "9.5.10" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.9-py3-none-any.whl", hash = "sha256:a5d62b73b3b74349e45472bfadc129c871dd2d4add68d84819580597b2f50d5d"}, - {file = "mkdocs_material-9.5.9.tar.gz", hash = "sha256:635df543c01c25c412d6c22991872267723737d5a2f062490f33b2da1c013c6d"}, + {file = "mkdocs_material-9.5.10-py3-none-any.whl", hash = "sha256:3c6c46b57d2ee3c8890e6e0406e68b6863cf65768f0f436990a742702d198442"}, + {file = "mkdocs_material-9.5.10.tar.gz", hash = "sha256:6ad626dbb31070ebbaedff813323a16a406629620e04b96458f16e6e9c7008fe"}, ] [package.dependencies] @@ -1633,7 +2222,6 @@ files = [ [package.dependencies] click = ">=7.0" -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} Jinja2 = ">=2.11.1" Markdown = ">=3.3" MarkupSafe = ">=1.1" @@ -1641,7 +2229,6 @@ mkdocs = ">=1.4" mkdocs-autorefs = ">=0.3.1" platformdirs = ">=2.2.0" pymdown-extensions = ">=6.3" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} [package.extras] crystal = ["mkdocstrings-crystal (>=0.3.4)"] @@ -1665,38 +2252,38 @@ mkdocstrings = ">=0.20" [[package]] name = "mypy" -version = "1.8.0" +version = "1.7.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, + {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, + {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, + {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, + {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, + {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, + {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, + {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, + {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, + {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, + {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, + {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, + {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, + {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, + {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, + {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, + {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, + {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, + {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, + {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, + {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, + {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, + {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, + {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, + {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, + {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, + {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, ] [package.dependencies] @@ -1745,20 +2332,19 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= [[package]] name = "nbconvert" -version = "7.16.0" -description = "Converting Jupyter Notebooks" +version = "7.16.1" +description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false python-versions = ">=3.8" files = [ - {file = "nbconvert-7.16.0-py3-none-any.whl", hash = "sha256:ad3dc865ea6e2768d31b7eb6c7ab3be014927216a5ece3ef276748dd809054c7"}, - {file = "nbconvert-7.16.0.tar.gz", hash = "sha256:813e6553796362489ae572e39ba1bff978536192fb518e10826b0e8cadf03ec8"}, + {file = "nbconvert-7.16.1-py3-none-any.whl", hash = "sha256:3188727dffadfdc9c6a1c7250729063d7bc78b355ad7aa023138afa030d1cd07"}, + {file = "nbconvert-7.16.1.tar.gz", hash = "sha256:e79e6a074f49ba3ed29428ed86487bf51509d9aab613bd8522ac08f6d28fd7fd"}, ] [package.dependencies] beautifulsoup4 = "*" bleach = "!=5.0.0" defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} jinja2 = ">=3.0" jupyter-core = ">=4.7" jupyterlab-pygments = "*" @@ -1827,6 +2413,22 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + [[package]] name = "packaging" version = "23.2" @@ -1911,26 +2513,89 @@ files = [ ptyprocess = ">=0.5" [[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" +name = "pillow" +version = "10.2.0" +description = "Python Imaging Library (Fork)" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, ] -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] [[package]] name = "platformdirs" @@ -1974,13 +2639,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.5.0" +version = "3.6.2" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, - {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, + {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, + {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, ] [package.dependencies] @@ -2032,6 +2697,104 @@ files = [ [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +[[package]] +name = "psycopg" +version = "3.1.18" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg-3.1.18-py3-none-any.whl", hash = "sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e"}, + {file = "psycopg-3.1.18.tar.gz", hash = "sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b"}, +] + +[package.dependencies] +psycopg-binary = {version = "3.1.18", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} +typing-extensions = ">=4.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.1.18)"] +c = ["psycopg-c (==3.1.18)"] +dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + +[[package]] +name = "psycopg-binary" +version = "3.1.18" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57"}, + {file = "psycopg_binary-3.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d"}, + {file = "psycopg_binary-3.1.18-cp311-cp311-win_amd64.whl", hash = "sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5"}, + {file = "psycopg_binary-3.1.18-cp312-cp312-win_amd64.whl", hash = "sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c76659ae29a84f2c14f56aad305dd00eb685bd88f8c0a3281a9a4bc6bd7d2aa7"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7afcd6f1d55992f26d9ff7b0bd4ee6b475eb43aa3f054d67d32e09f18b0065"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:639dd78ac09b144b0119076783cb64e1128cc8612243e9701d1503c816750b2e"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1cf59e0bb12e031a48bb628aae32df3d0c98fd6c759cb89f464b1047f0ca9c8"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e262398e5d51563093edf30612cd1e20fedd932ad0994697d7781ca4880cdc3d"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:59701118c7d8842e451f1e562d08e8708b3f5d14974eefbce9374badd723c4ae"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dea4a59da7850192fdead9da888e6b96166e90608cf39e17b503f45826b16f84"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4575da95fc441244a0e2ebaf33a2b2f74164603341d2046b5cde0a9aa86aa7e2"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:812726266ab96de681f2c7dbd6b734d327f493a78357fcc16b2ac86ff4f4e080"}, + {file = "psycopg_binary-3.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:3e7ce4d988112ca6c75765c7f24c83bdc476a6a5ce00878df6c140ca32c3e16d"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e"}, + {file = "psycopg_binary-3.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d"}, + {file = "psycopg_binary-3.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679"}, +] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -2043,6 +2806,20 @@ files = [ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "pycparser" version = "2.21" @@ -2069,6 +2846,26 @@ files = [ plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pymdown-extensions" version = "10.7" @@ -2127,6 +2924,43 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "pytest-django" +version = "4.8.0" +description = "A Django plugin for pytest." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"}, + {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +testing = ["Django", "django-configurations (>=2.0)"] + +[[package]] +name = "pytest-sugar" +version = "1.0.0" +description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +optional = false +python-versions = "*" +files = [ + {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, + {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, +] + +[package.dependencies] +packaging = ">=21.3" +pytest = ">=6.2.0" +termcolor = ">=2.1.0" + +[package.extras] +dev = ["black", "flake8", "pre-commit"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -2169,6 +3003,41 @@ files = [ [package.dependencies] Levenshtein = "0.25.0" +[[package]] +name = "python-slugify" +version = "8.0.4" +description = "A Python slugify application that also handles Unicode" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, + {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + +[[package]] +name = "python3-openid" +version = "3.2.0" +description = "OpenID support for modern servers and consumers." +optional = false +python-versions = "*" +files = [ + {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, + {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, +] + +[package.dependencies] +defusedxml = "*" + +[package.extras] +mysql = ["mysql-connector-python"] +postgresql = ["psycopg2"] + [[package]] name = "pytz" version = "2024.1" @@ -2484,6 +3353,56 @@ files = [ [package.extras] full = ["numpy"] +[[package]] +name = "rcssmin" +version = "1.1.1" +description = "CSS Minifier" +optional = false +python-versions = "*" +files = [ + {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1"}, + {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877"}, + {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f"}, + {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0afc6e7b64ef30d6dcde88830ec1a237b9f16a39f920a8fd159928684ccf8db"}, + {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:705c9112d0ed54ea40aecf97e7fd29bdf0f1c46d278a32d8f957f31dde90778a"}, + {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f7a1fcdbafaacac0530da04edca4a44303baab430ea42e7d59aece4b3f3e9a51"}, + {file = "rcssmin-1.1.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:cf74d7ea5e191f0f344b354eed8b7c83eeafbd9a97bec3a579c3d26edf11b005"}, + {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:908fe072efd2432fb0975a61124609a8e05021367f6a3463d45f5e3e74c4fdda"}, + {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:35da6a6999e9e2c5b0e691b42ed56cc479373e0ecab33ef5277dfecce625e44a"}, + {file = "rcssmin-1.1.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e923c105100ab70abde1c01d3196ddd6b07255e32073685542be4e3a60870c8e"}, + {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:868215e1fd0e92a6122e0ed5973dfc7bb8330fe1e92274d05b2585253b38c0ca"}, + {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c7728e3b546b1b6ea08cab721e8e21409dbcc11b881d0b87d10b0be8930af2a2"}, + {file = "rcssmin-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:271e3d2f8614a6d4637ed8fff3d90007f03e2a654cd9444f37d888797662ba72"}, + {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:42576d95dfad53d77df2e68dfdec95b89b10fad320f241f1af3ca1438578254a"}, + {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:79421230dd67c37ec61ed9892813d2b839b68f2f48ef55c75f976e81701d60b4"}, + {file = "rcssmin-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8fcfd10ae2a1c4ce231a33013f2539e07c3836bf17cc945cc25cc30bf8e68e45"}, + {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c30f8bc839747b6da59274e0c6e4361915d66532e26448d589cb2b1846d7bf11"}, + {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ee386bec6d62f8c814d65c011d604a7c82d24aa3f718facd66e850eea8d6a5a1"}, + {file = "rcssmin-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8a26fec3c1e6b7a3765ccbaccc20fbb5c0ed3422cc381e01a2607f08d7621c44"}, + {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a04d58a2a21e9a089306d3f99c4b12bf5b656a79c198ef2321e80f8fd9afab06"}, + {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:914e589f40573035006913861ed2adc28fbe70082a8b6bff5be7ee430b7b5c2e"}, + {file = "rcssmin-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019"}, + {file = "rcssmin-1.1.1.tar.gz", hash = "sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64"}, +] + +[[package]] +name = "redis" +version = "5.0.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, + {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + [[package]] name = "referencing" version = "0.33.0" @@ -2622,6 +3541,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + [[package]] name = "rich" version = "13.7.0" @@ -2636,11 +3573,42 @@ files = [ [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rjsmin" +version = "1.2.1" +description = "Javascript Minifier" +optional = false +python-versions = "*" +files = [ + {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1"}, + {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064"}, + {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346"}, + {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bd1faedc425006d9e86b23837d164f01d105b7a8b66b767a9766d0014773db2a"}, + {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:99c074cd6a8302ff47118a9c3d086f89328dc8e5c4b105aa1f348fb85c765a30"}, + {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:bc5bc2f94e59bc81562c572b7f1bdd6bcec4f61168dc68a2993bad2d355b6e19"}, + {file = "rjsmin-1.2.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:35f21046504544e2941e04190ce24161255479133751550e36ddb3f4af0ecdca"}, + {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:ca90630b84fe94bb07739c3e3793e87d30c6ee450dde08653121f0d9153c8d0d"}, + {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7dd58b5ed88233bc61dc80b0ed87b93a1786031d9977c70d335221ef1ac5581a"}, + {file = "rjsmin-1.2.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:f0895b360dccf7e2d6af8762a52985e3fbaa56778de1bf6b20dbc96134253807"}, + {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:747bc9d3bc8a220f40858e6aad50b2ae2eb7f69c924d4fa3803b81be1c1ddd02"}, + {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f7cd33602ec0f393a0058e883284496bb4dbbdd34e0bbe23b594c8933ddf9b65"}, + {file = "rjsmin-1.2.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3453ee6d5e7a2723ec45c2909e2382371783400e8d51952b692884c6d850a3d0"}, + {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8c340e251619c97571a5ade20f147f1f7e8664f66a2d6d7319e05e3ef6a4423c"}, + {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:145c6af8df42d8af102d0d39a6de2e5fa66aef9e38947cfb9d65377d1b9940b2"}, + {file = "rjsmin-1.2.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bbd7a0abaa394afd951f5d4e05249d306fec1c9674bfee179787674dddd0bdb7"}, + {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:eb770aaf637919b0011c4eb87b9ac6317079fb9800eb17c90dda05fc9de4ebc3"}, + {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5d67ec09da46a492186e35cabca02a0d092eda5ef5b408a419b99ee4acf28d5c"}, + {file = "rjsmin-1.2.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d332e44a1b21ad63401cc7eebc81157e3d982d5fb503bb4faaea5028068d71e9"}, + {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:113132a40ce7d03b2ced4fac215f0297338ed1c207394b739266efab7831988b"}, + {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:122aa52bcf7ad9f12728d309012d1308c6ecfe4d6b09ea867a110dcad7b7728c"}, + {file = "rjsmin-1.2.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39"}, + {file = "rjsmin-1.2.1.tar.gz", hash = "sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411"}, +] + [[package]] name = "rpds-py" version = "0.18.0" @@ -2775,6 +3743,51 @@ files = [ {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, ] +[[package]] +name = "sentry-sdk" +version = "1.40.5" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.40.5.tar.gz", hash = "sha256:d2dca2392cc5c9a2cc9bb874dd7978ebb759682fe4fe889ee7e970ee8dd1c61e"}, + {file = "sentry_sdk-1.40.5-py2.py3-none-any.whl", hash = "sha256:d188b407c9bacbe2a50a824e1f8fb99ee1aeb309133310488c570cb6d7056643"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +loguru = ["loguru (>=0.5)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + [[package]] name = "setuptools" version = "69.1.0" @@ -2824,6 +3837,17 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + [[package]] name = "soupsieve" version = "2.5" @@ -2836,106 +3860,39 @@ files = [ ] [[package]] -name = "sqlalchemy" -version = "2.0.27" -description = "Database Abstraction Library" +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." optional = false -python-versions = ">=3.7" +python-versions = ">=3.5" files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - -[[package]] -name = "sqlalchemy-stubs" -version = "0.4" -description = "SQLAlchemy stubs and mypy plugin" + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" files = [ - {file = "sqlalchemy-stubs-0.4.tar.gz", hash = "sha256:c665d6dd4482ef642f01027fa06c3d5e91befabb219dc71fc2a09e7d7695f7ae"}, - {file = "sqlalchemy_stubs-0.4-py3-none-any.whl", hash = "sha256:5eec7aa110adf9b957b631799a72fef396b23ff99fe296df726645d01e312aa5"}, + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, ] [package.dependencies] -mypy = ">=0.790" -typing-extensions = ">=3.7.4" +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "stevedore" @@ -2965,15 +3922,26 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + [[package]] name = "textual" -version = "0.51.0" +version = "0.52.0" description = "Modern Text User Interface framework" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "textual-0.51.0-py3-none-any.whl", hash = "sha256:c25c8d5f462ca169fa50add10f4d3604d98409b6a9f8dadff6a269cc7027516c"}, - {file = "textual-0.51.0.tar.gz", hash = "sha256:ca3d58c00a360ef1988a9be2dbb34d8a8526f2b9fe40c2ed7ac6687875422efd"}, + {file = "textual-0.52.0-py3-none-any.whl", hash = "sha256:cc9cef70e0dc26da1003651c7d3193c7dd54fcf55640d8d62c5f487ada9bca41"}, + {file = "textual-0.52.0.tar.gz", hash = "sha256:d7e189e0ac8efe982cc47e51c196848720b72a19f683870b7709389015989afb"}, ] [package.dependencies] @@ -3044,6 +4012,26 @@ files = [ {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, ] +[[package]] +name = "tqdm" +version = "4.66.2" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "traitlets" version = "5.14.1" @@ -3081,18 +4069,40 @@ doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1 test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] [[package]] -name = "types-wtforms" -version = "3.1.0.20240205" -description = "Typing stubs for WTForms" +name = "types-pytz" +version = "2024.1.0.20240203" +description = "Typing stubs for pytz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-pytz-2024.1.0.20240203.tar.gz", hash = "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e"}, + {file = "types_pytz-2024.1.0.20240203-py3-none-any.whl", hash = "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.12" +description = "Typing stubs for PyYAML" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"}, + {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"}, +] + +[[package]] +name = "types-requests" +version = "2.31.0.20240218" +description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-WTForms-3.1.0.20240205.tar.gz", hash = "sha256:96b132466ecb5d04660678c2bef74d7fe0ff69dd9496c3cd23dbb27c0373ab5c"}, - {file = "types_WTForms-3.1.0.20240205-py3-none-any.whl", hash = "sha256:3ab23f561d55b0cc3e499a333a86241c42a0fbe5a631f108a28ee3134ccd795d"}, + {file = "types-requests-2.31.0.20240218.tar.gz", hash = "sha256:f1721dba8385958f504a5386240b92de4734e047a08a40751c1654d1ac3349c5"}, + {file = "types_requests-2.31.0.20240218-py3-none-any.whl", hash = "sha256:a82807ec6ddce8f00fe0e949da6d6bc1fbf1715420218a9640d695f70a9e5a9b"}, ] [package.dependencies] -MarkupSafe = "*" +urllib3 = ">=2" [[package]] name = "typing-extensions" @@ -3105,6 +4115,17 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + [[package]] name = "uc-micro-py" version = "1.0.3" @@ -3119,6 +4140,17 @@ files = [ [package.extras] test = ["coverage", "pytest", "pytest-cov"] +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +optional = false +python-versions = ">=3.6" +files = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] + [[package]] name = "urllib3" version = "2.2.1" @@ -3136,6 +4168,76 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "uvicorn" +version = "0.27.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, + {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "virtualenv" version = "20.25.0" @@ -3211,6 +4313,93 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + [[package]] name = "wcwidth" version = "0.2.13" @@ -3233,6 +4422,87 @@ files = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + [[package]] name = "werkzeug" version = "3.0.1" @@ -3246,47 +4516,43 @@ files = [ [package.dependencies] MarkupSafe = ">=2.1.1" +watchdog = {version = ">=2.3", optional = true, markers = "extra == \"watchdog\""} [package.extras] watchdog = ["watchdog (>=2.3)"] [[package]] -name = "wtforms" -version = "2.3.3" -description = "A flexible forms validation and rendering library for Python web development." +name = "whitenoise" +version = "6.6.0" +description = "Radically simplified static file serving for WSGI applications" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "WTForms-2.3.3-py2.py3-none-any.whl", hash = "sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c"}, - {file = "WTForms-2.3.3.tar.gz", hash = "sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"}, + {file = "whitenoise-6.6.0-py3-none-any.whl", hash = "sha256:b1f9db9bf67dc183484d760b99f4080185633136a273a03f6436034a41064146"}, + {file = "whitenoise-6.6.0.tar.gz", hash = "sha256:8998f7370973447fac1e8ef6e8ded2c5209a7b1f67c1012866dbcd09681c3251"}, ] -[package.dependencies] -MarkupSafe = "*" - [package.extras] -email = ["email-validator"] -ipaddress = ["ipaddress"] -locale = ["Babel (>=1.3)"] +brotli = ["Brotli"] [[package]] name = "xonsh" -version = "0.14.0" +version = "0.14.4" description = "Python-powered, cross-platform, Unix-gazing shell" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "xonsh-0.14.0-py310-none-any.whl", hash = "sha256:72506c6dc494103df6d04467e127abddb1c6cbe05cc5903b6a4cbfbad217ff5d"}, - {file = "xonsh-0.14.0-py311-none-any.whl", hash = "sha256:678a65671bd0a62cdc43e932d6aecc8b1622aa18942e0afb388b8b8ae02f67a5"}, - {file = "xonsh-0.14.0-py38-none-any.whl", hash = "sha256:25976edc5695fb5806b8b23f384ff48e618e07f4596ec0806007f63122917a83"}, - {file = "xonsh-0.14.0-py39-none-any.whl", hash = "sha256:751b615726d2322d43c8166ad4bc5cbe65d03a3728f1837aa02380fa9fadb189"}, - {file = "xonsh-0.14.0.tar.gz", hash = "sha256:45a8aaabb17ce0d6d4eca9b709ecfd7ce1c8fb92162decd29a45bf88a60e9bf1"}, + {file = "xonsh-0.14.4-py310-none-any.whl", hash = "sha256:2627524483a2d251de2325366453e183f016e164cb62475f8291a8ebd3e8bdc0"}, + {file = "xonsh-0.14.4-py311-none-any.whl", hash = "sha256:8423fe0a2a5e91e4fa316eff8f445cfa12f61f2437b84fd06aef97bdfd306ffe"}, + {file = "xonsh-0.14.4-py312-none-any.whl", hash = "sha256:16f16147fbbd3110d3cda5e6738010bb16221bfd8c41f9f04eae1bde0b90f467"}, + {file = "xonsh-0.14.4-py39-none-any.whl", hash = "sha256:e85f5e21b72e807d9e77c341ef9e4e964b52c923498628d840f2a21c8820a357"}, + {file = "xonsh-0.14.4.tar.gz", hash = "sha256:7a20607f0914c9876f3500f0badc0414aa1b8640c85001ba3b9b3cfd6d890b39"}, ] [package.extras] bestshell = ["prompt-toolkit (>=3.0.29)", "pygments (>=2.2)"] dev = ["pre-commit", "re-ver", "tomli", "xonsh[doc,test]"] -doc = ["doctr", "furo", "livereload", "matplotlib", "myst-parser", "numpydoc", "psutil", "pyzmq", "runthis-sphinxext", "sphinx (>=3.1,<5)", "tornado", "xonsh[bestshell]"] +doc = ["doctr", "furo", "livereload", "matplotlib", "myst-parser", "numpydoc", "psutil", "pyzmq", "runthis-sphinxext", "sphinx (>=3.1)", "tornado", "xonsh[bestshell]"] full = ["distro", "gnureadline", "setproctitle", "ujson", "xonsh[ptk,pygments]"] linux = ["distro"] mac = ["gnureadline"] @@ -3295,22 +4561,7 @@ ptk = ["prompt-toolkit (>=3.0.29)", "pyperclip"] pygments = ["pygments (>=2.2)"] test = ["coverage (>=5.3.1)", "prompt-toolkit (>=3.0.29)", "pygments (>=2.2)", "pyte (>=0.8.0)", "pytest (>=7)", "pytest-cov", "pytest-mock", "pytest-rerunfailures", "pytest-subprocess", "pytest-timeout", "restructuredtext-lint", "virtualenv (>=20.16.2)", "xonsh[bestshell]"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = ">=3.8.1,<4" -content-hash = "e93a67349b26077f6a64533c1fc3681017ee89c0a20ddf4989871a286165f3da" +python-versions = ">=3.10,<4" +content-hash = "591ae737f1ada112a23e0c6ed8b87e766d54dca113d81e56fa12740affdaa363" diff --git a/pyproject.toml b/pyproject.toml index b0bebf5..519f054 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,6 @@ name = "feedback-linker" version = "0.1.0" # semantic-release description = "The main objective of this platform is to offer a space to handle and organize feedback between two people" authors = ["Ivan Ogasawara "] -packages = [ - {include = "feedback_linker", from="src"}, -] license = "BSD 3 Clause" exclude = [ ".git/*", @@ -14,28 +11,43 @@ exclude = [ include = ["src/feedback_linker/py.typed"] [tool.poetry.dependencies] -python = ">=3.8.1,<4" -flask = ">=3.0.2" -flask-wtf = ">=1.2.1" -wtforms = "<3" -email-validator = ">=2.1.0.post1" -sqlalchemy = ">=2.0.27" -flask-sqlalchemy = ">=3.1.1" +python = ">=3.10,<4" +python-slugify = ">=8.0.4" # https://github.com/un33k/python-slugify +Pillow = ">=10.2.0" # https://github.com/python-pillow/Pillow +rcssmin = ">=1.1.1" # https://github.com/ndparker/rcssmin +argon2-cffi = ">=23.1.0" # https://github.com/hynek/argon2_cffi +whitenoise = ">=6.6.0" # https://github.com/evansd/whitenoise +redis = ">=5.0.1" # https://github.com/redis/redis-py +hiredis = ">=2.3.2" # https://github.com/redis/hiredis-py +uvicorn = {version = ">=0.27.1", extras = ["standard"] } # https://github.com/encode/uvicorn +django = ">=5" # https://www.djangoproject.com/ +django-environ = ">=0.11.2" # https://github.com/joke2k/django-environ +django-model-utils = ">=4.4.0" # https://github.com/jazzband/django-model-utils +django-allauth = ">=0.61.1" # https://github.com/pennersr/django-allauth +django-crispy-forms = ">=2.1" # https://github.com/django-crispy-forms/django-crispy-forms +django-anymail = ">=10.2" # https://github.com/anymail/django-anymail +crispy-bootstrap5 = ">=2023.10" # https://github.com/django-crispy-forms/crispy-bootstrap5 +django-compressor = ">=4.4" # https://github.com/django-compressor/django-compressor +django-redis = ">=5.4.0" # https://github.com/jazzband/django-redis +djangorestframework = ">=3.14.0" # https://github.com/encode/django-rest-framework +django-cors-headers = ">=4.3.1" # https://github.com/adamchainz/django-cors-headers +drf-spectacular = ">=0.27.1" # https://github.com/tfranzel/drf-spectacular +gunicorn = ">=21.2.0" # https://github.com/benoitc/gunicorn +psycopg = {version = ">=3.1.18", extras = ["binary"]} # https://github.com/psycopg/psycopg +sentry-sdk = ">=1.40.4" # https://github.com/getsentry/sentry-python [tool.poetry.dev-dependencies] -pytest = ">=7.3.2" +pytest = ">=8.0" pytest-cov = ">=4.1.0" -coverage = ">=7.2.7" -black = ">=23.3.0" pre-commit = ">=3.3.2" ruff = ">=0.2.2" -mypy = ">=1.8.0" +mypy = ">=1.7.0,<1.8" bandit = ">=1.7.5" vulture = ">=2.7" mccabe = ">=0.6.1" compose-go = ">=2.18.1" -ipython = "<8" +ipython = ">=8" ipykernel = ">=6.0.0" Jinja2 = ">=3.1.2" mkdocs = ">=1.4.3" @@ -48,13 +60,20 @@ mkdocstrings = ">=0.21.2" mkdocstrings-python = ">=1.1.2" makim = "1.13.0" containers-sugar = "1.11.1" -types-wtforms = ">=3.1.0.20240205" -sqlalchemy-stubs = ">=0.4" +Werkzeug = {version = ">=3.0.1", extras = ["watchdog"] } # https://github.com/pallets/werkzeug +ipdb = ">=0.13.13" # https://github.com/gotcha/ipdb +watchfiles = ">=0.21.0" # https://github.com/samuelcolvin/watchfiles +django-stubs = {version = ">=4.2.7", extras = ["compatible-mypy"]} # https://github.com/typeddjango/django-stubs +pytest-sugar = ">=1.0.0" # https://github.com/Frozenball/pytest-sugar +djangorestframework-stubs = {version = ">=3.14.5", extras = ["compatible-mypy"]} # https://github.com/typeddjango/djangorestframework-stubs +coverage = ">=7.4.1" # https://github.com/nedbat/coveragepy +djlint = ">=1.34.1" # https://github.com/Riverside-Healthcare/djLint +factory-boy = ">=3.3.0" # https://github.com/FactoryBoy/factory_boy +django-debug-toolbar = ">=4.3.0" # https://github.com/jazzband/django-debug-toolbar +django-extensions = ">=3.2.3" # https://github.com/django-extensions/django-extensions +django-coverage-plugin = ">=3.1.0" # https://github.com/nedbat/django_coverage_plugin +pytest-django = ">=4.8.0" # https://github.com/pytest-dev/pytest-django -[tool.pytest.ini_options] -testpaths = [ - "tests", -] [tool.bandit] exclude_dirs = ["tests"] @@ -70,26 +89,190 @@ paths = ["./"] sort_by_size = true verbose = false +# ==== pytest ==== +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--ds=config.settings.test --reuse-db" +python_files = [ + "tests.py", + "test_*.py", +] + +# ==== Coverage ==== +[tool.coverage.run] +include = ["src/feedback_linker/**"] +omit = ["*/migrations/*", "*/tests/*"] +plugins = ["django_coverage_plugin"] + + +# ==== mypy ==== +[tool.mypy] +python_version = "3.11" +strict = true +check_untyped_defs = true +ignore_missing_imports = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true +files = ["./src/feedback_linker"] +plugins = [ + "mypy_django_plugin.main", + "mypy_drf_plugin.main", +] + +[[tool.mypy.overrides]] +# Django migrations should not produce any errors: +module = "*.migrations.*" +ignore_errors = true + +[tool.django-stubs] +django_settings_module = "config.settings.test" +strict_settings = false +ignore_missing_model_attributes = true + +# [[tool.mypy.overrides]] +# module = "feedback_linker.forms" +# ignore_errors = true + +[[tool.mypy.overrides]] +module = "NewSemanalDjangoPlugin" +ignore_errors = true + +# ==== djLint ==== +[tool.djlint] +blank_line_after_tag = "load,extends" +close_void_tags = true +format_css = true +format_js = true +# TODO: remove T002 when fixed https://github.com/Riverside-Healthcare/djLint/issues/687 +ignore = "H006,H030,H031,T002" +include = "H017,H035" +indent = 2 +max_line_length = 119 +profile = "django" + +[tool.djlint.css] +indent_size = 2 + +[tool.djlint.js] +indent_size = 2 + + [tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "*/migrations/*.py", + "staticfiles/*", + 'docs', +] +# Same as Django: https://github.com/cookiecutter/cookiecutter-django/issues/4792. line-length = 79 +indent-width = 4 +target-version = "py38" force-exclude = true src = ["./"] -exclude = [ - 'docs', -] fix = true + [tool.ruff.lint] -ignore = ["PLR0913"] select = [ - "E", # pycodestyle - "F", # pyflakes - "D", # pydocstyle - "YTT", # flake8-2020 - "PL", # PL - "RUF", # Ruff-specific rules + # "D", # pydocstyle + "F", + "E", + "W", + "C90", + "I", "I001", # isort + "N", + "UP", + "YTT", + # "ANN", # flake8-annotations: we should support this in the future but 100+ errors atm + "ASYNC", + "S", + "BLE", + # "FBT", + "B", + "A", + "COM", + "C4", + "DTZ", + "T10", + "DJ", + "EM", + "EXE", + "FA", + 'ISC', + "ICN", + "G", + 'INP', + 'PIE', + "T20", + 'PYI', + 'PT', + "Q", + "RSE", + "RET", + "SLF", + "SLOT", + "SIM", + "TID", + "TCH", + "INT", + # "ARG", # Unused function argument + "PTH", + "ERA", + "PD", + # "PGH", + "PL", + "TRY", + "FLY", + # "NPY", + # "AIR", + "PERF", + # "FURB", + # "LOG", + "RUF" +] +ignore = [ + "S101", # Use of assert detected https://docs.astral.sh/ruff/rules/assert/ + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "PLR0913", + "COM812", + "ISC001", ] +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + + +[tool.ruff.format] +quote-style = "single" +indent-style = "space" +# skip-magic-trailing-comma = false +line-ending = "auto" [tool.ruff.lint.pydocstyle] convention = "numpy" @@ -98,30 +281,7 @@ convention = "numpy" # Use a single line between direct and from import lines-between-types = 1 -[tool.ruff.format] -quote-style = "single" - -[tool.mypy] -python_version = "3.8" -check_untyped_defs = true -strict = true -ignore_missing_imports = true -warn_unused_ignores = true -warn_redundant_casts = true -warn_unused_configs = true - -# [[tool.mypy.overrides]] -# module = [ -# "wtforms", -# ] -# ignore_missing_imports = true - - -[[tool.mypy.overrides]] -module = "feedback_linker.models" -ignore_errors = true - - -[[tool.mypy.overrides]] -module = "feedback_linker.forms" -ignore_errors = true +[tool.ruff.lint.flake8-quotes] +# docstring-quotes = "double" +inline-quotes = "single" +# multiline-quotes = "double" diff --git a/src/feedback_linker/py.typed b/src/config/__init__.py similarity index 100% rename from src/feedback_linker/py.typed rename to src/config/__init__.py diff --git a/src/config/api_router.py b/src/config/api_router.py new file mode 100644 index 0000000..857a34d --- /dev/null +++ b/src/config/api_router.py @@ -0,0 +1,11 @@ +from django.conf import settings +from feedback_linker.users.api.views import UserViewSet +from rest_framework.routers import DefaultRouter, SimpleRouter + +router = DefaultRouter() if settings.DEBUG else SimpleRouter() + +router.register('users', UserViewSet) + + +app_name = 'api' +urlpatterns = router.urls diff --git a/src/config/asgi.py b/src/config/asgi.py new file mode 100644 index 0000000..d1e89db --- /dev/null +++ b/src/config/asgi.py @@ -0,0 +1,43 @@ +# ruff: noqa +""" +ASGI config for Feedback-Linker project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/dev/howto/deployment/asgi/ + +""" + +import os +import sys +from pathlib import Path + +from django.core.asgi import get_asgi_application + +# This allows easy placement of apps within the interior +# feedback_linker directory. +BASE_DIR = Path(__file__).resolve(strict=True).parent.parent +sys.path.append(str(BASE_DIR / 'feedback_linker')) + +# If DJANGO_SETTINGS_MODULE is unset, default to the local settings +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') + +# This application object is used by any ASGI server configured to use this file. +django_application = get_asgi_application() +# Apply ASGI middleware here. +# from helloworld.asgi import HelloWorldApplication +# application = HelloWorldApplication(application) + +# Import websocket application here, so apps from django_application are loaded first +from config.websocket import websocket_application + + +async def application(scope, receive, send): + if scope['type'] == 'http': + await django_application(scope, receive, send) + elif scope['type'] == 'websocket': + await websocket_application(scope, receive, send) + else: + msg = f"Unknown scope type {scope['type']}" + raise NotImplementedError(msg) diff --git a/src/config/settings/__init__.py b/src/config/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/config/settings/base.py b/src/config/settings/base.py new file mode 100644 index 0000000..a02b237 --- /dev/null +++ b/src/config/settings/base.py @@ -0,0 +1,337 @@ +# ruff: noqa: ERA001, E501 +"""Base settings to build other settings files upon.""" + +from pathlib import Path + +import environ + +BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent +# feedback_linker/ +APPS_DIR = BASE_DIR / 'feedback_linker' +env = environ.Env() + +READ_DOT_ENV_FILE = env.bool('DJANGO_READ_DOT_ENV_FILE', default=False) +if READ_DOT_ENV_FILE: + # OS environment variables take precedence over variables from .env + env.read_env(str(BASE_DIR / '.env')) + +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = env.bool('DJANGO_DEBUG', False) +# Local time zone. Choices are +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# though not all of them may be available with every OS. +# In Windows, this must be set to your system time zone. +TIME_ZONE = 'UTC' +# https://docs.djangoproject.com/en/dev/ref/settings/#language-code +LANGUAGE_CODE = 'en-us' +# https://docs.djangoproject.com/en/dev/ref/settings/#languages +# from django.utils.translation import gettext_lazy as _ +# LANGUAGES = [ +# ('en', _('English')), +# ('fr-fr', _('French')), +# ('pt-br', _('Portuguese')), +# ] +# https://docs.djangoproject.com/en/dev/ref/settings/#site-id +SITE_ID = 1 +# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n +USE_I18N = True +# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz +USE_TZ = True +# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths +LOCALE_PATHS = [str(BASE_DIR / 'locale')] + +# DATABASES +# ------------------------------------------------------------------------------ +_DATABASES_DEFAULT = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite', + 'OPTIONS': { + 'timeout': 20, + }, + } +} +# https://docs.djangoproject.com/en/dev/ref/settings/#databases +DATABASES = {'default': env.db('DATABASE_URL', default='sqlite:///db.sqlite')} +DATABASES['default']['ATOMIC_REQUESTS'] = True +# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# URLS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf +ROOT_URLCONF = 'config.urls' +# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application +WSGI_APPLICATION = 'config.wsgi.application' + +# APPS +# ------------------------------------------------------------------------------ +DJANGO_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # "django.contrib.humanize", # Handy template tags + 'django.contrib.admin', + 'django.forms', +] +THIRD_PARTY_APPS = [ + 'crispy_forms', + 'crispy_bootstrap5', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'rest_framework', + 'rest_framework.authtoken', + 'corsheaders', + 'drf_spectacular', +] + +LOCAL_APPS = [ + 'feedback_linker.users', + # Your stuff: custom apps go here +] +# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps +INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS + +# MIGRATIONS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules +MIGRATION_MODULES = {'sites': 'feedback_linker.contrib.sites.migrations'} + +# AUTHENTICATION +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +] +# https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model +AUTH_USER_MODEL = 'users.User' +# https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url +LOGIN_REDIRECT_URL = 'users:redirect' +# https://docs.djangoproject.com/en/dev/ref/settings/#login-url +LOGIN_URL = 'account_login' + +# PASSWORDS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers +PASSWORD_HASHERS = [ + # https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django + 'django.contrib.auth.hashers.Argon2PasswordHasher', + 'django.contrib.auth.hashers.PBKDF2PasswordHasher', + 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', + 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', +] +# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +# MIDDLEWARE +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#middleware +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'allauth.account.middleware.AccountMiddleware', +] + +# STATIC +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#static-root +STATIC_ROOT = str(BASE_DIR / 'staticfiles') +# https://docs.djangoproject.com/en/dev/ref/settings/#static-url +STATIC_URL = '/static/' +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS +STATICFILES_DIRS = [str(APPS_DIR / 'static')] +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders +STATICFILES_FINDERS = [ + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +] + +# MEDIA +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#media-root +MEDIA_ROOT = str(APPS_DIR / 'media') +# https://docs.djangoproject.com/en/dev/ref/settings/#media-url +MEDIA_URL = '/media/' + +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES = [ + { + # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + # https://docs.djangoproject.com/en/dev/ref/settings/#dirs + 'DIRS': [str(APPS_DIR / 'templates')], + # https://docs.djangoproject.com/en/dev/ref/settings/#app-dirs + 'APP_DIRS': True, + 'OPTIONS': { + # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + 'feedback_linker.users.context_processors.allauth_settings', + ], + }, + }, +] + +# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer +FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' + +# http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs +CRISPY_TEMPLATE_PACK = 'bootstrap5' +CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' + +# FIXTURES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs +FIXTURE_DIRS = (str(APPS_DIR / 'fixtures'),) + +# SECURITY +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly +SESSION_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly +CSRF_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options +X_FRAME_OPTIONS = 'DENY' + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = env( + 'DJANGO_EMAIL_BACKEND', + default='django.core.mail.backends.smtp.EmailBackend', +) +# https://docs.djangoproject.com/en/dev/ref/settings/#email-timeout +EMAIL_TIMEOUT = 5 + +# ADMIN +# ------------------------------------------------------------------------------ +# Django Admin URL. +ADMIN_URL = 'admin/' +# https://docs.djangoproject.com/en/dev/ref/settings/#admins +ADMINS = [("""Ivan Ogasawara""", 'ivan.ogasawara@gmail.com')] +# https://docs.djangoproject.com/en/dev/ref/settings/#managers +MANAGERS = ADMINS +# https://cookiecutter-django.readthedocs.io/en/latest/settings.html#other-environment-settings +# Force the `admin` sign in process to go through the `django-allauth` workflow +DJANGO_ADMIN_FORCE_ALLAUTH = env.bool( + 'DJANGO_ADMIN_FORCE_ALLAUTH', + default=False, +) + +# LOGGING +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#logging +# See https://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s', + }, + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'root': {'level': 'INFO', 'handlers': ['console']}, +} + + +# django-allauth +# ------------------------------------------------------------------------------ +ACCOUNT_ALLOW_REGISTRATION = env.bool( + 'DJANGO_ACCOUNT_ALLOW_REGISTRATION', + True, +) +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_AUTHENTICATION_METHOD = 'email' +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_EMAIL_REQUIRED = True +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_USERNAME_REQUIRED = False +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_USER_MODEL_USERNAME_FIELD = None +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_EMAIL_VERIFICATION = 'mandatory' +# https://docs.allauth.org/en/latest/account/configuration.html +ACCOUNT_ADAPTER = 'feedback_linker.users.adapters.AccountAdapter' +# https://docs.allauth.org/en/latest/account/forms.html +ACCOUNT_FORMS = {'signup': 'feedback_linker.users.forms.UserSignupForm'} +# https://docs.allauth.org/en/latest/socialaccount/configuration.html +SOCIALACCOUNT_ADAPTER = 'feedback_linker.users.adapters.SocialAccountAdapter' +# https://docs.allauth.org/en/latest/socialaccount/configuration.html +SOCIALACCOUNT_FORMS = { + 'signup': 'feedback_linker.users.forms.UserSocialSignupForm', +} +# django-compressor +# ------------------------------------------------------------------------------ +# https://django-compressor.readthedocs.io/en/latest/quickstart/#installation +INSTALLED_APPS += ['compressor'] +STATICFILES_FINDERS += ['compressor.finders.CompressorFinder'] +# django-rest-framework +# ------------------------------------------------------------------------------- +# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + +# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup +CORS_URLS_REGEX = r'^/api/.*$' + +# By Default swagger ui is available only to admin user(s). You can change permission classes to change that +# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings +SPECTACULAR_SETTINGS = { + 'TITLE': 'Feedback-Linker API', + 'DESCRIPTION': 'Documentation of API endpoints of Feedback-Linker', + 'VERSION': '1.0.0', + 'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAdminUser'], +} +# Your stuff... +# ------------------------------------------------------------------------------ diff --git a/src/config/settings/dev.py b/src/config/settings/dev.py new file mode 100644 index 0000000..ad52c72 --- /dev/null +++ b/src/config/settings/dev.py @@ -0,0 +1,66 @@ +# ruff: noqa: E501 +from .base import * # noqa: F403 +from .base import INSTALLED_APPS, MIDDLEWARE, env + +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = True +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +SECRET_KEY = env( + 'DJANGO_SECRET_KEY', + default='TqdFkljhxAdhCtrkCxI7CcCHyZS7kTeNfBo25t0LuFcAAwRT6p7C57ifebBnQ0sh', +) +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ['localhost', '0.0.0.0', '127.0.0.1'] # noqa: S104 + +# CACHES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#caches +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': '', + }, +} + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = env( + 'DJANGO_EMAIL_BACKEND', + default='django.core.mail.backends.console.EmailBackend', +) + +# WhiteNoise +# ------------------------------------------------------------------------------ +# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development +INSTALLED_APPS = ['whitenoise.runserver_nostatic', *INSTALLED_APPS] + + +# django-debug-toolbar +# ------------------------------------------------------------------------------ +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites +INSTALLED_APPS += ['debug_toolbar'] +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware +MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] +# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config +DEBUG_TOOLBAR_CONFIG = { + 'DISABLE_PANELS': ['debug_toolbar.panels.redirects.RedirectsPanel'], + 'SHOW_TEMPLATE_CONTEXT': True, +} +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips +INTERNAL_IPS = ['127.0.0.1', '10.0.2.2'] +if env('USE_DOCKER') == 'yes': + import socket + + hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) + INTERNAL_IPS += ['.'.join(ip.split('.')[:-1] + ['1']) for ip in ips] + +# django-extensions +# ------------------------------------------------------------------------------ +# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration +INSTALLED_APPS += ['django_extensions'] + +# Your stuff... +# ------------------------------------------------------------------------------ diff --git a/src/config/settings/production.py b/src/config/settings/production.py new file mode 100644 index 0000000..c70ccde --- /dev/null +++ b/src/config/settings/production.py @@ -0,0 +1,206 @@ +# ruff: noqa: E501 +import logging + +import sentry_sdk + +from sentry_sdk.integrations.django import DjangoIntegration +from sentry_sdk.integrations.logging import LoggingIntegration +from sentry_sdk.integrations.redis import RedisIntegration + +from .base import * # noqa: F403 +from .base import ( + DATABASES, + INSTALLED_APPS, + SPECTACULAR_SETTINGS, + STATIC_URL, + env, +) + +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +SECRET_KEY = env('DJANGO_SECRET_KEY') +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = env.list( + 'DJANGO_ALLOWED_HOSTS', + default=['https://opensciencelabs.github.io/feedback-linker'], +) + +# DATABASES +# ------------------------------------------------------------------------------ +DATABASES['default']['CONN_MAX_AGE'] = env.int('CONN_MAX_AGE', default=60) + +# CACHES +# ------------------------------------------------------------------------------ +CACHES = { + 'default': { + 'BACKEND': 'django_redis.cache.RedisCache', + 'LOCATION': env('REDIS_URL'), + 'OPTIONS': { + 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + # Mimicing memcache behavior. + # https://github.com/jazzband/django-redis#memcached-exceptions-behavior + 'IGNORE_EXCEPTIONS': True, + }, + }, +} + +# SECURITY +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect +SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure +SESSION_COOKIE_SECURE = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure +CSRF_COOKIE_SECURE = True +# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds +# TODO: set this to 60 seconds first and then to 518400 once you prove the former works +SECURE_HSTS_SECONDS = 60 +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains +SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool( + 'DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', + default=True, +) +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload +SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True) +# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff +SECURE_CONTENT_TYPE_NOSNIFF = env.bool( + 'DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', + default=True, +) + +# STATIC & MEDIA +# ------------------------ +STORAGES = { + 'default': { + 'BACKEND': 'django.core.files.storage.FileSystemStorage', + }, + 'staticfiles': { + 'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage', + }, +} + +# EMAIL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email +DEFAULT_FROM_EMAIL = env( + 'DJANGO_DEFAULT_FROM_EMAIL', + default='Feedback-Linker ', +) +# https://docs.djangoproject.com/en/dev/ref/settings/#server-email +SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) +# https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix +EMAIL_SUBJECT_PREFIX = env( + 'DJANGO_EMAIL_SUBJECT_PREFIX', + default='[Feedback-Linker] ', +) + +# ADMIN +# ------------------------------------------------------------------------------ +# Django Admin URL regex. +ADMIN_URL = env('DJANGO_ADMIN_URL') + +# Anymail +# ------------------------------------------------------------------------------ +# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail +INSTALLED_APPS += ['anymail'] +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference +# https://anymail.readthedocs.io/en/stable/esps +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +ANYMAIL = {} + +# django-compressor +# ------------------------------------------------------------------------------ +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_ENABLED +COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', default=True) +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_STORAGE +COMPRESS_STORAGE = 'compressor.storage.GzipCompressorFileStorage' +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_URL +COMPRESS_URL = STATIC_URL +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_OFFLINE +COMPRESS_OFFLINE = ( + True # Offline compression is required when using Whitenoise +) +# https://django-compressor.readthedocs.io/en/latest/settings/#django.conf.settings.COMPRESS_FILTERS +COMPRESS_FILTERS = { + 'css': [ + 'compressor.filters.css_default.CssAbsoluteFilter', + 'compressor.filters.cssmin.rCSSMinFilter', + ], + 'js': ['compressor.filters.jsmin.JSMinFilter'], +} + +# LOGGING +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#logging +# See https://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s', + }, + }, + 'handlers': { + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'root': {'level': 'INFO', 'handlers': ['console']}, + 'loggers': { + 'django.db.backends': { + 'level': 'ERROR', + 'handlers': ['console'], + 'propagate': False, + }, + # Errors logged by the SDK itself + 'sentry_sdk': { + 'level': 'ERROR', + 'handlers': ['console'], + 'propagate': False, + }, + 'django.security.DisallowedHost': { + 'level': 'ERROR', + 'handlers': ['console'], + 'propagate': False, + }, + }, +} + +# Sentry +# ------------------------------------------------------------------------------ +SENTRY_DSN = env('SENTRY_DSN') +SENTRY_LOG_LEVEL = env.int('DJANGO_SENTRY_LOG_LEVEL', logging.INFO) + +sentry_logging = LoggingIntegration( + level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs + event_level=logging.ERROR, # Send errors as events +) +integrations = [sentry_logging, DjangoIntegration(), RedisIntegration()] +sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=integrations, + environment=env('SENTRY_ENVIRONMENT', default='production'), + traces_sample_rate=env.float('SENTRY_TRACES_SAMPLE_RATE', default=0.0), +) + +# django-rest-framework +# ------------------------------------------------------------------------------- +# Tools that generate code samples can use SERVERS to point to the correct domain +SPECTACULAR_SETTINGS['SERVERS'] = [ + { + 'url': 'https://https://opensciencelabs.github.io/feedback-linker', + 'description': 'Production server', + }, +] +# Your stuff... +# ------------------------------------------------------------------------------ diff --git a/src/config/settings/test.py b/src/config/settings/test.py new file mode 100644 index 0000000..013a6fc --- /dev/null +++ b/src/config/settings/test.py @@ -0,0 +1,37 @@ +""" +With these settings, tests run faster. +""" + +from .base import * # noqa: F403 +from .base import TEMPLATES, env + +# GENERAL +# ----------------------------------------------------------------------------- +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +SECRET_KEY = env( + 'DJANGO_SECRET_KEY', + default='fsCuF2mBJHIC1dEO48y251JSvMeOcZEKfUDpnfMJmLMOi6LhofomXe0pMPXmqElf', +) +# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner +TEST_RUNNER = 'django.test.runner.DiscoverRunner' + +# PASSWORDS +# ----------------------------------------------------------------------------- +# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers +PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher'] + +# EMAIL +# ----------------------------------------------------------------------------- +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' + +# DEBUGGING FOR TEMPLATES +# ----------------------------------------------------------------------------- +TEMPLATES[0]['OPTIONS']['debug'] = True # type: ignore[index] + +# MEDIA +# ----------------------------------------------------------------------------- +# https://docs.djangoproject.com/en/dev/ref/settings/#media-url +MEDIA_URL = 'http://media.testserver' +# Your stuff... +# ----------------------------------------------------------------------------- diff --git a/src/config/urls.py b/src/config/urls.py new file mode 100644 index 0000000..8ab0fa8 --- /dev/null +++ b/src/config/urls.py @@ -0,0 +1,77 @@ +# ruff: noqa +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.contrib.staticfiles.urls import staticfiles_urlpatterns +from django.urls import include +from django.urls import path +from django.views import defaults as default_views +from django.views.generic import TemplateView +from drf_spectacular.views import SpectacularAPIView +from drf_spectacular.views import SpectacularSwaggerView +from rest_framework.authtoken.views import obtain_auth_token + +urlpatterns = [ + path( + '', TemplateView.as_view(template_name='pages/home.html'), name='home' + ), + path( + 'about/', + TemplateView.as_view(template_name='pages/about.html'), + name='about', + ), + # Django Admin, use {% url 'admin:index' %} + path(settings.ADMIN_URL, admin.site.urls), + # User management + path('users/', include('feedback_linker.users.urls', namespace='users')), + path('accounts/', include('allauth.urls')), + # Your stuff: custom urls includes go here + # ... + # Media files + *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), +] +if settings.DEBUG: + # Static file serving when using Gunicorn + Uvicorn for local web socket development + urlpatterns += staticfiles_urlpatterns() + +# API URLS +urlpatterns += [ + # API base url + path('api/', include('config.api_router')), + # DRF auth token + path('auth-token/', obtain_auth_token), + path('api/schema/', SpectacularAPIView.as_view(), name='api-schema'), + path( + 'api/docs/', + SpectacularSwaggerView.as_view(url_name='api-schema'), + name='api-docs', + ), +] + +if settings.DEBUG: + # This allows the error pages to be debugged during development, just visit + # these url in browser to see how these error pages look like. + urlpatterns += [ + path( + '400/', + default_views.bad_request, + kwargs={'exception': Exception('Bad Request!')}, + ), + path( + '403/', + default_views.permission_denied, + kwargs={'exception': Exception('Permission Denied')}, + ), + path( + '404/', + default_views.page_not_found, + kwargs={'exception': Exception('Page not Found')}, + ), + path('500/', default_views.server_error), + ] + if 'debug_toolbar' in settings.INSTALLED_APPS: + import debug_toolbar + + urlpatterns = [ + path('__debug__/', include(debug_toolbar.urls)) + ] + urlpatterns diff --git a/src/config/websocket.py b/src/config/websocket.py new file mode 100644 index 0000000..8f00ca9 --- /dev/null +++ b/src/config/websocket.py @@ -0,0 +1,13 @@ +# type: ignore +async def websocket_application(scope, receive, send) -> None: + while True: + event = await receive() + + if event['type'] == 'websocket.connect': + await send({'type': 'websocket.accept'}) + + if event['type'] == 'websocket.disconnect': + break + + if event['type'] == 'websocket.receive' and event['text'] == 'ping': + await send({'type': 'websocket.send', 'text': 'pong!'}) diff --git a/src/config/wsgi.py b/src/config/wsgi.py new file mode 100644 index 0000000..9b18fe6 --- /dev/null +++ b/src/config/wsgi.py @@ -0,0 +1,40 @@ +# ruff: noqa +""" +WSGI config for Feedback-Linker project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" + +import os +import sys +from pathlib import Path + +from django.core.wsgi import get_wsgi_application + +# This allows easy placement of apps within the interior +# feedback_linker directory. +BASE_DIR = Path(__file__).resolve(strict=True).parent.parent +sys.path.append(str(BASE_DIR / 'feedback_linker')) +# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks +# if running multiple sites in the same mod_wsgi process. To fix this, use +# mod_wsgi daemon mode with each site in its own daemon process, or use +# os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production" +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.production') + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +application = get_wsgi_application() +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application) diff --git a/src/feedback_linker/__init__.py b/src/feedback_linker/__init__.py index e6c2573..f1103b1 100644 --- a/src/feedback_linker/__init__.py +++ b/src/feedback_linker/__init__.py @@ -1,19 +1,5 @@ -"""Feedback Linker.""" -# mypy: disable-error-code="attr-defined" - -from importlib import metadata as importlib_metadata - - -def get_version() -> str: - """Return the program version.""" - try: - return importlib_metadata.version(__name__) - except importlib_metadata.PackageNotFoundError: # pragma: no cover - return '0.1.0' # semantic-release - - -version = get_version() - -__version__ = version -__author__ = 'Ivan Ogasawara' -__email__ = 'ivan.ogasawara@gmail.com' +__version__ = '0.1.0' +__version_info__ = tuple( + int(num) if num.isdigit() else num + for num in __version__.replace('-', '.', 1).split('.') +) diff --git a/src/feedback_linker/app.py b/src/feedback_linker/app.py deleted file mode 100644 index 6635fc4..0000000 --- a/src/feedback_linker/app.py +++ /dev/null @@ -1,90 +0,0 @@ -"""App module.""" -from __future__ import annotations - -from typing import Union - -from flask import Flask, flash, redirect, render_template, url_for -from werkzeug.wrappers.response import Response - -from feedback_linker.forms import ( - FeedbackForm, - LinkForm, - LoginForm, - PersonForm, - ProjectForm, -) -from feedback_linker.models import ( - Feedback, - Link, - Person, - Project, - db, - init_app, -) - -app = Flask(__name__) -app.config['SECRET_KEY'] = 'your_secret_key' -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///feedback_linker.db' - -init_app(app) - - -@app.route('/') -def index() -> str: - """Define the view for the index page.""" - return render_template('index.html') - - -@app.route('/login', methods=['GET', 'POST']) -def login() -> str: - """Define the view for managing login.""" - form = LoginForm() - # Login logic here - return render_template('login.html', form=form) - - -@app.route('/projects', methods=['GET', 'POST']) -def manage_projects() -> Union[Response, str]: - """Define the view for managing projects.""" - form = ProjectForm() - if form.validate_on_submit(): - project = Project(name=form.name.data) - db.session.add(project) - db.session.commit() - flash('Project created successfully!') - return redirect(url_for('manage_projects')) - projects = Project.query.all() - return render_template('project_list.html', form=form, projects=projects) - - -@app.route('/people', methods=['GET', 'POST']) -def manage_people() -> str: - """Define the view for managing people.""" - form = PersonForm() - # Person creation logic here - people = Person.query.all() - return render_template('people_list.html', form=form, people=people) - - -@app.route('/links', methods=['GET', 'POST']) -def manage_links() -> str: - """Define the view for managing links.""" - form = LinkForm() - # Link creation logic here - links = Link.query.all() - return render_template('link_list.html', form=form, links=links) - - -@app.route('/feedback', methods=['GET', 'POST']) -def submit_feedback() -> str: - """Define the view for submitting feedback.""" - form = FeedbackForm() - # Feedback submission logic here - feedbacks = Feedback.query.all() - return render_template( - 'feedback_list.html', form=form, feedbacks=feedbacks - ) - - -if __name__ == '__main__': - app.run(debug=True) diff --git a/src/feedback_linker/conftest.py b/src/feedback_linker/conftest.py new file mode 100644 index 0000000..00b25b3 --- /dev/null +++ b/src/feedback_linker/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from feedback_linker.users.models import User +from feedback_linker.users.tests.factories import UserFactory + + +@pytest.fixture(autouse=True) +def _media_storage(settings, tmpdir) -> None: # type: ignore + settings.MEDIA_ROOT = tmpdir.strpath + + +@pytest.fixture() +def user(db) -> User: # type: ignore + return UserFactory() diff --git a/src/feedback_linker/contrib/__init__.py b/src/feedback_linker/contrib/__init__.py new file mode 100644 index 0000000..1c7ecc8 --- /dev/null +++ b/src/feedback_linker/contrib/__init__.py @@ -0,0 +1,5 @@ +""" +To understand why this file is here, please read: + +http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django +""" diff --git a/src/feedback_linker/contrib/sites/__init__.py b/src/feedback_linker/contrib/sites/__init__.py new file mode 100644 index 0000000..1c7ecc8 --- /dev/null +++ b/src/feedback_linker/contrib/sites/__init__.py @@ -0,0 +1,5 @@ +""" +To understand why this file is here, please read: + +http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django +""" diff --git a/src/feedback_linker/contrib/sites/migrations/0001_initial.py b/src/feedback_linker/contrib/sites/migrations/0001_initial.py new file mode 100644 index 0000000..fd76afb --- /dev/null +++ b/src/feedback_linker/contrib/sites/migrations/0001_initial.py @@ -0,0 +1,43 @@ +import django.contrib.sites.models +from django.contrib.sites.models import _simple_domain_name_validator +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Site", + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "domain", + models.CharField( + max_length=100, + verbose_name="domain name", + validators=[_simple_domain_name_validator], + ), + ), + ("name", models.CharField(max_length=50, verbose_name="display name")), + ], + options={ + "ordering": ("domain",), + "db_table": "django_site", + "verbose_name": "site", + "verbose_name_plural": "sites", + }, + bases=(models.Model,), + managers=[("objects", django.contrib.sites.models.SiteManager())], + ), + ] diff --git a/src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py b/src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py new file mode 100644 index 0000000..4a44a6a --- /dev/null +++ b/src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py @@ -0,0 +1,21 @@ +import django.contrib.sites.models +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [("sites", "0001_initial")] + + operations = [ + migrations.AlterField( + model_name="site", + name="domain", + field=models.CharField( + max_length=100, + unique=True, + validators=[django.contrib.sites.models._simple_domain_name_validator], + verbose_name="domain name", + ), + ) + ] diff --git a/src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py b/src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py new file mode 100644 index 0000000..1b66160 --- /dev/null +++ b/src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py @@ -0,0 +1,63 @@ +""" +To understand why this file is here, please read: + +http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django +""" +from django.conf import settings +from django.db import migrations + + +def _update_or_create_site_with_sequence(site_model, connection, domain, name): + """Update or create the site with default ID and keep the DB sequence in sync.""" + site, created = site_model.objects.update_or_create( + id=settings.SITE_ID, + defaults={ + "domain": domain, + "name": name, + }, + ) + if created: + # We provided the ID explicitly when creating the Site entry, therefore the DB + # sequence to auto-generate them wasn't used and is now out of sync. If we + # don't do anything, we'll get a unique constraint violation the next time a + # site is created. + # To avoid this, we need to manually update DB sequence and make sure it's + # greater than the maximum value. + max_id = site_model.objects.order_by("-id").first().id + with connection.cursor() as cursor: + cursor.execute("SELECT last_value from django_site_id_seq") + (current_id,) = cursor.fetchone() + if current_id <= max_id: + cursor.execute( + "alter sequence django_site_id_seq restart with %s", + [max_id + 1], + ) + + +def update_site_forward(apps, schema_editor): + """Set site domain and name.""" + Site = apps.get_model("sites", "Site") + _update_or_create_site_with_sequence( + Site, + schema_editor.connection, + "https://opensciencelabs.github.io/feedback-linker", + "Feedback-Linker", + ) + + +def update_site_backward(apps, schema_editor): + """Revert site domain and name to default.""" + Site = apps.get_model("sites", "Site") + _update_or_create_site_with_sequence( + Site, + schema_editor.connection, + "example.com", + "example.com", + ) + + +class Migration(migrations.Migration): + + dependencies = [("sites", "0002_alter_domain_unique")] + + operations = [migrations.RunPython(update_site_forward, update_site_backward)] diff --git a/src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py b/src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py new file mode 100644 index 0000000..f7118ca --- /dev/null +++ b/src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py @@ -0,0 +1,21 @@ +# Generated by Django 3.1.7 on 2021-02-04 14:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("sites", "0003_set_site_domain_and_name"), + ] + + operations = [ + migrations.AlterModelOptions( + name="site", + options={ + "ordering": ["domain"], + "verbose_name": "site", + "verbose_name_plural": "sites", + }, + ), + ] diff --git a/src/feedback_linker/contrib/sites/migrations/__init__.py b/src/feedback_linker/contrib/sites/migrations/__init__.py new file mode 100644 index 0000000..1c7ecc8 --- /dev/null +++ b/src/feedback_linker/contrib/sites/migrations/__init__.py @@ -0,0 +1,5 @@ +""" +To understand why this file is here, please read: + +http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django +""" diff --git a/src/feedback_linker/forms.py b/src/feedback_linker/forms.py deleted file mode 100644 index 3317ca8..0000000 --- a/src/feedback_linker/forms.py +++ /dev/null @@ -1,114 +0,0 @@ -"""Forms module.""" -from __future__ import annotations - -from flask_wtf import FlaskForm -from wtforms import ( - IntegerField, - PasswordField, - SelectField, - StringField, - SubmitField, - TextAreaField, -) -from wtforms.ext.sqlalchemy.fields import ( - QuerySelectField, - QuerySelectMultipleField, -) -from wtforms.validators import DataRequired, Email, Length - -from .models import Link, Person, Project - - -def project_choices(): - """Return the choices for project.""" - return Project.query.all() - - -def person_choices(): - """Return the choices for person.""" - return Person.query.all() - - -class LoginForm(FlaskForm): - """The login form.""" - - email = StringField('Email', validators=[DataRequired(), Email()]) - password = PasswordField( - 'Password', validators=[DataRequired(), Length(min=6)] - ) - submit = SubmitField('Log In') - - -class ProjectForm(FlaskForm): - """The project form.""" - - name = StringField('Project Name', validators=[DataRequired()]) - people = QuerySelectMultipleField( - 'Select People', query_factory=person_choices, get_label='name' - ) - submit = SubmitField('Submit') - - -class PersonForm(FlaskForm): - """The person form.""" - - name = StringField('Name', validators=[DataRequired()]) - email = StringField('Email', validators=[DataRequired(), Email()]) - projects = QuerySelectMultipleField( - 'Select Projects', query_factory=project_choices, get_label='name' - ) - submit = SubmitField('Submit') - - -class LinkForm(FlaskForm): - """The link form.""" - - person_one = QuerySelectField( - 'Person One', - query_factory=person_choices, - get_label='name', - validators=[DataRequired()], - ) - person_two = QuerySelectField( - 'Person Two', - query_factory=person_choices, - get_label='name', - validators=[DataRequired()], - ) - supervisor = QuerySelectField( - 'Supervisor', - query_factory=person_choices, - get_label='name', - validators=[DataRequired()], - ) - periodicity = SelectField( - 'Periodicity', - choices=[ - ('daily', 'Daily'), - ('weekly', 'Weekly'), - ('monthly', 'Monthly'), - ], - validators=[DataRequired()], - ) - times = IntegerField('Every X times', validators=[DataRequired()]) - submit = SubmitField('Submit') - - -class FeedbackForm(FlaskForm): - """The feedback form.""" - - content = TextAreaField('Feedback Content', validators=[DataRequired()]) - link = QuerySelectField( - 'Link', - query_factory=lambda: Link.query.all(), - get_label='id', - validators=[DataRequired()], - ) - submit = SubmitField('Submit') - - -def init_app(app) -> None: - """Initialize the app for forms.""" - # This function is here if you need to initialize anything specifically - # for the forms - pass diff --git a/src/feedback_linker/models.py b/src/feedback_linker/models.py deleted file mode 100644 index 6595c17..0000000 --- a/src/feedback_linker/models.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Models module.""" -from __future__ import annotations - -from typing import TYPE_CHECKING - -from flask_sqlalchemy import SQLAlchemy - -db = SQLAlchemy() - -if TYPE_CHECKING: - from flask_sqlalchemy.model import Model - - BaseModel = db.make_declarative_base(Model) -else: - BaseModel = db.Model - - -class Project(BaseModel): # type: ignore[name-defined] - """Project model.""" - - __tablename__ = 'project' - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False) - people = db.relationship( - 'Person', secondary='project_person', back_populates='projects' - ) - supervisors = db.relationship('Person', secondary='project_supervisor') - - -class Person(BaseModel): # type: ignore[name-defined] - """Person model.""" - - __tablename__ = 'person' - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False) - email = db.Column(db.String(100), unique=True, nullable=False) - projects = db.relationship( - 'Project', secondary='project_person', back_populates='people' - ) - links = db.relationship( - 'Link', secondary='person_link', back_populates='people' - ) - - -project_person = db.Table( - 'project_person', - db.Column( - 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True - ), - db.Column( - 'project_id', db.Integer, db.ForeignKey('project.id'), primary_key=True - ), -) - -project_supervisor = db.Table( - 'project_supervisor', - db.Column( - 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True - ), - db.Column( - 'project_id', db.Integer, db.ForeignKey('project.id'), primary_key=True - ), -) - - -class Link(BaseModel): # type: ignore[name-defined] - """Link model.""" - - __tablename__ = 'link' - id = db.Column(db.Integer, primary_key=True) - person_one_id = db.Column(db.Integer, db.ForeignKey('person.id')) - person_two_id = db.Column(db.Integer, db.ForeignKey('person.id')) - supervisor_id = db.Column(db.Integer, db.ForeignKey('person.id')) - periodicity = db.Column(db.String(50)) - times = db.Column(db.Integer) - feedback = db.relationship('Feedback', backref='link', lazy=True) - - -person_link = db.Table( - 'person_link', - db.Column( - 'link_id', db.Integer, db.ForeignKey('link.id'), primary_key=True - ), - db.Column( - 'person_id', db.Integer, db.ForeignKey('person.id'), primary_key=True - ), -) - - -class Feedback(BaseModel): # type: ignore[name-defined] - """Feedback model.""" - - __tablename__ = 'feedback' - id = db.Column(db.Integer, primary_key=True) - content = db.Column(db.Text, nullable=False) - link_id = db.Column(db.Integer, db.ForeignKey('link.id')) - timestamp = db.Column(db.DateTime, server_default=db.func.now()) - - -def init_app(app) -> None: - """Initialize app for the database.""" - db.init_app(app) - with app.app_context(): - db.create_all() diff --git a/src/feedback_linker/static/css/project.css b/src/feedback_linker/static/css/project.css new file mode 100644 index 0000000..f1d543d --- /dev/null +++ b/src/feedback_linker/static/css/project.css @@ -0,0 +1,13 @@ +/* These styles are generated from project.scss. */ + +.alert-debug { + color: black; + background-color: white; + border-color: #d6e9c6; +} + +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} diff --git a/src/feedback_linker/static/fonts/.gitkeep b/src/feedback_linker/static/fonts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/feedback_linker/static/images/favicons/favicon.ico b/src/feedback_linker/static/images/favicons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e1c1dd1a32a3a077c41a21e52bc7fb5ac90d3afb GIT binary patch literal 8348 zcmeHLX-gGh6rQ3V&`92%;lcmGu`Jl^WK@OV`^XKh08PZoaH%l?rdiiWq>kJ2@6vM zhAH8L6=kTRD1!xR`-2o^hS&}loN!U1#gF+=YxEq~uqf5#iBw%p0;w;5ehm+6a!sSu zh;X+$+}oF$X1Q6DwS~>Y_Hpw^(&QvJO<5ZCPrn5laNp%s{B3x1hf%*?eW>oS{<{4s^u_yG zpDyG!_iV#~G=q<~slG@0Sx47XXQ%O;HY7ILVf}B-)R$6GV^A78)t0tN9`tu3r9Z+xM?NgUd8geu=c`0?r;-KR&IQjKs zSB#VCpg8CPW&M}$sth@H=VS%t;23!!j};F)bb;W3EkA!4QmCsY_p81^TK0{;$g>e1Hl998^0M+r0-7ZSN+l_cMSRuEALTE@|d6+3{GMP^;_|<yhubb{FF1IPgH|0>SH%pC!c)uFI)H z?jv4y0uO{P5WE>~I<$r!RFo0lN4r{xm;Jy4p$h~b3S*a#)$Z*nI}&Kc_C+*zZHz1v zIR8TBVH7Y?Mp~ zofpsr+SP@>EX4e*bQ{nAUYtKrQ&-5d4j;FF{^-^Dt2^5I`HSb!|22PN26n3>hKPOy zW2D*A*v+c{bFKCwozbB{dObp~e&1Nxrj<4Ih4~w)M`nl08o}Wq2 z#Jt(k+M>+}JY(=2gm(gdUq)^@e%tX(F)RBtotnB2^y>YKz-5eg_qO&n%lJ1nuQmTY zxmyE1NWjl1EGvD?Z|n;neT;sa?Q;Ef-#%$BYxXAhD4xF+@M>*qrR!x^sPN98|BX4; z!$NJcKF`^C7f(<_vlp%b>`pxL^8Y<&?KFx{n_!5C9VqLA*CP@zhXs2t#dmrAKu?d_ y^&_r5_e@uesG}CO*g!3o?-kB+I^cA`>44J#rvpw0oDMi0a5~_0!0Eu>4*Uj$LD0AW literal 0 HcmV?d00001 diff --git a/src/feedback_linker/static/js/project.js b/src/feedback_linker/static/js/project.js new file mode 100644 index 0000000..d26d23b --- /dev/null +++ b/src/feedback_linker/static/js/project.js @@ -0,0 +1 @@ +/* Project specific Javascript goes here. */ diff --git a/src/feedback_linker/templates/403.html b/src/feedback_linker/templates/403.html new file mode 100644 index 0000000..1259c22 --- /dev/null +++ b/src/feedback_linker/templates/403.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block title %} + Forbidden (403) +{% endblock title %} +{% block content %} +

Forbidden (403)

+

+ {% if exception %} + {{ exception }} + {% else %} + You're not allowed to access + this page. + {% endif %} +

+{% endblock content %} diff --git a/src/feedback_linker/templates/403_csrf.html b/src/feedback_linker/templates/403_csrf.html new file mode 100644 index 0000000..1259c22 --- /dev/null +++ b/src/feedback_linker/templates/403_csrf.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block title %} + Forbidden (403) +{% endblock title %} +{% block content %} +

Forbidden (403)

+

+ {% if exception %} + {{ exception }} + {% else %} + You're not allowed to access + this page. + {% endif %} +

+{% endblock content %} diff --git a/src/feedback_linker/templates/404.html b/src/feedback_linker/templates/404.html new file mode 100644 index 0000000..f0062c7 --- /dev/null +++ b/src/feedback_linker/templates/404.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block title %} + Page not found +{% endblock title %} +{% block content %} +

Page not found

+

+ {% if exception %} + {{ exception }} + {% else %} + This is not the page you were + looking for. + {% endif %} +

+{% endblock content %} diff --git a/src/feedback_linker/templates/500.html b/src/feedback_linker/templates/500.html new file mode 100644 index 0000000..f1e044c --- /dev/null +++ b/src/feedback_linker/templates/500.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block title %} + Server Error +{% endblock title %} +{% +block content %} +

Ooops!!! 500

+

Looks like something went wrong!

+

+ We track these errors automatically, but if the problem persists feel free to + contact us. In the meantime, try refreshing. +

+{% endblock content %} diff --git a/src/feedback_linker/templates/account/account_inactive.html b/src/feedback_linker/templates/account/account_inactive.html new file mode 100644 index 0000000..1dc3792 --- /dev/null +++ b/src/feedback_linker/templates/account/account_inactive.html @@ -0,0 +1,12 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Account Inactive" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Account Inactive" %}

+

{% translate "This account is inactive." %}

+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/base.html b/src/feedback_linker/templates/account/base.html new file mode 100644 index 0000000..6839f32 --- /dev/null +++ b/src/feedback_linker/templates/account/base.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block title %} + {% block head_title %} + {% endblock + head_title %} +{% endblock title %} +{% block content %} +
+
+ {% block inner %} + {% endblock inner %} +
+
+{% endblock content %} diff --git a/src/feedback_linker/templates/account/email.html b/src/feedback_linker/templates/account/email.html new file mode 100644 index 0000000..6c3678d --- /dev/null +++ b/src/feedback_linker/templates/account/email.html @@ -0,0 +1,97 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Account" %} +{% endblock head_title %} +{% +block inner %} +

{% translate "E-mail Addresses" %}

+{% if user.emailaddress_set.all %} +

+ {% translate "The following e-mail addresses are associated with your + account:" %} +

+ + {% else %} +

+ {% translate "Warning:" %} {% translate "You currently do not + have any e-mail address set up. You should really add an e-mail address so you + can receive notifications, reset your password, etc." %} +

+ {% endif %} +

{% translate "Add E-mail Address" %}

+
+ {% csrf_token %} {{ form|crispy }} + +
+{% endblock inner %} +{% block inline_javascript %} + {{ block.super }} + +{% endblock inline_javascript %} diff --git a/src/feedback_linker/templates/account/email_confirm.html b/src/feedback_linker/templates/account/email_confirm.html new file mode 100644 index 0000000..d4e87fb --- /dev/null +++ b/src/feedback_linker/templates/account/email_confirm.html @@ -0,0 +1,35 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} + +{% block + head_title %} + {% translate "Confirm E-mail Address" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Confirm E-mail Address" %}

+ {% if confirmation %} + {% user_display confirmation.email_address.user as + user_display %} +

+ {% blocktranslate with confirmation.email_address.email as email %}Please + confirm that {{ email }} is an e-mail address + for user {{ user_display }}.{% endblocktranslate %} +

+
+ {% csrf_token %} + +
+ {% else %} + {% url 'account_email' as email_url %} +

+ {% blocktranslate %} +This e-mail confirmation link expired or is invalid. +Please +issue a new e-mail confirmation request.{% +endblocktranslate %} +

+{% endif %} +{% endblock inner %} diff --git a/src/feedback_linker/templates/account/login.html b/src/feedback_linker/templates/account/login.html new file mode 100644 index 0000000..a26b706 --- /dev/null +++ b/src/feedback_linker/templates/account/login.html @@ -0,0 +1,58 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account socialaccount +%} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Sign In" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Sign In" %}

+ {% get_providers as socialaccount_providers %} + {% if socialaccount_providers %} +

+ {% translate "Please sign in with one of your existing third party accounts:" + %} + {% if ACCOUNT_ALLOW_REGISTRATION %} + {% blocktranslate trimmed %} + Or, + sign up + for a {{ site_name }} account and sign in below: + {% endblocktranslate %} + {% + endif %} +

+
+
    + {% include "socialaccount/snippets/provider_list.html" with process="login" + %} +
+ +
+ {% include "socialaccount/snippets/login_extra.html" %} + {% else %} + {% if + ACCOUNT_ALLOW_REGISTRATION %} +

+ {% blocktranslate trimmed %} + If you have not created an account yet, then + please + sign up first. + {% endblocktranslate %} +

+ {% endif %} + {% endif %} + + {% endblock inner %} diff --git a/src/feedback_linker/templates/account/logout.html b/src/feedback_linker/templates/account/logout.html new file mode 100644 index 0000000..bfcf596 --- /dev/null +++ b/src/feedback_linker/templates/account/logout.html @@ -0,0 +1,21 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Sign Out" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Sign Out" %}

+

{% translate "Are you sure you want to sign out?" %}

+
+ {% csrf_token %} + {% if redirect_field_value %} + + {% endif %} + +
+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_change.html b/src/feedback_linker/templates/account/password_change.html new file mode 100644 index 0000000..841c66d --- /dev/null +++ b/src/feedback_linker/templates/account/password_change.html @@ -0,0 +1,18 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Change Password" %} +{% endblock head_title +%} +{% block inner %} +

{% translate "Change Password" %}

+
+ {% csrf_token %} {{ form|crispy }} + +
+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_reset.html b/src/feedback_linker/templates/account/password_reset.html new file mode 100644 index 0000000..4003db2 --- /dev/null +++ b/src/feedback_linker/templates/account/password_reset.html @@ -0,0 +1,33 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} +{% load +crispy_forms_tags %} + +{% block head_title %} + {% translate "Password Reset" %} {% + endblock head_title %} + {% block inner %} +

{% translate "Password Reset" %}

+ {% if user.is_authenticated %} + {% include + "account/snippets/already_logged_in.html" %} + {% endif %} +

+ {% translate "Forgotten your password? Enter your e-mail address below, and + we'll send you an e-mail allowing you to reset it." %} +

+
+ {% csrf_token %} {{ form|crispy }} + +
+

+ {% blocktranslate %}Please contact us if you have any trouble resetting your + password.{% endblocktranslate %} +

+ {% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_reset_done.html b/src/feedback_linker/templates/account/password_reset_done.html new file mode 100644 index 0000000..a20c666 --- /dev/null +++ b/src/feedback_linker/templates/account/password_reset_done.html @@ -0,0 +1,21 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} + +{% block + head_title %} + {% translate "Password Reset" %} +{% endblock head_title %} +{% +block inner %} +

{% translate "Password Reset" %}

+{% if user.is_authenticated %} + {% include + "account/snippets/already_logged_in.html" %} +{% endif %} +

+ {% blocktranslate %}We have sent you an e-mail. Please contact us if you do + not receive it within a few minutes.{% endblocktranslate %} +

+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_reset_from_key.html b/src/feedback_linker/templates/account/password_reset_from_key.html new file mode 100644 index 0000000..6c79de3 --- /dev/null +++ b/src/feedback_linker/templates/account/password_reset_from_key.html @@ -0,0 +1,39 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Change Password" %} +{% endblock head_title +%} +{% block inner %} +

+ {% if token_fail %} + {% translate "Bad Token" %} + {% else %} + {% translate + "Change Password" %} + {% endif %} +

+ {% if token_fail %} + {% url 'account_reset_password' as passwd_reset_url %} +

+ {% blocktranslate %} +The password reset link was invalid, possibly because it +has already been used. Please request a +new password reset. +{% endblocktranslate +%} +

+{% else %} +{% if form %} +
+{% csrf_token %} {{ form|crispy }} + +
+{% else %} +

{% translate "Your password is now changed." %}

+{% endif %} +{% endif %} +{% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_reset_from_key_done.html b/src/feedback_linker/templates/account/password_reset_from_key_done.html new file mode 100644 index 0000000..f208ffc --- /dev/null +++ b/src/feedback_linker/templates/account/password_reset_from_key_done.html @@ -0,0 +1,12 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Change Password" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Change Password" %}

+

{% translate "Your password is now changed." %}

+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/password_set.html b/src/feedback_linker/templates/account/password_set.html new file mode 100644 index 0000000..65d8c11 --- /dev/null +++ b/src/feedback_linker/templates/account/password_set.html @@ -0,0 +1,20 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Set Password" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Set Password" %}

+
+ {% csrf_token %} {{ form|crispy }} + +
+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/signup.html b/src/feedback_linker/templates/account/signup.html new file mode 100644 index 0000000..4cd5c96 --- /dev/null +++ b/src/feedback_linker/templates/account/signup.html @@ -0,0 +1,28 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %} + {% translate "Signup" %} +{% endblock head_title %} +{% +block inner %} +

{% translate "Sign Up" %}

+

+ {% blocktranslate %}Already have an account? Then please + sign in.{% endblocktranslate %} +

+ +{% endblock inner %} diff --git a/src/feedback_linker/templates/account/signup_closed.html b/src/feedback_linker/templates/account/signup_closed.html new file mode 100644 index 0000000..b19bdec --- /dev/null +++ b/src/feedback_linker/templates/account/signup_closed.html @@ -0,0 +1,12 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Sign Up Closed" %} +{% endblock head_title %} +{% block inner %} +

{% translate "Sign Up Closed" %}

+

{% translate "We are sorry, but the sign up is currently closed." %}

+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/verification_sent.html b/src/feedback_linker/templates/account/verification_sent.html new file mode 100644 index 0000000..a1f3fc9 --- /dev/null +++ b/src/feedback_linker/templates/account/verification_sent.html @@ -0,0 +1,17 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Verify Your E-mail Address" %} +{% endblock head_title %} +{% block + inner %} +

{% translate "Verify Your E-mail Address" %}

+

+ {% blocktranslate %}We have sent an e-mail to you for verification. Follow the + link provided to finalize the signup process. Please contact us if you do not + receive it within a few minutes.{% endblocktranslate %} +

+{% endblock inner %} diff --git a/src/feedback_linker/templates/account/verified_email_required.html b/src/feedback_linker/templates/account/verified_email_required.html new file mode 100644 index 0000000..e75b39f --- /dev/null +++ b/src/feedback_linker/templates/account/verified_email_required.html @@ -0,0 +1,30 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %} + {% + translate "Verify Your E-mail Address" %} +{% endblock head_title %} +{% block + inner %} +

{% translate "Verify Your E-mail Address" %}

+ {% url 'account_email' as email_url %} +

+ {% blocktranslate %}This part of the site requires us to verify that you are + who you claim to be. For this purpose, we require that you verify ownership of + your e-mail address. {% endblocktranslate %} +

+

+ {% blocktranslate %}We have sent an e-mail to you for verification. Please + click on the link inside this e-mail. Please contact us if you do not receive + it within a few minutes.{% endblocktranslate %} +

+

+ {% blocktranslate %} +Note: you can still +change your e-mail address. +{% endblocktranslate +%} +

+{% endblock inner %} diff --git a/src/feedback_linker/templates/base.html b/src/feedback_linker/templates/base.html index 09cea92..c1c287d 100644 --- a/src/feedback_linker/templates/base.html +++ b/src/feedback_linker/templates/base.html @@ -1,42 +1,126 @@ - - - - - - Feedback-Linker - - - - - - -
- -
- -
{% block content %}{% endblock %}
+{% load static i18n compress %} -
- -
- - - + +{% get_current_language as LANGUAGE_CODE %} + + + + + + {% block title %} + Feedback-Linker + {% endblock title %} + + + + + + {% block css %} + + + + + {% compress css %} + + {% endcompress %} + {% endblock css %} + + {# Placed at the top of the document so pages load faster with defer #} {% + block javascript %} + + + + + {% compress js %} + + {% endcompress %} + {% endblock javascript %} + + +
+ +
+
+ {% if messages %} + {% for message in messages %} +
+ {{ message }} + +
+ {% endfor %} + {% endif %} + {% block content %} +

Use this document as a way to quick start any new project.

+ {% endblock content %} +
+ + {% block modal %} + {% endblock modal %} + {% block inline_javascript %} + {% + comment %} Script tags with only code, no src (defer by default). To run + with a "defer" so that you run inline code: + + {% endcomment %} + {% endblock inline_javascript %} + diff --git a/src/feedback_linker/templates/index.html b/src/feedback_linker/templates/index.html deleted file mode 100644 index 541f6cb..0000000 --- a/src/feedback_linker/templates/index.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "base.html" %} {% block content %} -

Welcome to Feedback-Linker

-

This is the landing page.

-{% endblock %} diff --git a/src/feedback_linker/templates/pages/about.html b/src/feedback_linker/templates/pages/about.html new file mode 100644 index 0000000..94d9808 --- /dev/null +++ b/src/feedback_linker/templates/pages/about.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/src/feedback_linker/templates/pages/home.html b/src/feedback_linker/templates/pages/home.html new file mode 100644 index 0000000..94d9808 --- /dev/null +++ b/src/feedback_linker/templates/pages/home.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/src/feedback_linker/templates/projects.html b/src/feedback_linker/templates/projects.html deleted file mode 100644 index a1d37eb..0000000 --- a/src/feedback_linker/templates/projects.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends 'base.html' %} {% block content %} -
-

Create Project

-
- {{ form.hidden_tag() }} -
- {{ form.name.label }} {{ form.name(class="form-control") }} -
-
{{ form.submit(class="btn btn-primary") }}
-
-
-{% endblock %} diff --git a/src/feedback_linker/templates/users/user_detail.html b/src/feedback_linker/templates/users/user_detail.html new file mode 100644 index 0000000..f0f1fd4 --- /dev/null +++ b/src/feedback_linker/templates/users/user_detail.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% load static %} + +{% block title %} + User: {{ + object.name }} +{% endblock title %} +{% block content %} +
+
+
+

{{ object.name }}

+
+
+ {% if object == request.user %} + +
+
+ My Info + E-Mail + +
+
+ + {% endif %} +
+{% endblock content %} diff --git a/src/feedback_linker/templates/users/user_form.html b/src/feedback_linker/templates/users/user_form.html new file mode 100644 index 0000000..b8fb8b7 --- /dev/null +++ b/src/feedback_linker/templates/users/user_form.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% load crispy_forms_tags %} + +{% block title %} + {{ + user.name }} +{% endblock title %} +{% block content %} +

{{ user.name }}

+
+ {% csrf_token %} {{ form|crispy }} +
+
+ +
+
+
+{% endblock content %} diff --git a/src/feedback_linker/users/__init__.py b/src/feedback_linker/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/feedback_linker/users/adapters.py b/src/feedback_linker/users/adapters.py new file mode 100644 index 0000000..1ccd2c9 --- /dev/null +++ b/src/feedback_linker/users/adapters.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import typing + +from allauth.account.adapter import DefaultAccountAdapter +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter +from django.conf import settings + +if typing.TYPE_CHECKING: + from allauth.socialaccount.models import SocialLogin + from django.http import HttpRequest + + from feedback_linker.users.models import User + + +class AccountAdapter(DefaultAccountAdapter): + def is_open_for_signup(self, request: HttpRequest) -> bool: + return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True) + + +class SocialAccountAdapter(DefaultSocialAccountAdapter): + def is_open_for_signup( + self, + request: HttpRequest, + sociallogin: SocialLogin, + ) -> bool: + return getattr(settings, 'ACCOUNT_ALLOW_REGISTRATION', True) + + def populate_user( + self, + request: HttpRequest, + sociallogin: SocialLogin, + data: dict[str, typing.Any], + ) -> User: + """ + Populates user information from social provider info. + + See: https://docs.allauth.org/en/latest/socialaccount/advanced.html#creating-and-populating-user-instances + """ + user = super().populate_user(request, sociallogin, data) + if not user.name: + if name := data.get('name'): + user.name = name + elif first_name := data.get('first_name'): + user.name = first_name + if last_name := data.get('last_name'): + user.name += f' {last_name}' + return user diff --git a/src/feedback_linker/users/admin.py b/src/feedback_linker/users/admin.py new file mode 100644 index 0000000..960e38c --- /dev/null +++ b/src/feedback_linker/users/admin.py @@ -0,0 +1,53 @@ +from django.conf import settings +from django.contrib import admin +from django.contrib.auth import admin as auth_admin +from django.contrib.auth import decorators, get_user_model +from django.utils.translation import gettext_lazy as _ + +from feedback_linker.users.forms import ( + UserAdminChangeForm, + UserAdminCreationForm, +) + +User = get_user_model() + +if settings.DJANGO_ADMIN_FORCE_ALLAUTH: + # Force the `admin` sign in process to go through the `django-allauth` + # workflow: + # https://docs.allauth.org/en/latest/common/admin.html#admin + admin.site.login = decorators.login_required(admin.site.login) # type: ignore[method-assign] + + +@admin.register(User) +class UserAdmin(auth_admin.UserAdmin): + form = UserAdminChangeForm + add_form = UserAdminCreationForm + fieldsets = ( + (None, {'fields': ('email', 'password')}), + (_('Personal info'), {'fields': ('name',)}), + ( + _('Permissions'), + { + 'fields': ( + 'is_active', + 'is_staff', + 'is_superuser', + 'groups', + 'user_permissions', + ), + }, + ), + (_('Important dates'), {'fields': ('last_login', 'date_joined')}), + ) + list_display = ['email', 'name', 'is_superuser'] + search_fields = ['name'] + ordering = ['id'] + add_fieldsets = ( + ( + None, + { + 'classes': ('wide',), + 'fields': ('email', 'password1', 'password2'), + }, + ), + ) diff --git a/src/feedback_linker/users/api/__init__.py b/src/feedback_linker/users/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/feedback_linker/users/api/serializers.py b/src/feedback_linker/users/api/serializers.py new file mode 100644 index 0000000..e8b09ed --- /dev/null +++ b/src/feedback_linker/users/api/serializers.py @@ -0,0 +1,16 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers + +from feedback_linker.users.models import User as UserType + +User = get_user_model() + + +class UserSerializer(serializers.ModelSerializer[UserType]): + class Meta: + model = User + fields = ['name', 'url'] + + extra_kwargs = { + 'url': {'view_name': 'api:user-detail', 'lookup_field': 'pk'}, + } diff --git a/src/feedback_linker/users/api/views.py b/src/feedback_linker/users/api/views.py new file mode 100644 index 0000000..fa9b692 --- /dev/null +++ b/src/feedback_linker/users/api/views.py @@ -0,0 +1,35 @@ +from django.contrib.auth import get_user_model +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.mixins import ( + ListModelMixin, + RetrieveModelMixin, + UpdateModelMixin, +) +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet + +from .serializers import UserSerializer + +User = get_user_model() + + +class UserViewSet( + RetrieveModelMixin, + ListModelMixin, + UpdateModelMixin, + GenericViewSet, +): + serializer_class = UserSerializer + queryset = User.objects.all() + lookup_field = 'pk' + + def get_queryset(self, *args, **kwargs): + assert isinstance(self.request.user.id, int) + return self.queryset.filter(id=self.request.user.id) + + @action(detail=False) + def me(self, request: Request) -> Response: + serializer = UserSerializer(request.user, context={'request': request}) + return Response(status=status.HTTP_200_OK, data=serializer.data) diff --git a/src/feedback_linker/users/apps.py b/src/feedback_linker/users/apps.py new file mode 100644 index 0000000..6e84c83 --- /dev/null +++ b/src/feedback_linker/users/apps.py @@ -0,0 +1,13 @@ +import contextlib + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class UsersConfig(AppConfig): + name = 'feedback_linker.users' + verbose_name = _('Users') + + def ready(self): + with contextlib.suppress(ImportError): + import feedback_linker.users.signals # noqa: F401 diff --git a/src/feedback_linker/users/context_processors.py b/src/feedback_linker/users/context_processors.py new file mode 100644 index 0000000..2ab5254 --- /dev/null +++ b/src/feedback_linker/users/context_processors.py @@ -0,0 +1,8 @@ +from django.conf import settings + + +def allauth_settings(request): + """Expose some settings from django-allauth in templates.""" + return { + 'ACCOUNT_ALLOW_REGISTRATION': settings.ACCOUNT_ALLOW_REGISTRATION, + } diff --git a/src/feedback_linker/users/forms.py b/src/feedback_linker/users/forms.py new file mode 100644 index 0000000..1a9d6f2 --- /dev/null +++ b/src/feedback_linker/users/forms.py @@ -0,0 +1,45 @@ +from allauth.account.forms import SignupForm +from allauth.socialaccount.forms import SignupForm as SocialSignupForm +from django.contrib.auth import forms as admin_forms +from django.contrib.auth import get_user_model +from django.forms import EmailField +from django.utils.translation import gettext_lazy as _ + +User = get_user_model() + + +class UserAdminChangeForm(admin_forms.UserChangeForm): + class Meta(admin_forms.UserChangeForm.Meta): + model = User + field_classes = {'email': EmailField} + + +class UserAdminCreationForm(admin_forms.UserCreationForm): + """ + Form for User Creation in the Admin Area. + To change user signup, see UserSignupForm and UserSocialSignupForm. + """ + + class Meta(admin_forms.UserCreationForm.Meta): + model = User + fields = ('email',) + field_classes = {'email': EmailField} + error_messages = { + 'email': {'unique': _('This email has already been taken.')}, + } + + +class UserSignupForm(SignupForm): + """ + Form that will be rendered on a user sign up section/screen. + Default fields will be added automatically. + Check UserSocialSignupForm for accounts created from social. + """ + + +class UserSocialSignupForm(SocialSignupForm): + """ + Renders the form when user has signed up using social accounts. + Default fields will be added automatically. + See UserSignupForm otherwise. + """ diff --git a/src/feedback_linker/users/managers.py b/src/feedback_linker/users/managers.py new file mode 100644 index 0000000..aed72bf --- /dev/null +++ b/src/feedback_linker/users/managers.py @@ -0,0 +1,57 @@ +# type: ignore +from __future__ import annotations + +from typing import TYPE_CHECKING + +from django.contrib.auth.hashers import make_password +from django.contrib.auth.models import UserManager as DjangoUserManager + +if TYPE_CHECKING: + from feedback_linker.users.models import User + + +class UserManager(DjangoUserManager['User']): + """Custom manager for the User model.""" + + def _create_user( + self, email: str, password: str | None, **extra_fields + ) -> User: + """ + Create and save a user with the given email and password. + """ + if not email: + msg = 'The given email must be set' + raise ValueError(msg) + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.password = make_password(password) + user.save(using=self._db) + return user + + def create_user( + self, + email: str, + password: str | None = None, + **extra_fields, + ): # type: ignore[override] + extra_fields.setdefault('is_staff', False) + extra_fields.setdefault('is_superuser', False) + return self._create_user(email, password, **extra_fields) + + def create_superuser( + self, + email: str, + password: str | None = None, + **extra_fields, + ): # type: ignore[override] + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + + if extra_fields.get('is_staff') is not True: + msg = 'Superuser must have is_staff=True.' + raise ValueError(msg) + if extra_fields.get('is_superuser') is not True: + msg = 'Superuser must have is_superuser=True.' + raise ValueError(msg) + + return self._create_user(email, password, **extra_fields) diff --git a/src/feedback_linker/users/migrations/0001_initial.py b/src/feedback_linker/users/migrations/0001_initial.py new file mode 100644 index 0000000..49db000 --- /dev/null +++ b/src/feedback_linker/users/migrations/0001_initial.py @@ -0,0 +1,112 @@ +import django.contrib.auth.models +import django.contrib.auth.validators +import django.utils.timezone +from django.db import migrations +from django.db import models + +import feedback_linker.users.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ] + + operations = [ + migrations.CreateModel( + name="User", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login", + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "email", + models.EmailField( + unique=True, max_length=254, verbose_name="email address", + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined", + ), + ), + ( + "name", + models.CharField( + blank=True, max_length=255, verbose_name="Name of User", + ), + ), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), + ), + ], + options={ + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, + }, + managers=[ + ("objects", feedback_linker.users.models.UserManager()), + ], + ), + ] diff --git a/src/feedback_linker/users/migrations/__init__.py b/src/feedback_linker/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/feedback_linker/users/models.py b/src/feedback_linker/users/models.py new file mode 100644 index 0000000..c3508ea --- /dev/null +++ b/src/feedback_linker/users/models.py @@ -0,0 +1,38 @@ +from typing import ClassVar + +from django.contrib.auth.models import AbstractUser +from django.db.models import CharField, EmailField +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from feedback_linker.users.managers import UserManager + + +class User(AbstractUser): + """ + Default custom user model for Feedback-Linker. + If adding fields that need to be filled at user signup, + check forms.SignupForm and forms.SocialSignupForms accordingly. + """ + + # First and last name do not cover name patterns around the globe + name = CharField(_('Name of User'), blank=True, max_length=255) + first_name = None # type: ignore[assignment] + last_name = None # type: ignore[assignment] + email = EmailField(_('email address'), unique=True) + username = None # type: ignore[assignment] + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] + + objects: ClassVar[UserManager] = UserManager() + + def get_absolute_url(self) -> str: + """Get URL for user's detail view. + + Returns + ------- + str: URL for user detail. + + """ + return reverse('users:detail', kwargs={'pk': self.id}) diff --git a/src/feedback_linker/users/tests/__init__.py b/src/feedback_linker/users/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/feedback_linker/users/tests/factories.py b/src/feedback_linker/users/tests/factories.py new file mode 100644 index 0000000..1422675 --- /dev/null +++ b/src/feedback_linker/users/tests/factories.py @@ -0,0 +1,40 @@ +from collections.abc import Sequence +from typing import Any + +from django.contrib.auth import get_user_model +from factory import Faker, post_generation +from factory.django import DjangoModelFactory + + +class UserFactory(DjangoModelFactory): + email = Faker('email') + name = Faker('name') + + @post_generation + def password( + self, create: bool, extracted: Sequence[Any], **kwargs + ) -> None: + password = ( + extracted + if extracted + else Faker( + 'password', + length=42, + special_chars=True, + digits=True, + upper_case=True, + lower_case=True, + ).evaluate(None, None, extra={'locale': None}) + ) + self.set_password(password) + + @classmethod + def _after_postgeneration(cls, instance, create, results=None) -> None: + """Save again the instance if creating and at least one hook ran.""" + if create and results and not cls._meta.skip_postgeneration_save: + # Some post-generation hooks ran, and may have modified us. + instance.save() + + class Meta: + model = get_user_model() + django_get_or_create = ['email'] diff --git a/src/feedback_linker/users/tests/test_admin.py b/src/feedback_linker/users/tests/test_admin.py new file mode 100644 index 0000000..348a397 --- /dev/null +++ b/src/feedback_linker/users/tests/test_admin.py @@ -0,0 +1,68 @@ +# type: ignore +import contextlib + +from http import HTTPStatus +from importlib import reload + +import pytest + +from django.contrib import admin +from django.contrib.auth.models import AnonymousUser +from django.urls import reverse +from pytest_django.asserts import assertRedirects + +from feedback_linker.users.models import User + + +class TestUserAdmin: + def test_changelist(self, admin_client) -> None: + url = reverse('admin:users_user_changelist') + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + def test_search(self, admin_client) -> None: + url = reverse('admin:users_user_changelist') + response = admin_client.get(url, data={'q': 'test'}) + assert response.status_code == HTTPStatus.OK + + def test_add(self, admin_client) -> None: + url = reverse('admin:users_user_add') + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + response = admin_client.post( + url, + data={ + 'email': 'new-admin@example.com', + 'password1': 'My_R@ndom-P@ssw0rd', + 'password2': 'My_R@ndom-P@ssw0rd', + }, + ) + assert response.status_code == HTTPStatus.FOUND + assert User.objects.filter(email='new-admin@example.com').exists() + + def test_view_user(self, admin_client) -> None: + user = User.objects.get(email='admin@example.com') + url = reverse('admin:users_user_change', kwargs={'object_id': user.pk}) + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + @pytest.fixture() + def _force_allauth(self, settings) -> None: + settings.DJANGO_ADMIN_FORCE_ALLAUTH = True + # Reload the admin module to apply the setting change + import feedback_linker.users.admin as users_admin + + with contextlib.suppress(admin.sites.AlreadyRegistered): + reload(users_admin) + + @pytest.mark.django_db() + @pytest.mark.usefixtures('_force_allauth') + def test_allauth_login(self, rf, settings) -> None: + request = rf.get('/fake-url') + request.user = AnonymousUser() + response = admin.site.login(request) + + # The `admin` login view should redirect to the `allauth` login view + target_url = reverse(settings.LOGIN_URL) + '?next=' + request.path + assertRedirects(response, target_url, fetch_redirect_response=False) diff --git a/src/feedback_linker/users/tests/test_drf_urls.py b/src/feedback_linker/users/tests/test_drf_urls.py new file mode 100644 index 0000000..46ad55a --- /dev/null +++ b/src/feedback_linker/users/tests/test_drf_urls.py @@ -0,0 +1,21 @@ +from django.urls import resolve, reverse + +from feedback_linker.users.models import User + + +def test_user_detail(user: User) -> None: + assert ( + reverse('api:user-detail', kwargs={'pk': user.pk}) + == f'/api/users/{user.pk}/' + ) + assert resolve(f'/api/users/{user.pk}/').view_name == 'api:user-detail' + + +def test_user_list() -> None: + assert reverse('api:user-list') == '/api/users/' + assert resolve('/api/users/').view_name == 'api:user-list' + + +def test_user_me() -> None: + assert reverse('api:user-me') == '/api/users/me/' + assert resolve('/api/users/me/').view_name == 'api:user-me' diff --git a/src/feedback_linker/users/tests/test_drf_views.py b/src/feedback_linker/users/tests/test_drf_views.py new file mode 100644 index 0000000..8f53164 --- /dev/null +++ b/src/feedback_linker/users/tests/test_drf_views.py @@ -0,0 +1,36 @@ +# type: ignore +import pytest + +from rest_framework.test import APIRequestFactory + +from feedback_linker.users.api.views import UserViewSet +from feedback_linker.users.models import User + + +class TestUserViewSet: + @pytest.fixture() + def api_rf(self) -> APIRequestFactory: + return APIRequestFactory() + + def test_get_queryset(self, user: User, api_rf: APIRequestFactory) -> None: + view = UserViewSet() + request = api_rf.get('/fake-url/') + request.user = user + + view.request = request + + assert user in view.get_queryset() + + def test_me(self, user: User, api_rf: APIRequestFactory) -> None: + view = UserViewSet() + request = api_rf.get('/fake-url/') + request.user = user + + view.request = request + + response = view.me(request) # type: ignore[call-arg, arg-type, misc] + + assert response.data == { + 'url': f'http://testserver/api/users/{user.pk}/', + 'name': user.name, + } diff --git a/src/feedback_linker/users/tests/test_forms.py b/src/feedback_linker/users/tests/test_forms.py new file mode 100644 index 0000000..8934b3e --- /dev/null +++ b/src/feedback_linker/users/tests/test_forms.py @@ -0,0 +1,37 @@ +"""Module for all Form Tests.""" + +from django.utils.translation import gettext_lazy as _ + +from feedback_linker.users.forms import UserAdminCreationForm +from feedback_linker.users.models import User + + +class TestUserAdminCreationForm: + """ + Test class for all tests related to the UserAdminCreationForm + """ + + def test_username_validation_error_msg(self, user: User) -> None: + """ + Tests UserAdminCreation Form's unique validator functions correctly by + testing: + 1) A new user with an existing username cannot be added. + 2) Only 1 error is raised by the UserCreation Form + 3) The desired error message is raised + """ + # The user already exists, + # hence cannot be created. + form = UserAdminCreationForm( + { + 'email': user.email, + 'password1': user.password, + 'password2': user.password, + }, + ) + + assert not form.is_valid() + assert len(form.errors) == 1 + assert 'email' in form.errors + assert form.errors['email'][0] == _( + 'This email has already been taken.', + ) diff --git a/src/feedback_linker/users/tests/test_managers.py b/src/feedback_linker/users/tests/test_managers.py new file mode 100644 index 0000000..e24045e --- /dev/null +++ b/src/feedback_linker/users/tests/test_managers.py @@ -0,0 +1,56 @@ +from io import StringIO + +import pytest + +from django.core.management import call_command + +from feedback_linker.users.models import User + + +@pytest.mark.django_db() +class TestUserManager: + def test_create_user(self) -> None: + user = User.objects.create_user( + email='john@example.com', + password='something-r@nd0m!', # noqa: S106 + ) + assert user.email == 'john@example.com' + assert not user.is_staff + assert not user.is_superuser + assert user.check_password('something-r@nd0m!') + assert user.username is None + + def test_create_superuser(self) -> None: + user = User.objects.create_superuser( + email='admin@example.com', + password='something-r@nd0m!', # noqa: S106 + ) + assert user.email == 'admin@example.com' + assert user.is_staff + assert user.is_superuser + assert user.username is None + + def test_create_superuser_username_ignored(self) -> None: + user = User.objects.create_superuser( + email='test@example.com', + password='something-r@nd0m!', # noqa: S106 + ) + assert user.username is None + + +@pytest.mark.django_db() +def test_createsuperuser_command() -> None: + """Ensure createsuperuser command works with our custom manager.""" + out = StringIO() + command_result = call_command( + 'createsuperuser', + '--email', + 'henry@example.com', + interactive=False, + stdout=out, + ) + + assert command_result is None + assert out.getvalue() == 'Superuser created successfully.\n' + user = User.objects.get(email='henry@example.com') + assert not user.has_usable_password() diff --git a/src/feedback_linker/users/tests/test_models.py b/src/feedback_linker/users/tests/test_models.py new file mode 100644 index 0000000..3b8f7bc --- /dev/null +++ b/src/feedback_linker/users/tests/test_models.py @@ -0,0 +1,5 @@ +from feedback_linker.users.models import User + + +def test_user_get_absolute_url(user: User): + assert user.get_absolute_url() == f'/users/{user.pk}/' diff --git a/src/feedback_linker/users/tests/test_swagger.py b/src/feedback_linker/users/tests/test_swagger.py new file mode 100644 index 0000000..4bdf34f --- /dev/null +++ b/src/feedback_linker/users/tests/test_swagger.py @@ -0,0 +1,25 @@ +# type: ignore +from http import HTTPStatus + +import pytest + +from django.urls import reverse + + +def test_swagger_accessible_by_admin(admin_client) -> None: + url = reverse('api-docs') + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + +@pytest.mark.django_db() +def test_swagger_ui_not_accessible_by_normal_user(client) -> None: + url = reverse('api-docs') + response = client.get(url) + assert response.status_code == HTTPStatus.FORBIDDEN + + +def test_api_schema_generated_successfully(admin_client) -> None: + url = reverse('api-schema') + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK diff --git a/src/feedback_linker/users/tests/test_urls.py b/src/feedback_linker/users/tests/test_urls.py new file mode 100644 index 0000000..72902cf --- /dev/null +++ b/src/feedback_linker/users/tests/test_urls.py @@ -0,0 +1,20 @@ +from django.urls import resolve, reverse + +from feedback_linker.users.models import User + + +def test_detail(user: User) -> None: + assert ( + reverse('users:detail', kwargs={'pk': user.pk}) == f'/users/{user.pk}/' + ) + assert resolve(f'/users/{user.pk}/').view_name == 'users:detail' + + +def test_update() -> None: + assert reverse('users:update') == '/users/~update/' + assert resolve('/users/~update/').view_name == 'users:update' + + +def test_redirect() -> None: + assert reverse('users:redirect') == '/users/~redirect/' + assert resolve('/users/~redirect/').view_name == 'users:redirect' diff --git a/src/feedback_linker/users/tests/test_views.py b/src/feedback_linker/users/tests/test_views.py new file mode 100644 index 0000000..04fe98f --- /dev/null +++ b/src/feedback_linker/users/tests/test_views.py @@ -0,0 +1,104 @@ +# type: ignore +from http import HTTPStatus + +import pytest + +from django.conf import settings +from django.contrib import messages +from django.contrib.auth.models import AnonymousUser +from django.contrib.messages.middleware import MessageMiddleware +from django.contrib.sessions.middleware import SessionMiddleware +from django.http import HttpRequest, HttpResponseRedirect +from django.test import RequestFactory +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from feedback_linker.users.forms import UserAdminChangeForm +from feedback_linker.users.models import User +from feedback_linker.users.tests.factories import UserFactory +from feedback_linker.users.views import ( + UserRedirectView, + UserUpdateView, + user_detail_view, +) + +pytestmark = pytest.mark.django_db + + +class TestUserUpdateView: + """ + TODO: + extracting view initialization code as class-scoped fixture + would be great if only pytest-django supported non-function-scoped + fixture db access -- this is a work-in-progress for now: + https://github.com/pytest-dev/pytest-django/pull/258 + """ + + def dummy_get_response(self, request: HttpRequest) -> None: + return None + + def test_get_success_url(self, user: User, rf: RequestFactory) -> None: + view = UserUpdateView() + request = rf.get('/fake-url/') + request.user = user + + view.request = request + assert view.get_success_url() == f'/users/{user.pk}/' + + def test_get_object(self, user: User, rf: RequestFactory) -> None: + view = UserUpdateView() + request = rf.get('/fake-url/') + request.user = user + + view.request = request + + assert view.get_object() == user + + def test_form_valid(self, user: User, rf: RequestFactory) -> None: + view = UserUpdateView() + request = rf.get('/fake-url/') + + # Add the session/message middleware to the request + SessionMiddleware(self.dummy_get_response).process_request(request) + MessageMiddleware(self.dummy_get_response).process_request(request) + request.user = user + + view.request = request + + # Initialize the form + form = UserAdminChangeForm() + form.cleaned_data = {} + form.instance = user + view.form_valid(form) + + messages_sent = [m.message for m in messages.get_messages(request)] + assert messages_sent == [_('Information successfully updated')] + + +class TestUserRedirectView: + def test_get_redirect_url(self, user: User, rf: RequestFactory) -> None: + view = UserRedirectView() + request = rf.get('/fake-url') + request.user = user + + view.request = request + assert view.get_redirect_url() == f'/users/{user.pk}/' + + +class TestUserDetailView: + def test_authenticated(self, user: User, rf: RequestFactory) -> None: + request = rf.get('/fake-url/') + request.user = UserFactory() + response = user_detail_view(request, pk=user.pk) + + assert response.status_code == HTTPStatus.OK + + def test_not_authenticated(self, user: User, rf: RequestFactory) -> None: + request = rf.get('/fake-url/') + request.user = AnonymousUser() + response = user_detail_view(request, pk=user.pk) + login_url = reverse(settings.LOGIN_URL) + + assert isinstance(response, HttpResponseRedirect) + assert response.status_code == HTTPStatus.FOUND + assert response.url == f'{login_url}?next=/fake-url/' diff --git a/src/feedback_linker/users/urls.py b/src/feedback_linker/users/urls.py new file mode 100644 index 0000000..ed8c6ac --- /dev/null +++ b/src/feedback_linker/users/urls.py @@ -0,0 +1,14 @@ +from django.urls import path + +from feedback_linker.users.views import ( + user_detail_view, + user_redirect_view, + user_update_view, +) + +app_name = 'users' +urlpatterns = [ + path('~redirect/', view=user_redirect_view, name='redirect'), + path('~update/', view=user_update_view, name='update'), + path('/', view=user_detail_view, name='detail'), +] diff --git a/src/feedback_linker/users/views.py b/src/feedback_linker/users/views.py new file mode 100644 index 0000000..1d64cc6 --- /dev/null +++ b/src/feedback_linker/users/views.py @@ -0,0 +1,44 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django.views.generic import DetailView, RedirectView, UpdateView + +User = get_user_model() + + +class UserDetailView(LoginRequiredMixin, DetailView): + model = User + slug_field = 'id' + slug_url_kwarg = 'id' + + +user_detail_view = UserDetailView.as_view() + + +class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): + model = User + fields = ['name'] + success_message = _('Information successfully updated') + + def get_success_url(self): + # for mypy to know that the user is authenticated + assert self.request.user.is_authenticated + return self.request.user.get_absolute_url() + + def get_object(self): + return self.request.user + + +user_update_view = UserUpdateView.as_view() + + +class UserRedirectView(LoginRequiredMixin, RedirectView): + permanent = False + + def get_redirect_url(self): + return reverse('users:detail', kwargs={'pk': self.request.user.pk}) + + +user_redirect_view = UserRedirectView.as_view() diff --git a/src/locale/README.md b/src/locale/README.md new file mode 100644 index 0000000..6cc53dc --- /dev/null +++ b/src/locale/README.md @@ -0,0 +1,46 @@ +# Translations + +Start by configuring the `LANGUAGES` settings in `base.py`, by uncommenting +languages you are willing to support. Then, translations strings will be placed +in this folder when running: + +```bash +docker compose -f local.yml run --rm django python manage.py makemessages -all --no-location +``` + +This should generate `django.po` (stands for Portable Object) files under each +locale `/LC_MESSAGES/django.po`. Each translatable string in the +codebase is collected with its `msgid` and need to be translated as `msgstr`, +for example: + +```po +msgid "users" +msgstr "utilisateurs" +``` + +Once all translations are done, they need to be compiled into `.mo` files +(stands for Machine Object), which are the actual binary files used by the +application: + +```bash +docker compose -f local.yml run --rm django python manage.py compilemessages +``` + +Note that the `.po` files are NOT used by the application directly, so if the +`.mo` files are out of dates, the content won't appear as translated even if the +`.po` files are up-to-date. + +## Production + +The production image runs `compilemessages` automatically at build time, so as +long as your translated source files (PO) are up-to-date, you're good to go. + +## Add a new language + +1. Update the + [`LANGUAGES` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-LANGUAGES) + to your project's base settings. +2. Create the locale folder for the language next to this file, e.g. `fr_FR` for + French. Make sure the case is correct. +3. Run `makemessages` (as instructed above) to generate the PO files for the new + language. diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po new file mode 100644 index 0000000..a9da5f2 --- /dev/null +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -0,0 +1,12 @@ +# Translations for the Feedback-Linker project +# Copyright (C) 2024 Ivan Ogasawara +# Ivan Ogasawara , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0\n" +"Language: en-US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" diff --git a/src/locale/fr_FR/LC_MESSAGES/django.po b/src/locale/fr_FR/LC_MESSAGES/django.po new file mode 100644 index 0000000..d53c6f0 --- /dev/null +++ b/src/locale/fr_FR/LC_MESSAGES/django.po @@ -0,0 +1,335 @@ +# Translations for the Feedback-Linker project +# Copyright (C) 2024 Ivan Ogasawara +# Ivan Ogasawara , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0\n" +"Language: fr-FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +#: feedback_linker/templates/account/account_inactive.html:5 +#: feedback_linker/templates/account/account_inactive.html:8 +msgid "Account Inactive" +msgstr "Compte inactif" + +#: feedback_linker/templates/account/account_inactive.html:10 +msgid "This account is inactive." +msgstr "Ce compte est inactif." + +#: feedback_linker/templates/account/email.html:7 +msgid "Account" +msgstr "Compte" + +#: feedback_linker/templates/account/email.html:10 +msgid "E-mail Addresses" +msgstr "Adresses e-mail" + +#: feedback_linker/templates/account/email.html:13 +msgid "The following e-mail addresses are associated with your account:" +msgstr "Les adresses e-mail suivantes sont associĂ©es Ă  votre compte :" + +#: feedback_linker/templates/account/email.html:27 +msgid "Verified" +msgstr "VĂ©rifiĂ©" + +#: feedback_linker/templates/account/email.html:29 +msgid "Unverified" +msgstr "Non vĂ©rifiĂ©" + +#: feedback_linker/templates/account/email.html:31 +msgid "Primary" +msgstr "Primaire" + +#: feedback_linker/templates/account/email.html:37 +msgid "Make Primary" +msgstr "Changer Primaire" + +#: feedback_linker/templates/account/email.html:38 +msgid "Re-send Verification" +msgstr "Renvoyer vĂ©rification" + +#: feedback_linker/templates/account/email.html:39 +msgid "Remove" +msgstr "Supprimer" + +#: feedback_linker/templates/account/email.html:46 +msgid "Warning:" +msgstr "Avertissement:" + +#: feedback_linker/templates/account/email.html:46 +msgid "" +"You currently do not have any e-mail address set up. You should really add " +"an e-mail address so you can receive notifications, reset your password, etc." +msgstr "" +"Vous n'avez actuellement aucune adresse e-mail configurĂ©e. Vous devriez ajouter " +"une adresse e-mail pour reçevoir des notifications, rĂ©initialiser votre mot " +"de passe, etc." + +#: feedback_linker/templates/account/email.html:51 +msgid "Add E-mail Address" +msgstr "Ajouter une adresse e-mail" + +#: feedback_linker/templates/account/email.html:56 +msgid "Add E-mail" +msgstr "Ajouter e-mail" + +#: feedback_linker/templates/account/email.html:66 +msgid "Do you really want to remove the selected e-mail address?" +msgstr "Voulez-vous vraiment supprimer l'adresse e-mail sĂ©lectionnĂ©e ?" + +#: feedback_linker/templates/account/email_confirm.html:6 +#: feedback_linker/templates/account/email_confirm.html:10 +msgid "Confirm E-mail Address" +msgstr "Confirmez votre adresse email" + +#: feedback_linker/templates/account/email_confirm.html:16 +#, python-format +msgid "" +"Please confirm that %(email)s is an e-mail " +"address for user %(user_display)s." +msgstr "" +"Veuillez confirmer que %(email)s est un e-mail " +"adresse de l'utilisateur %(user_display)s." + +#: feedback_linker/templates/account/email_confirm.html:20 +msgid "Confirm" +msgstr "Confirm" + +#: feedback_linker/templates/account/email_confirm.html:27 +#, python-format +msgid "" +"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request." +msgstr "" +"Ce lien de confirmation par e-mail a expirĂ© ou n'est pas valide. Veuillez" + "Ă©mettre une nouvelle demande de confirmation " +"par e-mail." + +#: feedback_linker/templates/account/login.html:7 +#: feedback_linker/templates/account/login.html:11 +#: feedback_linker/templates/account/login.html:56 +#: feedback_linker/templates/base.html:72 +msgid "Sign In" +msgstr "S'identifier" + +#: feedback_linker/templates/account/login.html:17 +msgid "Please sign in with one of your existing third party accounts:" +msgstr "Veuillez vous connecter avec l'un de vos comptes tiers existants :" + +#: feedback_linker/templates/account/login.html:19 +#, python-format +msgid "" +"Or, sign up for a %(site_name)s account and " +"sign in below:" +msgstr "" +"Ou, crĂ©ez un compte %(site_name)s et " +"connectez-vous ci-dessous :" + +#: feedback_linker/templates/account/login.html:32 +msgid "or" +msgstr "ou" + +#: feedback_linker/templates/account/login.html:41 +#, python-format +msgid "" +"If you have not created an account yet, then please sign up first." +msgstr "" +"Si vous n'avez pas encore crĂ©Ă© de compte, veuillez d'abord vous inscrire." + +#: feedback_linker/templates/account/login.html:55 +msgid "Forgot Password?" +msgstr "Mot de passe oubliĂ©?" + +#: feedback_linker/templates/account/logout.html:5 +#: feedback_linker/templates/account/logout.html:8 +#: feedback_linker/templates/account/logout.html:17 +#: feedback_linker/templates/base.html:61 +msgid "Sign Out" +msgstr "Se dĂ©connecter" + +#: feedback_linker/templates/account/logout.html:10 +msgid "Are you sure you want to sign out?" +msgstr "Êtes-vous certain de vouloir vous dĂ©connecter?" + +#: feedback_linker/templates/account/password_change.html:6 +#: feedback_linker/templates/account/password_change.html:9 +#: feedback_linker/templates/account/password_change.html:14 +#: feedback_linker/templates/account/password_reset_from_key.html:5 +#: feedback_linker/templates/account/password_reset_from_key.html:8 +#: feedback_linker/templates/account/password_reset_from_key_done.html:4 +#: feedback_linker/templates/account/password_reset_from_key_done.html:7 +msgid "Change Password" +msgstr "Changer le mot de passe" + +#: feedback_linker/templates/account/password_reset.html:7 +#: feedback_linker/templates/account/password_reset.html:11 +#: feedback_linker/templates/account/password_reset_done.html:6 +#: feedback_linker/templates/account/password_reset_done.html:9 +msgid "Password Reset" +msgstr "RĂ©initialisation du mot de passe" + +#: feedback_linker/templates/account/password_reset.html:16 +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll send you " +"an e-mail allowing you to reset it." +msgstr "" +"Mot de passe oubliĂ©? Entrez votre adresse e-mail ci-dessous, et nous vous " +"enverrons un e-mail vous permettant de le rĂ©initialiser." + +#: feedback_linker/templates/account/password_reset.html:21 +msgid "Reset My Password" +msgstr "RĂ©initialiser mon mot de passe" + +#: feedback_linker/templates/account/password_reset.html:24 +msgid "Please contact us if you have any trouble resetting your password." +msgstr "" +"Veuillez nous contacter si vous rencontrez des difficultĂ©s pour rĂ©initialiser" +"votre mot de passe." + +#: feedback_linker/templates/account/password_reset_done.html:15 +msgid "" +"We have sent you an e-mail. Please contact us if you do not receive it " +"within a few minutes." +msgstr "" +"Nous vous avons envoyĂ© un e-mail. Veuillez nous contacter si vous ne le " +"recevez pas d'ici quelques minutes." + +#: feedback_linker/templates/account/password_reset_from_key.html:8 +msgid "Bad Token" +msgstr "Token Invalide" + +#: feedback_linker/templates/account/password_reset_from_key.html:12 +#, python-format +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Le lien de rĂ©initialisation du mot de passe n'Ă©tait pas valide, peut-ĂȘtre parce " +"qu'il a dĂ©jĂ  Ă©tĂ© utilisĂ©. Veuillez faire une " +"nouvelle demande de rĂ©initialisation de mot de passe." + +#: feedback_linker/templates/account/password_reset_from_key.html:18 +msgid "change password" +msgstr "changer le mot de passe" + +#: feedback_linker/templates/account/password_reset_from_key.html:21 +#: feedback_linker/templates/account/password_reset_from_key_done.html:8 +msgid "Your password is now changed." +msgstr "Votre mot de passe est maintenant modifiĂ©." + +#: feedback_linker/templates/account/password_set.html:6 +#: feedback_linker/templates/account/password_set.html:9 +#: feedback_linker/templates/account/password_set.html:14 +msgid "Set Password" +msgstr "DĂ©finir le mot de passe" + +#: feedback_linker/templates/account/signup.html:6 +msgid "Signup" +msgstr "S'inscrire" + +#: feedback_linker/templates/account/signup.html:9 +#: feedback_linker/templates/account/signup.html:19 +#: feedback_linker/templates/base.html:67 +msgid "Sign Up" +msgstr "S'inscrire" + +#: feedback_linker/templates/account/signup.html:11 +#, python-format +msgid "" +"Already have an account? Then please sign in." +msgstr "" +"Vous avez dĂ©jĂ  un compte? Alors veuillez vous connecter." + +#: feedback_linker/templates/account/signup_closed.html:5 +#: feedback_linker/templates/account/signup_closed.html:8 +msgid "Sign Up Closed" +msgstr "Inscriptions closes" + +#: feedback_linker/templates/account/signup_closed.html:10 +msgid "We are sorry, but the sign up is currently closed." +msgstr "DĂ©solĂ©, mais l'inscription est actuellement fermĂ©e." + +#: feedback_linker/templates/account/verification_sent.html:5 +#: feedback_linker/templates/account/verification_sent.html:8 +#: feedback_linker/templates/account/verified_email_required.html:5 +#: feedback_linker/templates/account/verified_email_required.html:8 +msgid "Verify Your E-mail Address" +msgstr "VĂ©rifiez votre adresse e-mail" + +#: feedback_linker/templates/account/verification_sent.html:10 +msgid "" +"We have sent an e-mail to you for verification. Follow the link provided to " +"finalize the signup process. Please contact us if you do not receive it " +"within a few minutes." +msgstr "Nous vous avons envoyĂ© un e-mail pour vĂ©rification. Suivez le lien fourni " +"pour finalisez le processus d'inscription. Veuillez nous contacter si vous ne le " +"recevez pas d'ici quelques minutes." + +#: feedback_linker/templates/account/verified_email_required.html:12 +msgid "" +"This part of the site requires us to verify that\n" +"you are who you claim to be. For this purpose, we require that you\n" +"verify ownership of your e-mail address. " +msgstr "" +"Cette partie du site nous oblige Ă  vĂ©rifier que\n" +"vous ĂȘtes qui vous prĂ©tendez ĂȘtre. Nous vous demandons donc de\n" +"vĂ©rifier la propriĂ©tĂ© de votre adresse e-mail." + +#: feedback_linker/templates/account/verified_email_required.html:16 +msgid "" +"We have sent an e-mail to you for\n" +"verification. Please click on the link inside this e-mail. Please\n" +"contact us if you do not receive it within a few minutes." +msgstr "" +"Nous vous avons envoyĂ© un e-mail pour\n" +"vĂ©rification. Veuillez cliquer sur le lien contenu dans cet e-mail. Veuillez nous\n" +"contacter si vous ne le recevez pas d'ici quelques minutes." + +#: feedback_linker/templates/account/verified_email_required.html:20 +#, python-format +msgid "" +"Note: you can still change your e-" +"mail address." +msgstr "" +"Remarque : vous pouvez toujours changer votre e-" +"adresse e-mail." + +#: feedback_linker/templates/base.html:57 +msgid "My Profile" +msgstr "Mon Profil" + +#: feedback_linker/users/admin.py:17 +msgid "Personal info" +msgstr "Personal info" + +#: feedback_linker/users/admin.py:19 +msgid "Permissions" +msgstr "Permissions" + +#: feedback_linker/users/admin.py:30 +msgid "Important dates" +msgstr "Dates importantes" + +#: feedback_linker/users/apps.py:7 +msgid "Users" +msgstr "Utilisateurs" + +#: feedback_linker/users/forms.py:24 +#: feedback_linker/users/tests/test_forms.py:36 +msgid "This username has already been taken." +msgstr "Ce nom d'utilisateur est dĂ©jĂ  pris." + +#: feedback_linker/users/models.py:15 +msgid "Name of User" +msgstr "Nom de l'utilisateur" + +#: feedback_linker/users/views.py:23 +msgid "Information successfully updated" +msgstr "Informations mises Ă  jour avec succĂšs" diff --git a/src/locale/pt_BR/LC_MESSAGES/django.po b/src/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000..d0db143 --- /dev/null +++ b/src/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,315 @@ +# Translations for the Feedback-Linker project +# Copyright (C) 2024 Ivan Ogasawara +# Ivan Ogasawara , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0\n" +"Language: pt-BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +#: feedback_linker/templates/account/account_inactive.html:5 +#: feedback_linker/templates/account/account_inactive.html:8 +msgid "Account Inactive" +msgstr "Conta Inativa" + +#: feedback_linker/templates/account/account_inactive.html:10 +msgid "This account is inactive." +msgstr "Esta conta estĂĄ inativa." + +#: feedback_linker/templates/account/email.html:7 +msgid "Account" +msgstr "Conta" + +#: feedback_linker/templates/account/email.html:10 +msgid "E-mail Addresses" +msgstr "Endereços de E-mail" + +#: feedback_linker/templates/account/email.html:13 +msgid "The following e-mail addresses are associated with your account:" +msgstr "Os seguintes endereços de e-mail estĂŁo associados Ă  sua conta:" + +#: feedback_linker/templates/account/email.html:27 +msgid "Verified" +msgstr "Verificado" + +#: feedback_linker/templates/account/email.html:29 +msgid "Unverified" +msgstr "NĂŁo verificado" + +#: feedback_linker/templates/account/email.html:31 +msgid "Primary" +msgstr "PrimĂĄrio" + +#: feedback_linker/templates/account/email.html:37 +msgid "Make Primary" +msgstr "Tornar PrimĂĄrio" + +#: feedback_linker/templates/account/email.html:38 +msgid "Re-send Verification" +msgstr "Reenviar verificação" + +#: feedback_linker/templates/account/email.html:39 +msgid "Remove" +msgstr "Remover" + +#: feedback_linker/templates/account/email.html:46 +msgid "Warning:" +msgstr "Aviso:" + +#: feedback_linker/templates/account/email.html:46 +msgid "" +"You currently do not have any e-mail address set up. You should really add " +"an e-mail address so you can receive notifications, reset your password, etc." +msgstr "" +"No momento, vocĂȘ nĂŁo tem nenhum endereço de e-mail configurado. VocĂȘ " +"realmente deve adicionar um endereço de e-mail para receber notificaçÔes, " +"redefinir sua senha etc." + +#: feedback_linker/templates/account/email.html:51 +msgid "Add E-mail Address" +msgstr "Adicionar Endereço de E-mail" + +#: feedback_linker/templates/account/email.html:56 +msgid "Add E-mail" +msgstr "Adicionar E-mail" + +#: feedback_linker/templates/account/email.html:66 +msgid "Do you really want to remove the selected e-mail address?" +msgstr "VocĂȘ realmente deseja remover o endereço de e-mail selecionado?" + +#: feedback_linker/templates/account/email_confirm.html:6 +#: feedback_linker/templates/account/email_confirm.html:10 +msgid "Confirm E-mail Address" +msgstr "Confirme o endereço de e-mail" + +#: feedback_linker/templates/account/email_confirm.html:16 +#, python-format +msgid "" +"Please confirm that %(email)s is an e-mail " +"address for user %(user_display)s." +msgstr "" +"Confirme se %(email)s Ă© um endereço de " +"e-mail do usuĂĄrio %(user_display)s." + +#: feedback_linker/templates/account/email_confirm.html:20 +msgid "Confirm" +msgstr "Confirmar" + +#: feedback_linker/templates/account/email_confirm.html:27 +#, python-format +msgid "" +"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request." +msgstr "Este link de confirmação de e-mail expirou ou Ă© invĂĄlido. " +"Por favor, emita um novo pedido de confirmação por e-mail." + +#: feedback_linker/templates/account/login.html:7 +#: feedback_linker/templates/account/login.html:11 +#: feedback_linker/templates/account/login.html:56 +#: feedback_linker/templates/base.html:72 +msgid "Sign In" +msgstr "Entrar" + +#: feedback_linker/templates/account/login.html:17 +msgid "Please sign in with one of your existing third party accounts:" +msgstr "Faça login com uma de suas contas de terceiros existentes:" + +#: feedback_linker/templates/account/login.html:19 +#, python-format +msgid "" +"Or, sign up for a %(site_name)s account and " +"sign in below:" +msgstr "Ou, cadastre-se para uma conta em %(site_name)s e entre abaixo:" + +#: feedback_linker/templates/account/login.html:32 +msgid "or" +msgstr "ou" + +#: feedback_linker/templates/account/login.html:41 +#, python-format +msgid "" +"If you have not created an account yet, then please sign up first." +msgstr "Se vocĂȘ ainda nĂŁo criou uma conta, registre-se primeiro." + +#: feedback_linker/templates/account/login.html:55 +msgid "Forgot Password?" +msgstr "Esqueceu sua senha?" + +#: feedback_linker/templates/account/logout.html:5 +#: feedback_linker/templates/account/logout.html:8 +#: feedback_linker/templates/account/logout.html:17 +#: feedback_linker/templates/base.html:61 +msgid "Sign Out" +msgstr "Sair" + +#: feedback_linker/templates/account/logout.html:10 +msgid "Are you sure you want to sign out?" +msgstr "VocĂȘ tem certeza que deseja sair?" + +#: feedback_linker/templates/account/password_change.html:6 +#: feedback_linker/templates/account/password_change.html:9 +#: feedback_linker/templates/account/password_change.html:14 +#: feedback_linker/templates/account/password_reset_from_key.html:5 +#: feedback_linker/templates/account/password_reset_from_key.html:8 +#: feedback_linker/templates/account/password_reset_from_key_done.html:4 +#: feedback_linker/templates/account/password_reset_from_key_done.html:7 +msgid "Change Password" +msgstr "Alterar Senha" + +#: feedback_linker/templates/account/password_reset.html:7 +#: feedback_linker/templates/account/password_reset.html:11 +#: feedback_linker/templates/account/password_reset_done.html:6 +#: feedback_linker/templates/account/password_reset_done.html:9 +msgid "Password Reset" +msgstr "Redefinição de senha" + +#: feedback_linker/templates/account/password_reset.html:16 +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll send you " +"an e-mail allowing you to reset it." +msgstr "Esqueceu sua senha? Digite seu endereço de e-mail abaixo e enviaremos um e-mail permitindo que vocĂȘ o redefina." + +#: feedback_linker/templates/account/password_reset.html:21 +msgid "Reset My Password" +msgstr "Redefinir minha senha" + +#: feedback_linker/templates/account/password_reset.html:24 +msgid "Please contact us if you have any trouble resetting your password." +msgstr "Entre em contato conosco se tiver algum problema para redefinir sua senha." + +#: feedback_linker/templates/account/password_reset_done.html:15 +msgid "" +"We have sent you an e-mail. Please contact us if you do not receive it " +"within a few minutes." +msgstr "Enviamos um e-mail para vocĂȘ. Entre em contato conosco se vocĂȘ nĂŁo recebĂȘ-lo dentro de alguns minutos." + +#: feedback_linker/templates/account/password_reset_from_key.html:8 +msgid "Bad Token" +msgstr "Token InvĂĄlido" + +#: feedback_linker/templates/account/password_reset_from_key.html:12 +#, python-format +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "O link de redefinição de senha era invĂĄlido, possivelmente porque jĂĄ foi usado. " +"Solicite uma nova redefinição de senha." + +#: feedback_linker/templates/account/password_reset_from_key.html:18 +msgid "change password" +msgstr "alterar senha" + +#: feedback_linker/templates/account/password_reset_from_key.html:21 +#: feedback_linker/templates/account/password_reset_from_key_done.html:8 +msgid "Your password is now changed." +msgstr "Sua senha agora foi alterada." + +#: feedback_linker/templates/account/password_set.html:6 +#: feedback_linker/templates/account/password_set.html:9 +#: feedback_linker/templates/account/password_set.html:14 +msgid "Set Password" +msgstr "Definir Senha" + +#: feedback_linker/templates/account/signup.html:6 +msgid "Signup" +msgstr "Cadastro" + +#: feedback_linker/templates/account/signup.html:9 +#: feedback_linker/templates/account/signup.html:19 +#: feedback_linker/templates/base.html:67 +msgid "Sign Up" +msgstr "Cadastro" + +#: feedback_linker/templates/account/signup.html:11 +#, python-format +msgid "" +"Already have an account? Then please sign in." +msgstr "jĂĄ tem uma conta? EntĂŁo, por favor, faça login." + +#: feedback_linker/templates/account/signup_closed.html:5 +#: feedback_linker/templates/account/signup_closed.html:8 +msgid "Sign Up Closed" +msgstr "InscriçÔes encerradas" + +#: feedback_linker/templates/account/signup_closed.html:10 +msgid "We are sorry, but the sign up is currently closed." +msgstr "Lamentamos, mas as inscriçÔes estĂŁo encerradas no momento." + +#: feedback_linker/templates/account/verification_sent.html:5 +#: feedback_linker/templates/account/verification_sent.html:8 +#: feedback_linker/templates/account/verified_email_required.html:5 +#: feedback_linker/templates/account/verified_email_required.html:8 +msgid "Verify Your E-mail Address" +msgstr "Verifique seu endereço de e-mail" + +#: feedback_linker/templates/account/verification_sent.html:10 +msgid "" +"We have sent an e-mail to you for verification. Follow the link provided to " +"finalize the signup process. Please contact us if you do not receive it " +"within a few minutes." +msgstr "Enviamos um e-mail para vocĂȘ para verificação. Siga o link fornecido para finalizar o processo de inscrição. Entre em contato conosco se vocĂȘ nĂŁo recebĂȘ-lo dentro de alguns minutos." + +#: feedback_linker/templates/account/verified_email_required.html:12 +msgid "" +"This part of the site requires us to verify that\n" +"you are who you claim to be. For this purpose, we require that you\n" +"verify ownership of your e-mail address. " +msgstr "Esta parte do site exige que verifiquemos se vocĂȘ Ă© quem afirma ser.\n" +"Para esse fim, exigimos que vocĂȘ verifique a propriedade\n" +"do seu endereço de e-mail." + +#: feedback_linker/templates/account/verified_email_required.html:16 +msgid "" +"We have sent an e-mail to you for\n" +"verification. Please click on the link inside this e-mail. Please\n" +"contact us if you do not receive it within a few minutes." +msgstr "Enviamos um e-mail para vocĂȘ para verificação.\n" +"Por favor, clique no link dentro deste e-mail.\n" +"Entre em contato conosco se vocĂȘ nĂŁo recebĂȘ-lo dentro de alguns minutos." + +#: feedback_linker/templates/account/verified_email_required.html:20 +#, python-format +msgid "" +"Note: you can still change your e-" +"mail address." +msgstr "Nota: vocĂȘ ainda pode alterar seu endereço de e-mail." + +#: feedback_linker/templates/base.html:57 +msgid "My Profile" +msgstr "Meu perfil" + +#: feedback_linker/users/admin.py:17 +msgid "Personal info" +msgstr "Informação pessoal" + +#: feedback_linker/users/admin.py:19 +msgid "Permissions" +msgstr "PermissĂ”es" + +#: feedback_linker/users/admin.py:30 +msgid "Important dates" +msgstr "Datas importantes" + +#: feedback_linker/users/apps.py:7 +msgid "Users" +msgstr "UsuĂĄrios" + +#: feedback_linker/users/forms.py:24 +#: feedback_linker/users/tests/test_forms.py:36 +msgid "This username has already been taken." +msgstr "Este nome de usuĂĄrio jĂĄ foi usado." + +#: feedback_linker/users/models.py:15 +msgid "Name of User" +msgstr "Nome do UsuĂĄrio" + +#: feedback_linker/users/views.py:23 +msgid "Information successfully updated" +msgstr "Informação atualizada com sucesso" diff --git a/src/manage.py b/src/manage.py new file mode 100755 index 0000000..d48af29 --- /dev/null +++ b/src/manage.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# ruff: noqa +import os +import sys +from pathlib import Path + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') + + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + 'available on your PYTHONPATH environment variable? Did you ' + 'forget to activate a virtual environment?' + ) + + raise + + # This allows easy placement of apps within the interior + # feedback_linker directory. + current_path = Path(__file__).parent.resolve() + sys.path.append(str(current_path / 'feedback_linker')) + + execute_from_command_line(sys.argv) diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 9e14800..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Unit test package for feedback-linker.""" diff --git a/tests/test_feedback_linker.py b/tests/test_feedback_linker.py deleted file mode 100644 index 00a0d81..0000000 --- a/tests/test_feedback_linker.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Tests for feedback_linker package.""" -import pytest - - -@pytest.fixture -def response_pytest() -> bool: - """Sample pytest fixture.""" - return True - - -def test_content_pytest() -> None: - """Test with pytest.""" - assert True From 67ff4d6d371880707ffcfec4870b5eaae043a251 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 15:01:31 -0400 Subject: [PATCH 06/12] add new modules --- src/feedback_linker/forms.py | 72 +++++++++++++++++++++++++++++++++ src/feedback_linker/models.py | 67 +++++++++++++++++++++++++++++++ src/feedback_linker/urls.py | 12 ++++++ src/feedback_linker/views.py | 75 +++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 src/feedback_linker/forms.py create mode 100644 src/feedback_linker/models.py create mode 100644 src/feedback_linker/urls.py create mode 100644 src/feedback_linker/views.py diff --git a/src/feedback_linker/forms.py b/src/feedback_linker/forms.py new file mode 100644 index 0000000..6375d0a --- /dev/null +++ b/src/feedback_linker/forms.py @@ -0,0 +1,72 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.models import User + +from .models import Feedback, Link, Profile, Project + + +class UserRegisterForm(UserCreationForm): + email = forms.EmailField() + # Add any additional fields for the profile here, if needed + + class Meta: + model = User + fields = ['username', 'email', 'password1', 'password2'] + + +class UserUpdateForm(forms.ModelForm): + email = forms.EmailField() + + class Meta: + model = User + fields = ['username', 'email'] + + +class ProfileUpdateForm(forms.ModelForm): + # Add fields for updating profile-specific information + class Meta: + model = Profile + fields = [] # Specify the fields you want to include, e.g., [] + + +class ProjectForm(forms.ModelForm): + class Meta: + model = Project + fields = [ + 'name', + 'supervisors', + ] # Adjust according to your Project model fields + + +class LinkForm(forms.ModelForm): + class Meta: + model = Link + fields = [ + 'person_one', + 'person_two', + 'supervisor', + 'periodicity', + 'times', + ] + widgets = { + 'person_one': forms.Select(attrs={'class': 'form-control'}), + 'person_two': forms.Select(attrs={'class': 'form-control'}), + 'supervisor': forms.Select(attrs={'class': 'form-control'}), + 'periodicity': forms.Select( + choices=Link.PERIODICITY_CHOICES, + attrs={'class': 'form-control'}, + ), + 'times': forms.NumberInput(attrs={'class': 'form-control'}), + } + + +class FeedbackForm(forms.ModelForm): + class Meta: + model = Feedback + fields = ['content', 'link'] + widgets = { + 'content': forms.Textarea( + attrs={'class': 'form-control', 'rows': 3} + ), + 'link': forms.Select(attrs={'class': 'form-control'}), + } diff --git a/src/feedback_linker/models.py b/src/feedback_linker/models.py new file mode 100644 index 0000000..d13cdc6 --- /dev/null +++ b/src/feedback_linker/models.py @@ -0,0 +1,67 @@ +from django.contrib.auth.models import User +from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver + + +class Profile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + projects = models.ManyToManyField('Project', related_name='participants') + # Add any additional fields for your Person model here + + def __str__(self): + return self.user.username + + +class Project(models.Model): + name = models.CharField(max_length=100) + # Assuming you want to keep a relation to track project supervisors + supervisors = models.ManyToManyField( + User, related_name='supervised_projects' + ) + + def __str__(self): + return self.name + + +class Link(models.Model): + person_one = models.ForeignKey( + User, on_delete=models.CASCADE, related_name='person_one_links' + ) + person_two = models.ForeignKey( + User, on_delete=models.CASCADE, related_name='person_two_links' + ) + supervisor = models.ForeignKey( + User, on_delete=models.CASCADE, related_name='supervised_links' + ) + periodicity = models.CharField(max_length=50) + times = models.IntegerField() + + def __str__(self): + return f'{self.person_one.username} <-> {self.person_two.username}' + + +class Feedback(models.Model): + content = models.TextField() + link = models.ForeignKey( + Link, on_delete=models.CASCADE, related_name='feedback' + ) + timestamp = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return ( + f'Feedback from {self.link.person_one.username} to ' + f'{self.link.person_two.username} on ' + f'{self.timestamp.strftime("%Y-%m-%d %H:%M:%S")}' + ) + + +@receiver(post_save, sender=User) +def create_user_profile(sender, instance, created, **kwargs): + if created: + Profile.objects.create(user=instance) + + +@receiver(post_save, sender=User) +def save_user_profile(sender, instance, **kwargs): + instance.profile.save() diff --git a/src/feedback_linker/urls.py b/src/feedback_linker/urls.py new file mode 100644 index 0000000..f08ba89 --- /dev/null +++ b/src/feedback_linker/urls.py @@ -0,0 +1,12 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('register/', views.user_register, name='register'), + path('projects/', views.project_list, name='project_list'), + path('project/create/', views.project_create, name='project_create'), + path('link/create/', views.link_create, name='link_create'), + path('feedback/create/', views.feedback_create, name='feedback_create'), + # Add other paths as needed +] diff --git a/src/feedback_linker/views.py b/src/feedback_linker/views.py new file mode 100644 index 0000000..878f34b --- /dev/null +++ b/src/feedback_linker/views.py @@ -0,0 +1,75 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import redirect, render + +from .forms import FeedbackForm, LinkForm, ProjectForm, UserRegistrationForm +from .models import Project + + +def user_register(request): + if request.method == 'POST': + user_form = UserRegistrationForm(request.POST) + if user_form.is_valid(): + # Create a new user object but avoid saving it yet + new_user = user_form.save(commit=False) + # Set the chosen password + new_user.set_password(user_form.cleaned_data['password']) + # Save the User object + new_user.save() + return render( + request, + 'registration/register_done.html', + {'new_user': new_user}, + ) + else: + user_form = UserRegistrationForm() + return render( + request, 'registration/register.html', {'user_form': user_form} + ) + + +@login_required +def project_list(request): + projects = Project.objects.all() + return render(request, 'project/list.html', {'projects': projects}) + + +@login_required +def project_create(request): + if request.method == 'POST': + form = ProjectForm(request.POST) + if form.is_valid(): + form.save() + return redirect('project_list') + else: + form = ProjectForm() + return render(request, 'project/create.html', {'form': form}) + + +# Define similar views for Link and Feedback + + +@login_required +def link_create(request): + if request.method == 'POST': + form = LinkForm(request.POST) + if form.is_valid(): + form.save() + return redirect('link_list') + else: + form = LinkForm() + return render(request, 'link/create.html', {'form': form}) + + +@login_required +def feedback_create(request): + if request.method == 'POST': + form = FeedbackForm(request.POST) + if form.is_valid(): + form.save() + return redirect('feedback_list') + else: + form = FeedbackForm() + return render(request, 'feedback/create.html', {'form': form}) + + +# Add views for listing Links and Feedbacks, and any other operations you need From 32cdab265d83959b570b0a3f37ea5ba71b489cee Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 18:19:03 -0400 Subject: [PATCH 07/12] rename project --- .envs/.local/.postgres | 2 +- .envs/.production/.django | 2 +- .envs/.production/.postgres | 2 +- .github/ISSUE_TEMPLATE/bug-report.yml | 14 +- .github/ISSUE_TEMPLATE/config.yml | 16 +- .../ISSUE_TEMPLATE/documentation-report.yml | 10 +- .github/ISSUE_TEMPLATE/feature-request.yml | 12 +- .github/workflows/main.yaml | 2 +- .github/workflows/release.yaml | 2 +- .pre-commit-config.yaml | 4 +- .releaserc.json | 6 +- README.md | 16 +- conda/dev.yaml | 2 +- containers/Dockerfile | 16 +- containers/compose.local.yml | 2 +- containers/compose.production.yml | 2 +- containers/traefik/traefik.yml | 4 +- docs/api/references.md | 2 +- docs/api/references.rst | 4 +- docs/contributing.md | 20 +-- docs/example.ipynb | 6 +- docs/index.md | 4 +- docs/installation.md | 16 +- mkdocs.yaml | 14 +- pyproject.toml | 10 +- src/config/api_router.py | 2 +- src/config/asgi.py | 6 +- src/config/settings/base.py | 24 +-- src/config/settings/production.py | 8 +- src/config/urls.py | 4 +- src/config/wsgi.py | 6 +- .../__init__.py | 0 .../conftest.py | 4 +- .../contrib/__init__.py | 0 .../contrib/sites/__init__.py | 0 .../contrib/sites/migrations/0001_initial.py | 0 .../migrations/0002_alter_domain_unique.py | 0 .../0003_set_site_domain_and_name.py | 4 +- .../0004_alter_options_ordering_domain.py | 0 .../contrib/sites/migrations/__init__.py | 0 .../forms.py | 0 .../models.py | 0 .../static/css/project.css | 0 .../static/fonts/.gitkeep | 0 .../static/images/favicons/favicon.ico | Bin .../static/js/project.js | 0 .../templates/403.html | 0 .../templates/403_csrf.html | 0 .../templates/404.html | 0 .../templates/500.html | 0 .../templates/account/account_inactive.html | 0 .../templates/account/base.html | 0 .../templates/account/email.html | 0 .../templates/account/email_confirm.html | 0 .../templates/account/login.html | 0 .../templates/account/logout.html | 0 .../templates/account/password_change.html | 0 .../templates/account/password_reset.html | 0 .../account/password_reset_done.html | 0 .../account/password_reset_from_key.html | 0 .../account/password_reset_from_key_done.html | 0 .../templates/account/password_set.html | 0 .../templates/account/signup.html | 0 .../templates/account/signup_closed.html | 0 .../templates/account/verification_sent.html | 0 .../account/verified_email_required.html | 0 .../templates/base.html | 4 +- .../templates/pages/about.html | 0 .../templates/pages/home.html | 0 .../templates/users/user_detail.html | 0 .../templates/users/user_form.html | 0 .../urls.py | 0 .../users/__init__.py | 0 .../users/adapters.py | 2 +- .../users/admin.py | 2 +- .../users/api/__init__.py | 0 .../users/api/serializers.py | 2 +- .../users/api/views.py | 0 .../users/apps.py | 4 +- .../users/context_processors.py | 0 .../users/forms.py | 0 .../users/managers.py | 2 +- .../users/migrations/0001_initial.py | 4 +- .../users/migrations/__init__.py | 0 .../users/models.py | 4 +- .../users/tests/__init__.py | 0 .../users/tests/factories.py | 0 .../users/tests/test_admin.py | 4 +- .../users/tests/test_drf_urls.py | 2 +- .../users/tests/test_drf_views.py | 4 +- .../users/tests/test_forms.py | 4 +- .../users/tests/test_managers.py | 2 +- .../users/tests/test_models.py | 2 +- .../users/tests/test_swagger.py | 0 .../users/tests/test_urls.py | 2 +- .../users/tests/test_views.py | 8 +- .../users/urls.py | 2 +- .../users/views.py | 0 .../views.py | 0 src/locale/en_US/LC_MESSAGES/django.po | 2 +- src/locale/fr_FR/LC_MESSAGES/django.po | 170 +++++++++--------- src/locale/pt_BR/LC_MESSAGES/django.po | 170 +++++++++--------- src/manage.py | 4 +- 103 files changed, 326 insertions(+), 322 deletions(-) rename src/{feedback_linker => growth_plan_linker}/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/conftest.py (67%) rename src/{feedback_linker => growth_plan_linker}/contrib/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/migrations/0001_initial.py (100%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/migrations/0002_alter_domain_unique.py (100%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/migrations/0003_set_site_domain_and_name.py (95%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/migrations/0004_alter_options_ordering_domain.py (100%) rename src/{feedback_linker => growth_plan_linker}/contrib/sites/migrations/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/forms.py (100%) rename src/{feedback_linker => growth_plan_linker}/models.py (100%) rename src/{feedback_linker => growth_plan_linker}/static/css/project.css (100%) rename src/{feedback_linker => growth_plan_linker}/static/fonts/.gitkeep (100%) rename src/{feedback_linker => growth_plan_linker}/static/images/favicons/favicon.ico (100%) rename src/{feedback_linker => growth_plan_linker}/static/js/project.js (100%) rename src/{feedback_linker => growth_plan_linker}/templates/403.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/403_csrf.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/404.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/500.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/account_inactive.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/base.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/email.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/email_confirm.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/login.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/logout.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_change.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_reset.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_reset_done.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_reset_from_key.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_reset_from_key_done.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/password_set.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/signup.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/signup_closed.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/verification_sent.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/account/verified_email_required.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/base.html (97%) rename src/{feedback_linker => growth_plan_linker}/templates/pages/about.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/pages/home.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/users/user_detail.html (100%) rename src/{feedback_linker => growth_plan_linker}/templates/users/user_form.html (100%) rename src/{feedback_linker => growth_plan_linker}/urls.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/adapters.py (96%) rename src/{feedback_linker => growth_plan_linker}/users/admin.py (97%) rename src/{feedback_linker => growth_plan_linker}/users/api/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/api/serializers.py (85%) rename src/{feedback_linker => growth_plan_linker}/users/api/views.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/apps.py (69%) rename src/{feedback_linker => growth_plan_linker}/users/context_processors.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/forms.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/managers.py (97%) rename src/{feedback_linker => growth_plan_linker}/users/migrations/0001_initial.py (97%) rename src/{feedback_linker => growth_plan_linker}/users/migrations/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/models.py (90%) rename src/{feedback_linker => growth_plan_linker}/users/tests/__init__.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/tests/factories.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_admin.py (95%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_drf_urls.py (92%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_drf_views.py (89%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_forms.py (90%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_managers.py (97%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_models.py (68%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_swagger.py (100%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_urls.py (91%) rename src/{feedback_linker => growth_plan_linker}/users/tests/test_views.py (93%) rename src/{feedback_linker => growth_plan_linker}/users/urls.py (87%) rename src/{feedback_linker => growth_plan_linker}/users/views.py (100%) rename src/{feedback_linker => growth_plan_linker}/views.py (100%) diff --git a/.envs/.local/.postgres b/.envs/.local/.postgres index 668477d..46c46e9 100644 --- a/.envs/.local/.postgres +++ b/.envs/.local/.postgres @@ -2,6 +2,6 @@ # ------------------------------------------------------------------------------ POSTGRES_HOST=postgres POSTGRES_PORT=5432 -POSTGRES_DB=feedback_linker +POSTGRES_DB=growth_plan_linker POSTGRES_USER=debug POSTGRES_PASSWORD=debug diff --git a/.envs/.production/.django b/.envs/.production/.django index ac286a2..89e7fc0 100644 --- a/.envs/.production/.django +++ b/.envs/.production/.django @@ -4,7 +4,7 @@ DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_SECRET_KEY=rzB5Sl2LAHyQOR5SAKqc0KeJTyUjNYVhd0ovG6H1dGrdvpd2FtZ9phEacFvFcbd0 DJANGO_ADMIN_URL=Rz4fpz5g02YMT9VXPQ4QMu8J3hoXOIBa/ -DJANGO_ALLOWED_HOSTS=.https://opensciencelabs.github.io/feedback-linker +DJANGO_ALLOWED_HOSTS=.https://opensciencelabs.github.io/growth-plan-linker # Security # ------------------------------------------------------------------------------ diff --git a/.envs/.production/.postgres b/.envs/.production/.postgres index 668477d..46c46e9 100644 --- a/.envs/.production/.postgres +++ b/.envs/.production/.postgres @@ -2,6 +2,6 @@ # ------------------------------------------------------------------------------ POSTGRES_HOST=postgres POSTGRES_PORT=5432 -POSTGRES_DB=feedback_linker +POSTGRES_DB=growth_plan_linker POSTGRES_USER=debug POSTGRES_PASSWORD=debug diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 6d422d7..c450856 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -7,7 +7,7 @@ body: - type: markdown attributes: value: > - **Thank you for wanting to report a bug in Feedback Linker!** + **Thank you for wanting to report a bug in Growth Plan Linker!** ⚠ @@ -15,7 +15,7 @@ body: GitHub][issue search]. - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Abug + [issue search]: https://github.com/xmnlab/growth-plan-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Abug - type: textarea attributes: @@ -124,14 +124,14 @@ body: - type: textarea attributes: - # label: Feedback Linker version + # label: Growth Plan Linker version # description: >- - # Paste verbatim output from `Feedback Linker --version` below, under the prompt line. + # Paste verbatim output from `Growth Plan Linker --version` below, under the prompt line. # Don't wrap it with triple backticks — your whole input will be # turned into a code snippet automatically. render: console placeholder: | - python -m pip show Feedback Linker + python -m pip show Growth Plan Linker validations: required: true @@ -139,9 +139,9 @@ body: attributes: label: Code of Conduct description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. + Read the [`Growth Plan Linker` Code of Conduct][CoC] first. - [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + [CoC]: https://github.com/xmnlab/growth-plan-linker.git/coc/CODE_OF_CONDUCT.md options: - label: I agree to follow the Code of Conduct required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6a118aa..ca0209d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,14 +2,14 @@ blank_issues_enabled: true contact_links: - name: 🙋 Usage Questions - url: https://github.com/xmnlab/feedback-linker.git/discussions + url: https://github.com/xmnlab/growth-plan-linker.git/discussions about: | - Use Feedback Linker's GitHub Discussions to ask "How do I do X with Feedback Linker?". + Use Growth Plan Linker's GitHub Discussions to ask "How do I do X with Growth Plan Linker?". - name: 📖 Tutorial - url: https://github.com/xmnlab/feedback-linker.git + url: https://github.com/xmnlab/growth-plan-linker.git about: | - The Feedback Linker tutorial is continually updated and provides an in depth walkthrough - of how to use the latest release of Feedback Linker. - - name: 📝 Feedback Linker Code of Conduct - url: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md - about: Expectations for how people will interact with each other on Feedback Linker's GitHub. + The Growth Plan Linker tutorial is continually updated and provides an in depth walkthrough + of how to use the latest release of Growth Plan Linker. + - name: 📝 Growth Plan Linker Code of Conduct + url: https://github.com/xmnlab/growth-plan-linker.git/coc/CODE_OF_CONDUCT.md + about: Expectations for how people will interact with each other on Growth Plan Linker's GitHub. diff --git a/.github/ISSUE_TEMPLATE/documentation-report.yml b/.github/ISSUE_TEMPLATE/documentation-report.yml index 600dd83..73b6d3f 100644 --- a/.github/ISSUE_TEMPLATE/documentation-report.yml +++ b/.github/ISSUE_TEMPLATE/documentation-report.yml @@ -7,7 +7,7 @@ body: - type: markdown attributes: value: > - **Thank you for wanting to report a problem with Feedback Linker's documentation!** + **Thank you for wanting to report a problem with Growth Plan Linker's documentation!** ⚠ @@ -15,7 +15,7 @@ body: GitHub][issue search]. - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Adocs + [issue search]: https://github.com/xmnlab/growth-plan-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Adocs - type: textarea attributes: @@ -25,7 +25,7 @@ body: If there are external references that are related please link them here as well. placeholder: >- - I was reading the Feedback Linker documentation for Feedback Linker version X and I'm having + I was reading the Growth Plan Linker documentation for Growth Plan Linker version X and I'm having problems understanding Y. It would be very helpful if that got rephrased as Z. validations: @@ -43,9 +43,9 @@ body: attributes: label: Code of Conduct description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. + Read the [`Growth Plan Linker` Code of Conduct][CoC] first. - [CoC]: https://github.com/xmnlab/feedback-linker.git/blob/main/CODE_OF_CONDUCT.md + [CoC]: https://github.com/xmnlab/growth-plan-linker.git/blob/main/CODE_OF_CONDUCT.md options: - label: I agree to follow the Code of Conduct required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index e5b54ca..72749da 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -7,7 +7,7 @@ body: - type: markdown attributes: value: > - **Thank you for wanting to suggest a feature for Feedback Linker!** + **Thank you for wanting to suggest a feature for Growth Plan Linker!** ⚠ @@ -17,7 +17,7 @@ body: already be implemented in a development release. - [issue search]: https://github.com/xmnlab/feedback-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Afeat%2Fenhancement + [issue search]: https://github.com/xmnlab/growth-plan-linker.git/issues?q=is%3Aopen+is%3Aissue+label%3Afeat%2Fenhancement - type: textarea attributes: @@ -28,7 +28,7 @@ body: What's the problem this feature will solve? What are you trying to do, that you are unable to achieve - with the **latest** release of Feedback Linker? + with the **latest** release of Growth Plan Linker? * Provide examples of real-world use cases that this would enable @@ -44,7 +44,7 @@ body: If there are external references or other GitHub Issues that are related please link them here as well. placeholder: >- - I am trying to do X with Feedback Linker version x.y.z and I think that implementing + I am trying to do X with Growth Plan Linker version x.y.z and I think that implementing new feature Y would be very helpful for me and every other user because of Z. validations: required: true @@ -70,9 +70,9 @@ body: attributes: label: Code of Conduct description: | - Read the [`Feedback Linker` Code of Conduct][CoC] first. + Read the [`Growth Plan Linker` Code of Conduct][CoC] first. - [CoC]: https://github.com/xmnlab/feedback-linker.git/coc/CODE_OF_CONDUCT.md + [CoC]: https://github.com/xmnlab/growth-plan-linker.git/coc/CODE_OF_CONDUCT.md options: - label: I agree to follow the Code of Conduct required: true diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 550cbd4..2b7f86b 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -24,7 +24,7 @@ jobs: environment-file: conda/dev.yaml channels: conda-forge,nodefaults channel-priority: true - activate-environment: feedback-linker + activate-environment: growth-plan-linker use-mamba: true miniforge-variant: Mambaforge diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b4ef35e..11a0850 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,7 +32,7 @@ jobs: environment-file: conda/dev.yaml channels: conda-forge,nodefaults channel-priority: true - activate-environment: feedback-linker + activate-environment: growth-plan-linker use-mamba: true miniforge-variant: Mambaforge diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9a14f7..98fcc7d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -82,7 +82,7 @@ repos: name: vulture entry: vulture --min-confidence 80 language: system - files: "src/feedback-linker" + files: "src/growth-plan-linker" description: Find unused Python code. pass_filenames: true types: @@ -92,7 +92,7 @@ repos: name: mccabe entry: python -m mccabe --min 10 language: system - files: "src/feedback-linker" + files: "src/growth-plan-linker" pass_filenames: true types: - python diff --git a/.releaserc.json b/.releaserc.json index bdcca18..d9eed6f 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -8,12 +8,12 @@ { "replacements": [ { - "files": ["src/feedback_linker/__init__.py"], + "files": ["src/growth_plan_linker/__init__.py"], "from": "return '.*' # semantic-release", "to": "return '${nextRelease.version}' # semantic-release", "results": [ { - "file": "src/feedback_linker/__init__.py", + "file": "src/growth_plan_linker/__init__.py", "hasChanged": true, "numMatches": 1, "numReplacements": 1 @@ -65,7 +65,7 @@ "assets": [ "pyproject.toml", "docs/changelog.md", - "src/feedback_linker/__init__.py" + "src/growth_plan_linker/__init__.py" ], "message": "chore(release): ${nextRelease.version}" } diff --git a/README.md b/README.md index 02c4ff0..cc7b7b6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Feedback-Linker +# Growth-Plan-Linker -Feedback-Linker is a simplified feedback exchange platform designed to +Growth-Plan-Linker is a simplified feedback exchange platform designed to facilitate periodic feedback between individuals within specific projects. It aims to streamline communication and insights sharing, enhancing project collaboration and personal development. -- Documentation: +- Documentation: ## Features -Feedback-Linker includes several key features to manage feedback efficiently: +Growth-Plan-Linker includes several key features to manage feedback efficiently: ### Projects @@ -60,13 +60,13 @@ Feedback-Linker includes several key features to manage feedback efficiently: ## Contributing -Feedback-Linker is open for contributions. Whether it's feature requests, bug -reports, or code contributions, we welcome your input to make Feedback-Linker +Growth-Plan-Linker is open for contributions. Whether it's feature requests, bug +reports, or code contributions, we welcome your input to make Growth-Plan-Linker better. Please see our contributing guidelines for more information. ## License -Feedback-Linker is licensed under the BSD 3-Clause License. +Growth-Plan-Linker is licensed under the BSD 3-Clause License. ## Contact @@ -74,5 +74,5 @@ For support or to get in touch with the developers, please open an issue. --- -We hope Feedback-Linker enhances your project collaboration and personal +We hope Growth-Plan-Linker enhances your project collaboration and personal development efforts! diff --git a/conda/dev.yaml b/conda/dev.yaml index 80165ab..25fb5b7 100644 --- a/conda/dev.yaml +++ b/conda/dev.yaml @@ -1,4 +1,4 @@ -name: feedback-linker +name: growth-plan-linker channels: - nodefaults - conda-forge diff --git a/containers/Dockerfile b/containers/Dockerfile index 155028c..ff15f37 100644 --- a/containers/Dockerfile +++ b/containers/Dockerfile @@ -3,9 +3,9 @@ FROM condaforge/mambaforge:latest LABEL maintainer="Ivan Ogasawara " -LABEL org.opencontainers.image.title="Feedback Linker" -LABEL org.opencontainers.image.authors="Feedback Linker Team" -LABEL org.opencontainers.image.source="https://github.com/xmnlab/feedback-linker.git" +LABEL org.opencontainers.image.title="Growth Plan Linker" +LABEL org.opencontainers.image.authors="Growth Plan Linker Team" +LABEL org.opencontainers.image.source="https://github.com/xmnlab/growth-plan-linker.git" LABEL org.opencontainers.image.version="latest" LABEL org.opencontainers.image.description="The main objective of this platform is to offer a space to handle and organize feedback between two people" @@ -18,7 +18,7 @@ SHELL ["/bin/bash", "-c"] # executing commands with /bin/bash -c # Needed to have the micromamba activate command configured etc. -ENV ENV_NAME=feedback_linker +ENV ENV_NAME=growth_plan_linker ENV DEBIAN_FRONTEND=noninteractive ARG UID=1000 ARG GID=1000 @@ -39,9 +39,9 @@ RUN apt-get update && \ vim && \ rm -rf /var/lib/apt/lists/* -USER feedback_linker +USER growth_plan_linker -WORKDIR /feedback-linker +WORKDIR /growth-plan-linker COPY pyproject.toml . @@ -54,10 +54,10 @@ RUN mamba install -y poetry && \ poetry install --no-dev --no-interaction --no-ansi -COPY . /feedback-linker/ +COPY . /growth-plan-linker/ COPY . . COPY compose.yaml . -CMD ["python", "feedback_linker.py"] +CMD ["python", "growth_plan_linker.py"] diff --git a/containers/compose.local.yml b/containers/compose.local.yml index f6ec67b..2810a6c 100644 --- a/containers/compose.local.yml +++ b/containers/compose.local.yml @@ -45,7 +45,7 @@ services: volumes: - ./docs:/docs:z - ./config:/app/config:z - - ./feedback_linker:/app/feedback_linker:z + - ./growth_plan_linker:/app/growth_plan_linker:z ports: - "9000:9000" command: /start-docs diff --git a/containers/compose.production.yml b/containers/compose.production.yml index 3e23c30..922b9c1 100644 --- a/containers/compose.production.yml +++ b/containers/compose.production.yml @@ -14,7 +14,7 @@ services: image: feedback_linker_production_django volumes: - - production_django_media:/app/feedback_linker/media + - production_django_media:/app/growth_plan_linker/media depends_on: - postgres - redis diff --git a/containers/traefik/traefik.yml b/containers/traefik/traefik.yml index be6f742..9006654 100644 --- a/containers/traefik/traefik.yml +++ b/containers/traefik/traefik.yml @@ -28,7 +28,7 @@ certificatesResolvers: http: routers: web-secure-router: - rule: "Host(`https://opensciencelabs.github.io/feedback-linker`)" + rule: "Host(`https://opensciencelabs.github.io/growth-plan-linker`)" entryPoints: - web-secure middlewares: @@ -39,7 +39,7 @@ http: certResolver: letsencrypt web-media-router: - rule: "Host(`https://opensciencelabs.github.io/feedback-linker`) && PathPrefix(`/media/`)" + rule: "Host(`https://opensciencelabs.github.io/growth-plan-linker`) && PathPrefix(`/media/`)" entryPoints: - web-secure middlewares: diff --git a/docs/api/references.md b/docs/api/references.md index ecc54ab..0397905 100644 --- a/docs/api/references.md +++ b/docs/api/references.md @@ -1,3 +1,3 @@ # Api references -::: feedback_linker +::: growth_plan_linker diff --git a/docs/api/references.rst b/docs/api/references.rst index 0769463..6c7429a 100644 --- a/docs/api/references.rst +++ b/docs/api/references.rst @@ -1,8 +1,8 @@ API references ============== -.. automodule:: feedback_linker +.. automodule:: growth_plan_linker :members: -.. automodule:: feedback_linker.feedback_linker +.. automodule:: growth_plan_linker.growth_plan_linker :members: diff --git a/docs/contributing.md b/docs/contributing.md index 2d87526..9fa35bc 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,7 +2,7 @@ In order to be able to contribute, it is important that you understand the project layout. This project uses the _src layout_, which means that the package -code is located at `./src/feedback_linker`. +code is located at `./src/growth_plan_linker`. For my information, check the official documentation: https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/ @@ -24,7 +24,7 @@ You can contribute in many ways: ### Report Bugs -Report bugs at https://github.com/xmnlab/feedback-linker.git/issues. +Report bugs at https://github.com/xmnlab/growth-plan-linker.git/issues. If you are reporting a bug, please include: @@ -44,14 +44,14 @@ and “help wanted” is open to whoever wants to implement it. ### Write Documentation -Feedback Linker could always use more documentation, whether as part of the -official Feedback Linker docs, in docstrings, or even on the web in blog posts, -articles, and such. +Growth Plan Linker could always use more documentation, whether as part of the +official Growth Plan Linker docs, in docstrings, or even on the web in blog +posts, articles, and such. ### Submit Feedback The best way to send feedback is to file an issue at -https://github.com/xmnlab/feedback-linker.git/issues. +https://github.com/xmnlab/growth-plan-linker.git/issues. If you are proposing a feature: @@ -62,20 +62,20 @@ If you are proposing a feature: ## Get Started! -Ready to contribute? Here’s how to set up `feedback-linker` for local +Ready to contribute? Here’s how to set up `growth-plan-linker` for local development. -1. Fork the `feedback-linker` repo on GitHub. +1. Fork the `growth-plan-linker` repo on GitHub. 2. Clone your fork locally:: - $ git clone git@github.com:your_name_here/feedback-linker.git + $ git clone git@github.com:your_name_here/growth-plan-linker.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: - $ mkvirtualenv feedback-linker $ cd feedback-linker/ $ python setup.py + $ mkvirtualenv growth-plan-linker $ cd growth-plan-linker/ $ python setup.py develop 4. Create a branch for local development:: diff --git a/docs/example.ipynb b/docs/example.ipynb index b18fdcc..8d9682a 100644 --- a/docs/example.ipynb +++ b/docs/example.ipynb @@ -4,9 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Feedback Linker\n", + "# Growth Plan Linker\n", "\n", - "Feedback Linker is Python library that aims to do ...\n", + "Growth Plan Linker is Python library that aims to do ...\n", "\n", "## Getting Started\n", "\n", @@ -21,7 +21,7 @@ "metadata": {}, "outputs": [], "source": [ - "import feedback_linker" + "import growth_plan_linker" ] }, { diff --git a/docs/index.md b/docs/index.md index 04600f6..cfbbed5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,12 +1,12 @@ ![LOGO](/images/logo.png) -# Feedback Linker +# Growth Plan Linker The main objective of this platform is to offer a space to handle and organize feedback between two people - License: BSD 3 Clause -- Documentation: https://feedback-linker.github.io +- Documentation: https://growth-plan-linker.github.io ## Features diff --git a/docs/installation.md b/docs/installation.md index bae32c5..e1aa04c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -2,13 +2,13 @@ ## Stable release -To install Feedback Linker, run this command in your terminal: +To install Growth Plan Linker, run this command in your terminal: ```bash -$ pip install feedback-linker +$ pip install growth-plan-linker ``` -This is the preferred method to install Feedback Linker, as it will always +This is the preferred method to install Growth Plan Linker, as it will always install the most recent stable release. If you don't have [pip](https://pip.pypa.io) installed, this @@ -17,20 +17,20 @@ can guide you through the process. ## From sources -The sources for Feedback Linker can be downloaded from the -[Github repo](https://github.com/xmnlab/feedback-linker.git). +The sources for Growth Plan Linker can be downloaded from the +[Github repo](https://github.com/xmnlab/growth-plan-linker.git). You can either clone the public repository: ```bash -$ git clone https://github.com/xmnlab/feedback-linker.git +$ git clone https://github.com/xmnlab/growth-plan-linker.git ``` Or download the -[tarball](https://github.com/xmnlab/feedback-linker.git/tarball/main): +[tarball](https://github.com/xmnlab/growth-plan-linker.git/tarball/main): ```bash -$ curl -OJL https://github.com/xmnlab/feedback-linker.git/tarball/main +$ curl -OJL https://github.com/xmnlab/growth-plan-linker.git/tarball/main ``` Once you have a copy of the source, you can install it with: diff --git a/mkdocs.yaml b/mkdocs.yaml index 251d896..b51206d 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -1,6 +1,6 @@ -site_name: Feedback Linker -site_url: https://opensciencelabs.github.io/feedback-linker -repo_url: https://github.com/xmnlab/feedback-linker.git +site_name: Growth Plan Linker +site_url: https://opensciencelabs.github.io/growth-plan-linker +repo_url: https://github.com/xmnlab/growth-plan-linker.git docs_dir: ./docs site_dir: ./build # extra_css: @@ -123,7 +123,7 @@ markdown_extensions: - tables - toc extra: - project_name: "Feedback Linker" + project_name: "Growth Plan Linker" team: - name: "Active maintainers" members: @@ -135,11 +135,11 @@ extra: - icon: fontawesome/brands/twitter link: https://twitter.com/xmnlab - icon: fontawesome/brands/github - link: https://github.com/xmnlab/feedback-linker.git + link: https://github.com/xmnlab/growth-plan-linker.git support_levels: supported: icon: :material-check-decagram:{ .verified } - description: Tested in CI. If this doesn't work for you, please [file an issue](https://github.com/xmnlab/feedback-linker.git/issues/new). + description: Tested in CI. If this doesn't work for you, please [file an issue](https://github.com/xmnlab/growth-plan-linker.git/issues/new). bug: icon: :material-bug:{ .bug } description: Should work but doesn't because upstream is broken. Supported on a best-effort basis. @@ -147,4 +147,4 @@ extra: icon: :material-cancel:{ .cancel } description: Unlikely to ever be supported or no upstream support. -copyright: "Copyright © 2022, Feedback Linker Team" +copyright: "Copyright © 2022, Growth Plan Linker Team" diff --git a/pyproject.toml b/pyproject.toml index 519f054..4cdea3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "feedback-linker" +name = "growth-plan-linker" version = "0.1.0" # semantic-release description = "The main objective of this platform is to offer a space to handle and organize feedback between two people" authors = ["Ivan Ogasawara "] @@ -8,7 +8,7 @@ exclude = [ ".git/*", ".env*", ] -include = ["src/feedback_linker/py.typed"] +include = ["src/growth_plan_linker/py.typed"] [tool.poetry.dependencies] python = ">=3.10,<4" @@ -100,7 +100,7 @@ python_files = [ # ==== Coverage ==== [tool.coverage.run] -include = ["src/feedback_linker/**"] +include = ["src/growth_plan_linker/**"] omit = ["*/migrations/*", "*/tests/*"] plugins = ["django_coverage_plugin"] @@ -114,7 +114,7 @@ ignore_missing_imports = true warn_unused_ignores = true warn_redundant_casts = true warn_unused_configs = true -files = ["./src/feedback_linker"] +files = ["./src/growth_plan_linker"] plugins = [ "mypy_django_plugin.main", "mypy_drf_plugin.main", @@ -131,7 +131,7 @@ strict_settings = false ignore_missing_model_attributes = true # [[tool.mypy.overrides]] -# module = "feedback_linker.forms" +# module = "growth_plan_linker.forms" # ignore_errors = true [[tool.mypy.overrides]] diff --git a/src/config/api_router.py b/src/config/api_router.py index 857a34d..623cb3a 100644 --- a/src/config/api_router.py +++ b/src/config/api_router.py @@ -1,5 +1,5 @@ from django.conf import settings -from feedback_linker.users.api.views import UserViewSet +from growth_plan_linker.users.api.views import UserViewSet from rest_framework.routers import DefaultRouter, SimpleRouter router = DefaultRouter() if settings.DEBUG else SimpleRouter() diff --git a/src/config/asgi.py b/src/config/asgi.py index d1e89db..9ae82ce 100644 --- a/src/config/asgi.py +++ b/src/config/asgi.py @@ -1,6 +1,6 @@ # ruff: noqa """ -ASGI config for Feedback-Linker project. +ASGI config for Growth-Plan-Linker project. It exposes the ASGI callable as a module-level variable named ``application``. @@ -16,9 +16,9 @@ from django.core.asgi import get_asgi_application # This allows easy placement of apps within the interior -# feedback_linker directory. +# growth_plan_linker directory. BASE_DIR = Path(__file__).resolve(strict=True).parent.parent -sys.path.append(str(BASE_DIR / 'feedback_linker')) +sys.path.append(str(BASE_DIR / 'growth_plan_linker')) # If DJANGO_SETTINGS_MODULE is unset, default to the local settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') diff --git a/src/config/settings/base.py b/src/config/settings/base.py index a02b237..f3e1eb4 100644 --- a/src/config/settings/base.py +++ b/src/config/settings/base.py @@ -6,8 +6,8 @@ import environ BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent -# feedback_linker/ -APPS_DIR = BASE_DIR / 'feedback_linker' +# growth_plan_linker/ +APPS_DIR = BASE_DIR / 'growth_plan_linker' env = environ.Env() READ_DOT_ENV_FILE = env.bool('DJANGO_READ_DOT_ENV_FILE', default=False) @@ -92,7 +92,7 @@ ] LOCAL_APPS = [ - 'feedback_linker.users', + 'growth_plan_linker.users', # Your stuff: custom apps go here ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps @@ -101,7 +101,7 @@ # MIGRATIONS # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules -MIGRATION_MODULES = {'sites': 'feedback_linker.contrib.sites.migrations'} +MIGRATION_MODULES = {'sites': 'growth_plan_linker.contrib.sites.migrations'} # AUTHENTICATION # ------------------------------------------------------------------------------ @@ -201,7 +201,7 @@ 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', - 'feedback_linker.users.context_processors.allauth_settings', + 'growth_plan_linker.users.context_processors.allauth_settings', ], }, }, @@ -294,14 +294,16 @@ # https://docs.allauth.org/en/latest/account/configuration.html ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # https://docs.allauth.org/en/latest/account/configuration.html -ACCOUNT_ADAPTER = 'feedback_linker.users.adapters.AccountAdapter' +ACCOUNT_ADAPTER = 'growth_plan_linker.users.adapters.AccountAdapter' # https://docs.allauth.org/en/latest/account/forms.html -ACCOUNT_FORMS = {'signup': 'feedback_linker.users.forms.UserSignupForm'} +ACCOUNT_FORMS = {'signup': 'growth_plan_linker.users.forms.UserSignupForm'} # https://docs.allauth.org/en/latest/socialaccount/configuration.html -SOCIALACCOUNT_ADAPTER = 'feedback_linker.users.adapters.SocialAccountAdapter' +SOCIALACCOUNT_ADAPTER = ( + 'growth_plan_linker.users.adapters.SocialAccountAdapter' +) # https://docs.allauth.org/en/latest/socialaccount/configuration.html SOCIALACCOUNT_FORMS = { - 'signup': 'feedback_linker.users.forms.UserSocialSignupForm', + 'signup': 'growth_plan_linker.users.forms.UserSocialSignupForm', } # django-compressor # ------------------------------------------------------------------------------ @@ -328,8 +330,8 @@ # By Default swagger ui is available only to admin user(s). You can change permission classes to change that # See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings SPECTACULAR_SETTINGS = { - 'TITLE': 'Feedback-Linker API', - 'DESCRIPTION': 'Documentation of API endpoints of Feedback-Linker', + 'TITLE': 'Growth-Plan-Linker API', + 'DESCRIPTION': 'Documentation of API endpoints of Growth-Plan-Linker', 'VERSION': '1.0.0', 'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAdminUser'], } diff --git a/src/config/settings/production.py b/src/config/settings/production.py index c70ccde..9286db0 100644 --- a/src/config/settings/production.py +++ b/src/config/settings/production.py @@ -23,7 +23,7 @@ # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = env.list( 'DJANGO_ALLOWED_HOSTS', - default=['https://opensciencelabs.github.io/feedback-linker'], + default=['https://opensciencelabs.github.io/growth-plan-linker'], ) # DATABASES @@ -88,14 +88,14 @@ # https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email DEFAULT_FROM_EMAIL = env( 'DJANGO_DEFAULT_FROM_EMAIL', - default='Feedback-Linker ', + default='Growth-Plan-Linker ', ) # https://docs.djangoproject.com/en/dev/ref/settings/#server-email SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) # https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix EMAIL_SUBJECT_PREFIX = env( 'DJANGO_EMAIL_SUBJECT_PREFIX', - default='[Feedback-Linker] ', + default='[Growth-Plan-Linker] ', ) # ADMIN @@ -198,7 +198,7 @@ # Tools that generate code samples can use SERVERS to point to the correct domain SPECTACULAR_SETTINGS['SERVERS'] = [ { - 'url': 'https://https://opensciencelabs.github.io/feedback-linker', + 'url': 'https://https://opensciencelabs.github.io/growth-plan-linker', 'description': 'Production server', }, ] diff --git a/src/config/urls.py b/src/config/urls.py index 8ab0fa8..357f32e 100644 --- a/src/config/urls.py +++ b/src/config/urls.py @@ -23,7 +23,9 @@ # Django Admin, use {% url 'admin:index' %} path(settings.ADMIN_URL, admin.site.urls), # User management - path('users/', include('feedback_linker.users.urls', namespace='users')), + path( + 'users/', include('growth_plan_linker.users.urls', namespace='users') + ), path('accounts/', include('allauth.urls')), # Your stuff: custom urls includes go here # ... diff --git a/src/config/wsgi.py b/src/config/wsgi.py index 9b18fe6..d8be001 100644 --- a/src/config/wsgi.py +++ b/src/config/wsgi.py @@ -1,6 +1,6 @@ # ruff: noqa """ -WSGI config for Feedback-Linker project. +WSGI config for Growth-Plan-Linker project. This module contains the WSGI application used by Django's development server and any production WSGI deployments. It should expose a module-level variable @@ -22,9 +22,9 @@ from django.core.wsgi import get_wsgi_application # This allows easy placement of apps within the interior -# feedback_linker directory. +# growth_plan_linker directory. BASE_DIR = Path(__file__).resolve(strict=True).parent.parent -sys.path.append(str(BASE_DIR / 'feedback_linker')) +sys.path.append(str(BASE_DIR / 'growth_plan_linker')) # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks # if running multiple sites in the same mod_wsgi process. To fix this, use # mod_wsgi daemon mode with each site in its own daemon process, or use diff --git a/src/feedback_linker/__init__.py b/src/growth_plan_linker/__init__.py similarity index 100% rename from src/feedback_linker/__init__.py rename to src/growth_plan_linker/__init__.py diff --git a/src/feedback_linker/conftest.py b/src/growth_plan_linker/conftest.py similarity index 67% rename from src/feedback_linker/conftest.py rename to src/growth_plan_linker/conftest.py index 00b25b3..b90bcff 100644 --- a/src/feedback_linker/conftest.py +++ b/src/growth_plan_linker/conftest.py @@ -1,7 +1,7 @@ import pytest -from feedback_linker.users.models import User -from feedback_linker.users.tests.factories import UserFactory +from growth_plan_linker.users.models import User +from growth_plan_linker.users.tests.factories import UserFactory @pytest.fixture(autouse=True) diff --git a/src/feedback_linker/contrib/__init__.py b/src/growth_plan_linker/contrib/__init__.py similarity index 100% rename from src/feedback_linker/contrib/__init__.py rename to src/growth_plan_linker/contrib/__init__.py diff --git a/src/feedback_linker/contrib/sites/__init__.py b/src/growth_plan_linker/contrib/sites/__init__.py similarity index 100% rename from src/feedback_linker/contrib/sites/__init__.py rename to src/growth_plan_linker/contrib/sites/__init__.py diff --git a/src/feedback_linker/contrib/sites/migrations/0001_initial.py b/src/growth_plan_linker/contrib/sites/migrations/0001_initial.py similarity index 100% rename from src/feedback_linker/contrib/sites/migrations/0001_initial.py rename to src/growth_plan_linker/contrib/sites/migrations/0001_initial.py diff --git a/src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py b/src/growth_plan_linker/contrib/sites/migrations/0002_alter_domain_unique.py similarity index 100% rename from src/feedback_linker/contrib/sites/migrations/0002_alter_domain_unique.py rename to src/growth_plan_linker/contrib/sites/migrations/0002_alter_domain_unique.py diff --git a/src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py b/src/growth_plan_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py similarity index 95% rename from src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py rename to src/growth_plan_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py index 1b66160..207e501 100644 --- a/src/feedback_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py +++ b/src/growth_plan_linker/contrib/sites/migrations/0003_set_site_domain_and_name.py @@ -40,8 +40,8 @@ def update_site_forward(apps, schema_editor): _update_or_create_site_with_sequence( Site, schema_editor.connection, - "https://opensciencelabs.github.io/feedback-linker", - "Feedback-Linker", + "https://opensciencelabs.github.io/growth-plan-linker", + "Growth-Plan-Linker", ) diff --git a/src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py b/src/growth_plan_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py similarity index 100% rename from src/feedback_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py rename to src/growth_plan_linker/contrib/sites/migrations/0004_alter_options_ordering_domain.py diff --git a/src/feedback_linker/contrib/sites/migrations/__init__.py b/src/growth_plan_linker/contrib/sites/migrations/__init__.py similarity index 100% rename from src/feedback_linker/contrib/sites/migrations/__init__.py rename to src/growth_plan_linker/contrib/sites/migrations/__init__.py diff --git a/src/feedback_linker/forms.py b/src/growth_plan_linker/forms.py similarity index 100% rename from src/feedback_linker/forms.py rename to src/growth_plan_linker/forms.py diff --git a/src/feedback_linker/models.py b/src/growth_plan_linker/models.py similarity index 100% rename from src/feedback_linker/models.py rename to src/growth_plan_linker/models.py diff --git a/src/feedback_linker/static/css/project.css b/src/growth_plan_linker/static/css/project.css similarity index 100% rename from src/feedback_linker/static/css/project.css rename to src/growth_plan_linker/static/css/project.css diff --git a/src/feedback_linker/static/fonts/.gitkeep b/src/growth_plan_linker/static/fonts/.gitkeep similarity index 100% rename from src/feedback_linker/static/fonts/.gitkeep rename to src/growth_plan_linker/static/fonts/.gitkeep diff --git a/src/feedback_linker/static/images/favicons/favicon.ico b/src/growth_plan_linker/static/images/favicons/favicon.ico similarity index 100% rename from src/feedback_linker/static/images/favicons/favicon.ico rename to src/growth_plan_linker/static/images/favicons/favicon.ico diff --git a/src/feedback_linker/static/js/project.js b/src/growth_plan_linker/static/js/project.js similarity index 100% rename from src/feedback_linker/static/js/project.js rename to src/growth_plan_linker/static/js/project.js diff --git a/src/feedback_linker/templates/403.html b/src/growth_plan_linker/templates/403.html similarity index 100% rename from src/feedback_linker/templates/403.html rename to src/growth_plan_linker/templates/403.html diff --git a/src/feedback_linker/templates/403_csrf.html b/src/growth_plan_linker/templates/403_csrf.html similarity index 100% rename from src/feedback_linker/templates/403_csrf.html rename to src/growth_plan_linker/templates/403_csrf.html diff --git a/src/feedback_linker/templates/404.html b/src/growth_plan_linker/templates/404.html similarity index 100% rename from src/feedback_linker/templates/404.html rename to src/growth_plan_linker/templates/404.html diff --git a/src/feedback_linker/templates/500.html b/src/growth_plan_linker/templates/500.html similarity index 100% rename from src/feedback_linker/templates/500.html rename to src/growth_plan_linker/templates/500.html diff --git a/src/feedback_linker/templates/account/account_inactive.html b/src/growth_plan_linker/templates/account/account_inactive.html similarity index 100% rename from src/feedback_linker/templates/account/account_inactive.html rename to src/growth_plan_linker/templates/account/account_inactive.html diff --git a/src/feedback_linker/templates/account/base.html b/src/growth_plan_linker/templates/account/base.html similarity index 100% rename from src/feedback_linker/templates/account/base.html rename to src/growth_plan_linker/templates/account/base.html diff --git a/src/feedback_linker/templates/account/email.html b/src/growth_plan_linker/templates/account/email.html similarity index 100% rename from src/feedback_linker/templates/account/email.html rename to src/growth_plan_linker/templates/account/email.html diff --git a/src/feedback_linker/templates/account/email_confirm.html b/src/growth_plan_linker/templates/account/email_confirm.html similarity index 100% rename from src/feedback_linker/templates/account/email_confirm.html rename to src/growth_plan_linker/templates/account/email_confirm.html diff --git a/src/feedback_linker/templates/account/login.html b/src/growth_plan_linker/templates/account/login.html similarity index 100% rename from src/feedback_linker/templates/account/login.html rename to src/growth_plan_linker/templates/account/login.html diff --git a/src/feedback_linker/templates/account/logout.html b/src/growth_plan_linker/templates/account/logout.html similarity index 100% rename from src/feedback_linker/templates/account/logout.html rename to src/growth_plan_linker/templates/account/logout.html diff --git a/src/feedback_linker/templates/account/password_change.html b/src/growth_plan_linker/templates/account/password_change.html similarity index 100% rename from src/feedback_linker/templates/account/password_change.html rename to src/growth_plan_linker/templates/account/password_change.html diff --git a/src/feedback_linker/templates/account/password_reset.html b/src/growth_plan_linker/templates/account/password_reset.html similarity index 100% rename from src/feedback_linker/templates/account/password_reset.html rename to src/growth_plan_linker/templates/account/password_reset.html diff --git a/src/feedback_linker/templates/account/password_reset_done.html b/src/growth_plan_linker/templates/account/password_reset_done.html similarity index 100% rename from src/feedback_linker/templates/account/password_reset_done.html rename to src/growth_plan_linker/templates/account/password_reset_done.html diff --git a/src/feedback_linker/templates/account/password_reset_from_key.html b/src/growth_plan_linker/templates/account/password_reset_from_key.html similarity index 100% rename from src/feedback_linker/templates/account/password_reset_from_key.html rename to src/growth_plan_linker/templates/account/password_reset_from_key.html diff --git a/src/feedback_linker/templates/account/password_reset_from_key_done.html b/src/growth_plan_linker/templates/account/password_reset_from_key_done.html similarity index 100% rename from src/feedback_linker/templates/account/password_reset_from_key_done.html rename to src/growth_plan_linker/templates/account/password_reset_from_key_done.html diff --git a/src/feedback_linker/templates/account/password_set.html b/src/growth_plan_linker/templates/account/password_set.html similarity index 100% rename from src/feedback_linker/templates/account/password_set.html rename to src/growth_plan_linker/templates/account/password_set.html diff --git a/src/feedback_linker/templates/account/signup.html b/src/growth_plan_linker/templates/account/signup.html similarity index 100% rename from src/feedback_linker/templates/account/signup.html rename to src/growth_plan_linker/templates/account/signup.html diff --git a/src/feedback_linker/templates/account/signup_closed.html b/src/growth_plan_linker/templates/account/signup_closed.html similarity index 100% rename from src/feedback_linker/templates/account/signup_closed.html rename to src/growth_plan_linker/templates/account/signup_closed.html diff --git a/src/feedback_linker/templates/account/verification_sent.html b/src/growth_plan_linker/templates/account/verification_sent.html similarity index 100% rename from src/feedback_linker/templates/account/verification_sent.html rename to src/growth_plan_linker/templates/account/verification_sent.html diff --git a/src/feedback_linker/templates/account/verified_email_required.html b/src/growth_plan_linker/templates/account/verified_email_required.html similarity index 100% rename from src/feedback_linker/templates/account/verified_email_required.html rename to src/growth_plan_linker/templates/account/verified_email_required.html diff --git a/src/feedback_linker/templates/base.html b/src/growth_plan_linker/templates/base.html similarity index 97% rename from src/feedback_linker/templates/base.html rename to src/growth_plan_linker/templates/base.html index c1c287d..56ff581 100644 --- a/src/feedback_linker/templates/base.html +++ b/src/growth_plan_linker/templates/base.html @@ -8,7 +8,7 @@ {% block title %} - Feedback-Linker + Growth-Plan-Linker {% endblock title %} @@ -58,7 +58,7 @@ aria-label="Toggle navigation"> - Feedback-Linker + Growth-Plan-Linker +
+ {% if messages %} + {% for message in messages %} +
+ {{ message }} + +
+ {% endfor %} + {% endif %} + {% block content %} +

Use this document as a way to quick start any new project.

+ {% endblock content %} +
+ + {% block modal %} + {% endblock modal %} + {% block inline_javascript %} + {% comment %} + Script tags with only code, no src (defer by default). + To run with a "defer" so that you run inline code: - {% endcomment %} - {% endblock inline_javascript %} - + {% endcomment %} + {% endblock inline_javascript %} + diff --git a/src/manage.py b/src/manage.py index b2b0283..2ed95f8 100755 --- a/src/manage.py +++ b/src/manage.py @@ -5,7 +5,7 @@ from pathlib import Path if __name__ == '__main__': - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.dev') try: from django.core.management import execute_from_command_line From fca80b234edef061701015586c3ba442462b83d3 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 21:32:32 -0400 Subject: [PATCH 09/12] fix container issues --- .sugar.yaml | 15 +++ containers/app/Dockerfile | 95 +++++++++++++++++++ containers/app/Dockerfile.bkp | 82 ++++++++++++++++ containers/app/Dockerfile.local.bkp | 62 ++++++++++++ containers/app/scripts/entrypoint.sh | 30 ++++++ containers/app/scripts/install-deps.sh | 12 +++ containers/app/scripts/start.sh | 29 ++++++ containers/compose.base.yaml | 39 ++++++++ .../migrations/0001_initial.py | 53 +++++++++++ src/growth_plan_linker/migrations/__init__.py | 0 10 files changed, 417 insertions(+) create mode 100644 .sugar.yaml create mode 100644 containers/app/Dockerfile create mode 100644 containers/app/Dockerfile.bkp create mode 100644 containers/app/Dockerfile.local.bkp create mode 100755 containers/app/scripts/entrypoint.sh create mode 100755 containers/app/scripts/install-deps.sh create mode 100644 containers/app/scripts/start.sh create mode 100644 containers/compose.base.yaml create mode 100644 src/growth_plan_linker/migrations/0001_initial.py create mode 100644 src/growth_plan_linker/migrations/__init__.py diff --git a/.sugar.yaml b/.sugar.yaml new file mode 100644 index 0000000..34284c9 --- /dev/null +++ b/.sugar.yaml @@ -0,0 +1,15 @@ +version: 1.0 +compose-app: docker compose +defaults: + group: dev + project-name: growthplan +groups: + dev: + compose-path: + - containers/compose.base.yaml + # env-file: .env + services: + default: app,postgres + available: + - name: app + - name: postgres diff --git a/containers/app/Dockerfile b/containers/app/Dockerfile new file mode 100644 index 0000000..763f730 --- /dev/null +++ b/containers/app/Dockerfile @@ -0,0 +1,95 @@ +# ref: https://github.com/mamba-org/micromamba-docker/blob/main/Dockerfile + +FROM condaforge/mambaforge:latest + +LABEL maintainer="Ivan Ogasawara " +LABEL org.opencontainers.image.title="LiteRev" +LABEL org.opencontainers.image.authors="LiteRev Team" +LABEL org.opencontainers.image.source="https://github.com/thegraphnetwork-app/LiteRev" +LABEL org.opencontainers.image.version="latest" +LABEL org.opencontainers.image.description="LiteRev" +LABEL org.thegraphnetwork.config.version="latest" + +# it is the default, but using it here to have it explicitly +USER root + +SHELL ["/bin/bash", "-c"] +# Use bash in Dockerfile RUN commands and make sure bashrc is sourced when +# executing commands with /bin/bash -c +# Needed to have the micromamba activate command configured etc. + +ENV ENV_NAME=growth-plan-linker +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC +ARG UID=1000 +ARG GID=1000 + +RUN apt-get update -y \ + && apt-get install -y \ + apt-utils \ + build-essential \ + curl \ + tini \ + nginx \ + snapd \ + sudo \ + tzdata \ + gcc-multilib \ + g++-multilib \ + openssl \ + && rm -rf /var/lib/apt/lists/* \ + /var/cache/apt/archives \ + /tmp/* + +RUN addgroup --gid ${GID} app \ + && useradd --uid ${UID} --gid ${GID} -ms /bin/bash app \ + && mkdir -p /app \ + && mkdir -p /opt/appfiles \ + && chmod -R a+rwx /opt/conda /app /opt/appfiles \ + && export ENV_NAME="$ENV_NAME" \ + && chown app:app /app /opt/appfiles \ + && echo "app ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/app \ + && chmod 0440 /etc/sudoers.d/app + +USER app + +WORKDIR /app + +COPY --chown=app:app ./conda/ /tmp/conda +# COPY --chown=app:app ./.github/workflows/.condarc /home/app/.condarc + +ARG HTTP_PROXY +ARG HTTPS_PROXY + +RUN mamba env create -n $ENV_NAME --file /tmp/conda/dev.yaml \ + && conda clean --all \ + && find /opt/conda/ -type f,l -name '*.pyc' -delete \ + && find /opt/conda/ -type f,l -name '*.js.map' -delete \ + && rm -rf /opt/conda/pkgs /tmp/* + +ENV CONDA_PREFIX /opt/conda/envs/$ENV_NAME +ENV PATH ${CONDA_PREFIX}/bin:$PATH + +# install dependencies +COPY --chown=app:app pyproject.toml poetry.lock /opt/appfiles +COPY --chown=app:app containers/app/scripts/install-deps.sh /opt/appfiles/install-deps.sh + +ARG ENV=prod + +RUN /opt/appfiles/install-deps.sh + +COPY --chown=app:app containers/app/scripts/entrypoint.sh /opt/appfiles/entrypoint.sh +COPY --chown=app:app src/ /app + +# copy start script +COPY --chown=app:app containers/app/scripts/start.sh /opt/appfiles/start.sh + +RUN chmod +x /opt/appfiles/start.sh \ + && chmod +x /opt/appfiles/entrypoint.sh \ + && echo "source /opt/appfiles/entrypoint.sh" > ~/.bashrc + + +ENV PYTHONPATH='/app' + +ENTRYPOINT ["tini", "--", "/opt/appfiles/entrypoint.sh"] +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/containers/app/Dockerfile.bkp b/containers/app/Dockerfile.bkp new file mode 100644 index 0000000..206aab1 --- /dev/null +++ b/containers/app/Dockerfile.bkp @@ -0,0 +1,82 @@ + +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + +# Python build stage +FROM docker.io/python as python-build-stage + +ARG BUILD_ENVIRONMENT=production + +# Install apt packages +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential \ + # psycopg2 dependencies + libpq-dev + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements . + +# Create Python Dependency and Sub-Dependency Wheels. +RUN pip wheel --wheel-dir /usr/src/app/wheels \ + -r ${BUILD_ENVIRONMENT}.txt + + +# Python 'run' stage +FROM docker.io/python as python-run-stage + +ARG BUILD_ENVIRONMENT=production +ARG APP_HOME=/app + +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 +ENV BUILD_ENV ${BUILD_ENVIRONMENT} + +WORKDIR ${APP_HOME} + +RUN addgroup --system django \ + && adduser --system --ingroup django django + + +# Install required system dependencies +RUN apt-get update && apt-get install --no-install-recommends -y \ + # psycopg2 dependencies + libpq-dev \ + # Translations dependencies + gettext \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction +# copy python dependency wheels from python-build-stage +COPY --from=python-build-stage /usr/src/app/wheels /wheels/ + +# use wheels to install python dependencies +RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ + && rm -rf /wheels/ + + +COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint +RUN sed -i 's/\r$//g' /entrypoint +RUN chmod +x /entrypoint + + +COPY --chown=django:django ./compose/production/django/start /start +RUN sed -i 's/\r$//g' /start +RUN chmod +x /start + + +# copy application code to WORKDIR +COPY --chown=django:django . ${APP_HOME} + +# make django owner of the WORKDIR directory as well. +RUN chown django:django ${APP_HOME} + +USER django + +RUN DATABASE_URL="" \ + DJANGO_SETTINGS_MODULE="config.settings.test" \ + python manage.py compilemessages + +ENTRYPOINT ["/entrypoint"] diff --git a/containers/app/Dockerfile.local.bkp b/containers/app/Dockerfile.local.bkp new file mode 100644 index 0000000..87a1b24 --- /dev/null +++ b/containers/app/Dockerfile.local.bkp @@ -0,0 +1,62 @@ +# define an alias for the specific python version used in this file. +FROM docker.io/python:3.11.8-slim-bookworm as python + + +# Python build stage +FROM docker.io/python as python-build-stage + +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential \ + # psycopg2 dependencies + libpq-dev \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements /requirements + +# create python dependency wheels +RUN pip wheel --no-cache-dir --wheel-dir /usr/src/app/wheels \ + -r /requirements/local.txt -r /requirements/production.txt \ + && rm -rf /requirements + + +# Python 'run' stage +FROM docker.io/python as python-run-stage + +ARG BUILD_ENVIRONMENT +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + # To run the Makefile + make \ + # psycopg2 dependencies + libpq-dev \ + # Translations dependencies + gettext \ + # Uncomment below lines to enable Sphinx output to latex and pdf + # texlive-latex-recommended \ + # texlive-fonts-recommended \ + # texlive-latex-extra \ + # latexmk \ + # cleaning up unused files + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# copy python dependency wheels from python-build-stage +COPY --from=python-build-stage /usr/src/app/wheels /wheels + +# use wheels to install python dependencies +RUN pip install --no-cache /wheels/* \ + && rm -rf /wheels + +COPY ./compose/local/docs/start /start-docs +RUN sed -i 's/\r$//g' /start-docs +RUN chmod +x /start-docs + +WORKDIR /docs diff --git a/containers/app/scripts/entrypoint.sh b/containers/app/scripts/entrypoint.sh new file mode 100755 index 0000000..21cc518 --- /dev/null +++ b/containers/app/scripts/entrypoint.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -ex + +# prepare the conda environment +is_conda_in_path=$(echo $PATH|grep -m 1 --count /opt/conda/) + +if [ $is_conda_in_path == 0 ]; then + export PATH="/opt/conda/condabin:/opt/conda/bin:$PATH" + echo "[II] included conda to the PATH" +fi + +echo "[II] activate growth-plan-linker" +source activate growth-plan-linker + +pushd /opt/appfiles +if [[ "${ENV:-dev}" != "dev" ]]; then + poetry install --no-root --only main +else + poetry install --no-root +fi +popd + +set +ex + +if [ $# -ne 0 ] + then + echo "Running: ${@}" + ${@} +fi diff --git a/containers/app/scripts/install-deps.sh b/containers/app/scripts/install-deps.sh new file mode 100755 index 0000000..640e466 --- /dev/null +++ b/containers/app/scripts/install-deps.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex +poetry config virtualenvs.create false + +pushd /opt/appfiles +if [[ "${ENV:-dev}" != "dev" ]]; then + poetry install --no-root --only main +else + poetry install --no-root +fi +popd +set +ex diff --git a/containers/app/scripts/start.sh b/containers/app/scripts/start.sh new file mode 100644 index 0000000..22eb7e5 --- /dev/null +++ b/containers/app/scripts/start.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + + +python /app/manage.py collectstatic --noinput + +compress_enabled() { +python << END +import sys + +from environ import Env + +env = Env(COMPRESS_ENABLED=(bool, True)) +if env('COMPRESS_ENABLED'): + sys.exit(0) +else: + sys.exit(1) + +END +} + +if compress_enabled; then + # NOTE this command will fail if django-compressor is disabled + python /app/manage.py compress +fi +exec /usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker diff --git a/containers/compose.base.yaml b/containers/compose.base.yaml new file mode 100644 index 0000000..8850de3 --- /dev/null +++ b/containers/compose.base.yaml @@ -0,0 +1,39 @@ +version: "3.9" + +services: + app: + hostname: growthplan-app + build: + context: .. + dockerfile: containers/app/Dockerfile + args: + ENV: ${ENV:-dev} + ports: + - "8000:8000" + volumes: + - ../src:/app + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres-admin} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-1kdis94id} + POSTGRES_HOST: ${POSTGRES_HOST:-growthplan-postgres} + POSTGRES_PORT: ${POSTGRES_HOST:-5432} + POSTGRES_DBNAME: ${POSTGRES_DBNAME:-postgres} + ENV: ${ENV:-dev} + depends_on: + - postgres + + postgres: + hostname: growthplan-postgres + build: + context: .. + dockerfile: containers/postgres/Dockerfile + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres-admin} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-1kdis94id} + POSTGRES_HOST: ${POSTGRES_HOST:-growthplan-postgres} + POSTGRES_PORT: ${POSTGRES_HOST:-5432} + volumes: + - growthplandb:/var/lib/postgresql/data + +volumes: + growthplandb: diff --git a/src/growth_plan_linker/migrations/0001_initial.py b/src/growth_plan_linker/migrations/0001_initial.py new file mode 100644 index 0000000..31523d2 --- /dev/null +++ b/src/growth_plan_linker/migrations/0001_initial.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0.2 on 2024-02-20 01:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Link', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('periodicity', models.CharField(max_length=50)), + ('times', models.IntegerField()), + ('person_one', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='person_one_links', to=settings.AUTH_USER_MODEL)), + ('person_two', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='person_two_links', to=settings.AUTH_USER_MODEL)), + ('supervisor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervised_links', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Feedback', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('link', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback', to='growth_plan_linker.link')), + ], + ), + migrations.CreateModel( + name='Project', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('supervisors', models.ManyToManyField(related_name='supervised_projects', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('projects', models.ManyToManyField(related_name='participants', to='growth_plan_linker.project')), + ], + ), + ] diff --git a/src/growth_plan_linker/migrations/__init__.py b/src/growth_plan_linker/migrations/__init__.py new file mode 100644 index 0000000..e69de29 From f0b20761b51d4b661f07b7434ed5462ccef1be7b Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 21:38:17 -0400 Subject: [PATCH 10/12] fix typo --- .makim.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.makim.yaml b/.makim.yaml index 0de5fa8..69d5841 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -65,7 +65,7 @@ groups: CMD="python manage.py test --no-input {{ args.app or args.module }} \ --settings={{ args.settings or env.DJANGO_SETTINGS_MODULE }} {{ args.params }}" sugar ext start --services postgres --options -d - sugar run --service literev --cmd $CMD + sugar run --service app --cmd $CMD ci: help: run the sames tests executed on CI From 13c42fd577e92f52c7fd5c6c59cf4a3eca6d405e Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 21:41:22 -0400 Subject: [PATCH 11/12] fix ci --- .github/workflows/main.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 01baacf..7aba283 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,6 +1,10 @@ -name: build +name: main -on: [push, pull_request] +on: + push: + branches: [main] + pull_request: + branches: [main] jobs: build: From e2155f887dd02d29e1895835ea7e2e684abcc8b6 Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Mon, 19 Feb 2024 21:54:20 -0400 Subject: [PATCH 12/12] update ci --- .github/workflows/main.yaml | 12 +++++------- .github/workflows/release.yaml | 10 ++++------ .makim.yaml | 1 + 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7aba283..8b3a851 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -7,7 +7,7 @@ on: branches: [main] jobs: - build: + test: runs-on: ubuntu-latest timeout-minutes: 10 concurrency: @@ -21,16 +21,14 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: "latest" - mamba-version: "1.*" + miniforge-version: latest environment-file: conda/dev.yaml channels: conda-forge,nodefaults - channel-priority: true activate-environment: growth-plan-linker - use-mamba: true - miniforge-variant: Mambaforge + auto-update-conda: true + conda-solver: libmamba - name: Install dependencies run: poetry install --only dev diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 11a0850..df9b35b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -25,16 +25,14 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: "latest" - mamba-version: "1.*" + miniforge-version: latest environment-file: conda/dev.yaml channels: conda-forge,nodefaults - channel-priority: true activate-environment: growth-plan-linker - use-mamba: true - miniforge-variant: Mambaforge + auto-update-conda: true + conda-solver: libmamba - name: Install deps run: | diff --git a/.makim.yaml b/.makim.yaml index 69d5841..23ef7b1 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -61,6 +61,7 @@ groups: help: Specify parameters to be used for tests type: string default: "-v 3" + shell: bash run: | CMD="python manage.py test --no-input {{ args.app or args.module }} \ --settings={{ args.settings or env.DJANGO_SETTINGS_MODULE }} {{ args.params }}"