From 44fab332114fe647258a21e0684356a2f4bac038 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Tue, 22 Oct 2024 18:44:30 +0200 Subject: [PATCH 01/20] Rip out fastapi, move existing code to agent subdir --- .github/workflows/build.yaml | 8 +- Dockerfile => agent/Dockerfile | 0 .../docker-compose.example.yaml | 5 +- agent/entrypoint/main.py | 17 + {humitifier => agent/humitifier}/__init__.py | 0 {humitifier => agent/humitifier}/alerts.py | 0 {humitifier => agent/humitifier}/config.py | 0 {humitifier => agent/humitifier}/facts.py | 0 {humitifier => agent/humitifier}/logging.py | 0 {humitifier => agent/humitifier}/models.py | 0 {humitifier => agent/humitifier}/tasks.py | 20 - {humitifier => agent/humitifier}/utils.py | 0 agent/poetry.lock | 1400 ++++++++++++++ pyproject.toml => agent/pyproject.toml | 7 +- {supabase => agent/supabase}/config.toml | 0 .../migrations/20240229081901_init_tables.sql | 0 .../20240304085952_init_fn_sync_hosts.sql | 0 {supabase => agent/supabase}/seed.sql | 0 entrypoint/main.py | 31 - humitifier/dashboard.py | 142 -- humitifier/templates/base.jinja2 | 26 - humitifier/templates/components.jinja2 | 187 -- humitifier/templates/page_index.jinja2 | 19 - poetry.lock | 1640 ----------------- 24 files changed, 1422 insertions(+), 2080 deletions(-) rename Dockerfile => agent/Dockerfile (100%) rename docker-compose.example.yaml => agent/docker-compose.example.yaml (91%) create mode 100644 agent/entrypoint/main.py rename {humitifier => agent/humitifier}/__init__.py (100%) rename {humitifier => agent/humitifier}/alerts.py (100%) rename {humitifier => agent/humitifier}/config.py (100%) rename {humitifier => agent/humitifier}/facts.py (100%) rename {humitifier => agent/humitifier}/logging.py (100%) rename {humitifier => agent/humitifier}/models.py (100%) rename {humitifier => agent/humitifier}/tasks.py (87%) rename {humitifier => agent/humitifier}/utils.py (100%) create mode 100644 agent/poetry.lock rename pyproject.toml => agent/pyproject.toml (81%) rename {supabase => agent/supabase}/config.toml (100%) rename {supabase => agent/supabase}/migrations/20240229081901_init_tables.sql (100%) rename {supabase => agent/supabase}/migrations/20240304085952_init_fn_sync_hosts.sql (100%) rename {supabase => agent/supabase}/seed.sql (100%) delete mode 100644 entrypoint/main.py delete mode 100644 humitifier/dashboard.py delete mode 100644 humitifier/templates/base.jinja2 delete mode 100644 humitifier/templates/components.jinja2 delete mode 100644 humitifier/templates/page_index.jinja2 delete mode 100644 poetry.lock diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 57edb6e..304cf93 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,7 +8,7 @@ on: env: IMAGE_NAME: humitifier - DOCKERFILE_PATH: ./Dockerfile + DOCKERFILE_PATH: ./agent/Dockerfile jobs: build-and-push: @@ -17,12 +17,6 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Compile CSS from SCSS files - uses: gha-utilities/sass-build@v0.5.1 - with: - source: web/style.scss - destination: static/out.css - - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: diff --git a/Dockerfile b/agent/Dockerfile similarity index 100% rename from Dockerfile rename to agent/Dockerfile diff --git a/docker-compose.example.yaml b/agent/docker-compose.example.yaml similarity index 91% rename from docker-compose.example.yaml rename to agent/docker-compose.example.yaml index 2394a82..ce45be2 100644 --- a/docker-compose.example.yaml +++ b/agent/docker-compose.example.yaml @@ -1,9 +1,8 @@ -version: '3' services: web: image: humitifier:dev - ports: - - "8000:8000" +# ports: +# - "8000:8000" environment: - SSH_AUTH_SOCK=/ssh-agent - HUMITIFIER_CONFIG=/code/app_config.toml diff --git a/agent/entrypoint/main.py b/agent/entrypoint/main.py new file mode 100644 index 0000000..38390ce --- /dev/null +++ b/agent/entrypoint/main.py @@ -0,0 +1,17 @@ +import asyncio +from humitifier.tasks import app as task_app +from humitifier.logging import logging + + + +async def main(): + "Run scheduler" + logging.info("Starting scheduler") + + sched = asyncio.create_task(task_app.serve()) + + await asyncio.wait([sched]) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/humitifier/__init__.py b/agent/humitifier/__init__.py similarity index 100% rename from humitifier/__init__.py rename to agent/humitifier/__init__.py diff --git a/humitifier/alerts.py b/agent/humitifier/alerts.py similarity index 100% rename from humitifier/alerts.py rename to agent/humitifier/alerts.py diff --git a/humitifier/config.py b/agent/humitifier/config.py similarity index 100% rename from humitifier/config.py rename to agent/humitifier/config.py diff --git a/humitifier/facts.py b/agent/humitifier/facts.py similarity index 100% rename from humitifier/facts.py rename to agent/humitifier/facts.py diff --git a/humitifier/logging.py b/agent/humitifier/logging.py similarity index 100% rename from humitifier/logging.py rename to agent/humitifier/logging.py diff --git a/humitifier/models.py b/agent/humitifier/models.py similarity index 100% rename from humitifier/models.py rename to agent/humitifier/models.py diff --git a/humitifier/tasks.py b/agent/humitifier/tasks.py similarity index 87% rename from humitifier/tasks.py rename to agent/humitifier/tasks.py index ef93687..e3fd8c7 100644 --- a/humitifier/tasks.py +++ b/agent/humitifier/tasks.py @@ -10,10 +10,8 @@ from pssh.output import HostOutput from humitifier import facts from humitifier.config import CONFIG, Config -from humitifier.dashboard import template_env, host_filters from humitifier.logging import logging from humitifier.utils import FactError -from humitifier.models import get_hosts logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -68,7 +66,6 @@ async def sync_hosts(): @app.task(hourly) async def scan_hosts(): - app.session logger.info("Initiating scan of hosts") ts = datetime.now() conn = await asyncpg.connect(CONFIG.db) @@ -151,20 +148,3 @@ async def cleanup_db(): await conn.execute("VACUUM;") await conn.close() - - -@app.task(after_success(cleanup_db)) -async def pre_render_index(): - logger.info("Pre-rendering index") - template = template_env.get_template("page_index.jinja2") - hosts = await get_hosts() - filters = host_filters(None, hosts) - html = template.render( - current_hosts=hosts, - critical_count=len([h for h in hosts if h.severity == "critical"]), - warning_count=len([h for h in hosts if h.severity == "warning"]), - info_count=len([h for h in hosts if h.severity == "info"]), - filters=filters, - ) - with open("static/index_prerender.html", "w") as out: - out.write(html) diff --git a/humitifier/utils.py b/agent/humitifier/utils.py similarity index 100% rename from humitifier/utils.py rename to agent/humitifier/utils.py diff --git a/agent/poetry.lock b/agent/poetry.lock new file mode 100644 index 0000000..a3edf90 --- /dev/null +++ b/agent/poetry.lock @@ -0,0 +1,1400 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "ansicon" +version = "1.89.0" +description = "Python wrapper for loading Jason Hood's ANSICON" +optional = false +python-versions = "*" +files = [ + {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, + {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, +] + +[[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 = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "23.12.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, +] + +[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 = "blessed" +version = "1.20.0" +description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." +optional = false +python-versions = ">=2.7" +files = [ + {file = "blessed-1.20.0-py2.py3-none-any.whl", hash = "sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058"}, + {file = "blessed-1.20.0.tar.gz", hash = "sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680"}, +] + +[package.dependencies] +jinxed = {version = ">=1.1.0", markers = "platform_system == \"Windows\""} +six = ">=1.9.0" +wcwidth = ">=0.1.4" + +[[package]] +name = "bpython" +version = "0.24" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bpython-0.24-py3-none-any.whl", hash = "sha256:0d196ae3d1ce3dcd559a3fb89ed2c468dfbd1504af0d680b906dd65a9c7a32eb"}, + {file = "bpython-0.24.tar.gz", hash = "sha256:98736ffd7a8c48fd2bfb53d898a475f4241bde0b672125706af04d9d08fd3dbd"}, +] + +[package.dependencies] +curtsies = ">=0.4.0" +cwcwidth = "*" +greenlet = "*" +pygments = "*" +pyxdg = "*" +requests = "*" + +[package.extras] +clipboard = ["pyperclip"] +jedi = ["jedi (>=0.16)"] +urwid = ["urwid"] +watch = ["watchdog"] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +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.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[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 = "curtsies" +version = "0.4.2" +description = "Curses-like terminal wrapper, with colored strings!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "curtsies-0.4.2-py3-none-any.whl", hash = "sha256:f24d676a8c4711fb9edba1ab7e6134bc52305a222980b3b717bb303f5e94cec6"}, + {file = "curtsies-0.4.2.tar.gz", hash = "sha256:6ebe33215bd7c92851a506049c720cca4cf5c192c1665c1d7a98a04c4702760e"}, +] + +[package.dependencies] +blessed = ">=1.5" +cwcwidth = "*" + +[[package]] +name = "cwcwidth" +version = "0.1.9" +description = "Python bindings for wc(s)width" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cwcwidth-0.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704f0d6888aa5e81e76d9f76709385f9f55aca8c450ee82cc722054814a7791f"}, + {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0633158205b50f253ad04e301156807e309a9fb9479a520418e010da6df13604"}, + {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a5407d0933c3aab8ee92cffd9e4f09010f25af10ebdfa19776748402bba9261"}, + {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:72490e07dfbc599fdf6efe26a13cfbf725f0513b181c3386d65bfd84f6175924"}, + {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf71151ae06e95f266bef91392c1562539b2eed847fd1f00f7b5b4ca3fd41a67"}, + {file = "cwcwidth-0.1.9-cp310-cp310-win32.whl", hash = "sha256:3e3c186b5c171d85f2b7f093e7efb33fd9b6e55b791ff75a0f101b18ec0433cd"}, + {file = "cwcwidth-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:ae17e493ffc18497c2602f8f42a0d8e490ea42ab3ccfbe5e4a6069a6d24f3b36"}, + {file = "cwcwidth-0.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1c3eb0a8c1b25c4a17b6b9bbf7d25ce9df3ea43b6f87903c51bc12434a2cc29"}, + {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c8752815ce4e40e7b34b7fe039276a5fbfb1b077131614381b13ef3b7bb21ff"}, + {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368ace13080dbaacdc247370d8a965a749b124aa50d0b1b6eb87601826db870f"}, + {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ca9a653661e152a426bdb51a272f36bc79f9830e6a7169abe8110ec367c3518c"}, + {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f96386cc29e6eef8ef066d7dd3c767c5119d66506dabea20dd344dabb3f2d225"}, + {file = "cwcwidth-0.1.9-cp311-cp311-win32.whl", hash = "sha256:f6ba88970ec12fdbed5554beb1b9a25d8271fc3d0d9e60639db700a79bed1863"}, + {file = "cwcwidth-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:aa6725e7b3571fdf6ce7c02d1dd2d69e00d166bb6df44e46ab215067028b3a03"}, + {file = "cwcwidth-0.1.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:42de102d5191fc68ef3ff6530f60c4895148ddc21aa0acaaf4612e5f7f0c38c4"}, + {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877e48c615b3fec88b7e640f9cf9d96704497657fb5aad2b7c0b0c59ecabff69"}, + {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdbaf0a8dad20eb685df11a195a2449fe230b08a5b356d036c8d7e59d4128a88"}, + {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f6e0e023c4b127c47fd4c44cf537be209b9a28d8725f4f576f4d63744a23aa38"}, + {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b4f7d24236ce3c9d3b5e07fd75d232452f19bdddb6ae8bbfdcb97b6cb02835e8"}, + {file = "cwcwidth-0.1.9-cp312-cp312-win32.whl", hash = "sha256:ba9da6c911bf108334426890bc9f57b839a38e1afc4383a41bd70adbce470db3"}, + {file = "cwcwidth-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:40466f16e85c338e8fc3eee87a8c9ca23416cc68b3049f68cb4cead5fb8b71b3"}, + {file = "cwcwidth-0.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:167f59c3c1e1d8e231a1abd666af4e73dd8a94917efb6522e9b610ac4587903a"}, + {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afc745f18c9e3c38851a931c0c0a7e479d6494911ba1353f998d707f95a895b4"}, + {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8d55c47cbec4796e89cfedc89c52e6c4c2faeb77489a763415b9f76d8fc14db"}, + {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c858842849ce2cfdf207095253da83831d9407771c8073f6b75f24d3faf1a1eb"}, + {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc049ce273f32b632f5ead649b2120f8b2b78035d7b069fdc460c4be9affddb5"}, + {file = "cwcwidth-0.1.9-cp38-cp38-win32.whl", hash = "sha256:1bafe978a5b7915848244a952829e3f8757c0cebef581c8250da6064c906c38c"}, + {file = "cwcwidth-0.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:024d1b21e6123bf30a849e60eea3482f556bbd00d39215f86c904e5bd81fc1b6"}, + {file = "cwcwidth-0.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d367da5e6fb538388817bf5b2d6dd4db90e5e631d99c34055589d007b5c94bc"}, + {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad721d9dbc14eafd06176e4f5594942336b1e813de2a5ab7bd64254393c5713f"}, + {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711ace9796cb6767ff29095ff5b0ec4619e7297854eb4b91ba99154590eddcc9"}, + {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:945615a7b8cdcbcd8e06d399f96a2b09440c3a4c5cb3c2d0109f00d80da27a12"}, + {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ffaf706abe400282f299463594d8887566e2a280cd0255110bd4397cc7be2910"}, + {file = "cwcwidth-0.1.9-cp39-cp39-win32.whl", hash = "sha256:03093cac6f8e4326b1c30169e024fe2894f76c6ffddf6464e489bb33cb3a2897"}, + {file = "cwcwidth-0.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:0ddef2c504e6f4fd6122b46d55061f487add1ebb86596ae70ffc2a8b8955c8bc"}, + {file = "cwcwidth-0.1.9.tar.gz", hash = "sha256:f19d11a0148d4a8cacd064c96e93bca8ce3415a186ae8204038f45e108db76b8"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "faker" +version = "17.6.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Faker-17.6.0-py3-none-any.whl", hash = "sha256:5aaa16fa9cfde7d117eef70b6b293a705021e57158f3fa6b44ed1b70202d2065"}, + {file = "Faker-17.6.0.tar.gz", hash = "sha256:51f37ff9df710159d6d736d0ba1c75e063430a8c806b91334d7794305b5a6114"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "fancycompleter" +version = "0.9.1" +description = "colorful TAB completion for Python prompt" +optional = false +python-versions = "*" +files = [ + {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, + {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, +] + +[package.dependencies] +pyreadline = {version = "*", markers = "platform_system == \"Windows\""} +pyrepl = ">=0.8.2" + +[[package]] +name = "gevent" +version = "24.10.3" +description = "Coroutine-based network library" +optional = false +python-versions = ">=3.9" +files = [ + {file = "gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13"}, + {file = "gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba"}, + {file = "gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26"}, + {file = "gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290"}, + {file = "gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4"}, + {file = "gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99"}, + {file = "gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec"}, + {file = "gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015"}, + {file = "gevent-24.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70e9ed7ecb70e0df7dc97c3bc420de9a45a7c76bd5861c6cfec8c549700e681e"}, + {file = "gevent-24.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ac83b74304487afa211a01909c7dd257e574db0cd429d866c298e21df7aeedf"}, + {file = "gevent-24.10.3-cp39-cp39-win32.whl", hash = "sha256:a9a89d6e396ef6f1e3968521bf56e8c4bee25b193bbf5d428b7782d582410822"}, + {file = "gevent-24.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:40ea3e40e8bb4fdb143c2a8edf2ccfdebd56016c7317c341ce8094c7bee08818"}, + {file = "gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18"}, + {file = "gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1"}, +] + +[package.dependencies] +cffi = {version = ">=1.17.1", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} +greenlet = {version = ">=3.1.1", markers = "platform_python_implementation == \"CPython\""} +"zope.event" = "*" +"zope.interface" = "*" + +[package.extras] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] + +[[package]] +name = "greenlet" +version = "3.1.1" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "hypothesis" +version = "5.49.0" +description = "A library for property-based testing" +optional = false +python-versions = ">=3.6" +files = [ + {file = "hypothesis-5.49.0-py3-none-any.whl", hash = "sha256:e91111f2f01abf2566041c4c86366aa7f08bfd5b3d858cc77a545fcf67df335e"}, + {file = "hypothesis-5.49.0.tar.gz", hash = "sha256:36a4d5587c34193125d654b61bf9284e24a227d1edd339c49143378658a10c7d"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "importlib-metadata", "importlib-resources (>=3.3.0)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "tzdata (>=2020.4)"] +cli = ["black (>=19.10b0)", "click (>=7.0)"] +codemods = ["libcst (>=0.3.16)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["django (>=2.2)", "pytz (>=2014.1)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=19.10b0)"] +lark = ["lark-parser (>=0.6.5)"] +numpy = ["numpy (>=1.9.0)"] +pandas = ["pandas (>=0.25)"] +pytest = ["pytest (>=4.3)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["backports.zoneinfo (>=0.2.1)", "importlib-resources (>=3.3.0)", "tzdata (>=2020.4)"] + +[[package]] +name = "hypothesis-auto" +version = "1.1.5" +description = "Extends Hypothesis to add fully automatic testing of type annotated functions" +optional = false +python-versions = ">=3.6" +files = [ + {file = "hypothesis_auto-1.1.5-py3-none-any.whl", hash = "sha256:7e962789a9ac691d4d9b6996f06f7afa1708013b67fd2df93a8857a92d6d7854"}, + {file = "hypothesis_auto-1.1.5.tar.gz", hash = "sha256:534bdc381f635e6515e6fd88c326d5bb66ab6351693e72a43fb9b691b0f5911c"}, +] + +[package.dependencies] +hypothesis = ">=4.36,<6.0.0" +pydantic = ">=0.32.2,<2.0.0" + +[package.extras] +pytest = ["pytest (>=4.0.0)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[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 = "jinxed" +version = "1.3.0" +description = "Jinxed Terminal Library" +optional = false +python-versions = "*" +files = [ + {file = "jinxed-1.3.0-py2.py3-none-any.whl", hash = "sha256:b993189f39dc2d7504d802152671535b06d380b26d78070559551cbf92df4fc5"}, + {file = "jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf"}, +] + +[package.dependencies] +ansicon = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "mypy" +version = "1.12.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, + {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, + {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, + {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, + {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, + {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, + {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, + {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, + {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, + {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, + {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, + {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, + {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, + {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, + {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, + {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, + {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, + {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, + {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, + {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, + {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.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 = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "parallel-ssh" +version = "2.12.0" +description = "Asynchronous parallel SSH library" +optional = false +python-versions = "*" +files = [ + {file = "parallel-ssh-2.12.0.tar.gz", hash = "sha256:be2c06ee8765273d204e00f82afae2dace5eebaefc5343d1dfc64513642161e0"}, + {file = "parallel_ssh-2.12.0-py3-none-any.whl", hash = "sha256:49979a0c6244a8bbd05cfba7d8666e719ad5b539b9e34a45a6df2c6f075f7295"}, +] + +[package.dependencies] +gevent = ">=1.3.0" +ssh-python = "*" +ssh2-python = "*" + +[[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 = "pdbpp" +version = "0.10.3" +description = "pdb++, a drop-in replacement for pdb" +optional = false +python-versions = "*" +files = [ + {file = "pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1"}, + {file = "pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5"}, +] + +[package.dependencies] +fancycompleter = ">=0.8" +pygments = "*" +wmctrl = "*" + +[package.extras] +funcsigs = ["funcsigs"] +testing = ["funcsigs", "pytest"] + +[[package]] +name = "platformdirs" +version = "4.3.6" +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.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "1.10.18" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e405ffcc1254d76bb0e760db101ee8916b620893e6edfbfee563b3c6f7a67c02"}, + {file = "pydantic-1.10.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e306e280ebebc65040034bff1a0a81fd86b2f4f05daac0131f29541cafd80b80"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d9d9b87b50338b1b7de4ebf34fd29fdb0d219dc07ade29effc74d3d2609c62"}, + {file = "pydantic-1.10.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b661ce52c7b5e5f600c0c3c5839e71918346af2ef20062705ae76b5c16914cab"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c20f682defc9ef81cd7eaa485879ab29a86a0ba58acf669a78ed868e72bb89e0"}, + {file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c5ae6b7c8483b1e0bf59e5f1843e4fd8fd405e11df7de217ee65b98eb5462861"}, + {file = "pydantic-1.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:74fe19dda960b193b0eb82c1f4d2c8e5e26918d9cda858cbf3f41dd28549cb70"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72fa46abace0a7743cc697dbb830a41ee84c9db8456e8d77a46d79b537efd7ec"}, + {file = "pydantic-1.10.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef0fe7ad7cbdb5f372463d42e6ed4ca9c443a52ce544472d8842a0576d830da5"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a00e63104346145389b8e8f500bc6a241e729feaf0559b88b8aa513dd2065481"}, + {file = "pydantic-1.10.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae6fa2008e1443c46b7b3a5eb03800121868d5ab6bc7cda20b5df3e133cde8b3"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9f463abafdc92635da4b38807f5b9972276be7c8c5121989768549fceb8d2588"}, + {file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3445426da503c7e40baccefb2b2989a0c5ce6b163679dd75f55493b460f05a8f"}, + {file = "pydantic-1.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:467a14ee2183bc9c902579bb2f04c3d3dac00eff52e252850509a562255b2a33"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:efbc8a7f9cb5fe26122acba1852d8dcd1e125e723727c59dcd244da7bdaa54f2"}, + {file = "pydantic-1.10.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24a4a159d0f7a8e26bf6463b0d3d60871d6a52eac5bb6a07a7df85c806f4c048"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74be007703547dc52e3c37344d130a7bfacca7df112a9e5ceeb840a9ce195c7"}, + {file = "pydantic-1.10.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcb20d4cb355195c75000a49bb4a31d75e4295200df620f454bbc6bdf60ca890"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46f379b8cb8a3585e3f61bf9ae7d606c70d133943f339d38b76e041ec234953f"}, + {file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbfbca662ed3729204090c4d09ee4beeecc1a7ecba5a159a94b5a4eb24e3759a"}, + {file = "pydantic-1.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:c6d0a9f9eccaf7f438671a64acf654ef0d045466e63f9f68a579e2383b63f357"}, + {file = "pydantic-1.10.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d5492dbf953d7d849751917e3b2433fb26010d977aa7a0765c37425a4026ff1"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe734914977eed33033b70bfc097e1baaffb589517863955430bf2e0846ac30f"}, + {file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15fdbe568beaca9aacfccd5ceadfb5f1a235087a127e8af5e48df9d8a45ae85c"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c3e742f62198c9eb9201781fbebe64533a3bbf6a76a91b8d438d62b813079dbc"}, + {file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:19a3bd00b9dafc2cd7250d94d5b578edf7a0bd7daf102617153ff9a8fa37871c"}, + {file = "pydantic-1.10.18-cp37-cp37m-win_amd64.whl", hash = "sha256:2ce3fcf75b2bae99aa31bd4968de0474ebe8c8258a0110903478bd83dfee4e3b"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:335a32d72c51a313b33fa3a9b0fe283503272ef6467910338e123f90925f0f03"}, + {file = "pydantic-1.10.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:34a3613c7edb8c6fa578e58e9abe3c0f5e7430e0fc34a65a415a1683b9c32d9a"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ee4e6ca1d9616797fa2e9c0bfb8815912c7d67aca96f77428e316741082a1b"}, + {file = "pydantic-1.10.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23e8ec1ce4e57b4f441fc91e3c12adba023fedd06868445a5b5f1d48f0ab3682"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:44ae8a3e35a54d2e8fa88ed65e1b08967a9ef8c320819a969bfa09ce5528fafe"}, + {file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5389eb3b48a72da28c6e061a247ab224381435256eb541e175798483368fdd3"}, + {file = "pydantic-1.10.18-cp38-cp38-win_amd64.whl", hash = "sha256:069b9c9fc645474d5ea3653788b544a9e0ccd3dca3ad8c900c4c6eac844b4620"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80b982d42515632eb51f60fa1d217dfe0729f008e81a82d1544cc392e0a50ddf"}, + {file = "pydantic-1.10.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aad8771ec8dbf9139b01b56f66386537c6fe4e76c8f7a47c10261b69ad25c2c9"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941a2eb0a1509bd7f31e355912eb33b698eb0051730b2eaf9e70e2e1589cae1d"}, + {file = "pydantic-1.10.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65f7361a09b07915a98efd17fdec23103307a54db2000bb92095457ca758d485"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6951f3f47cb5ca4da536ab161ac0163cab31417d20c54c6de5ddcab8bc813c3f"}, + {file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a4c5eec138a9b52c67f664c7d51d4c7234c5ad65dd8aacd919fb47445a62c86"}, + {file = "pydantic-1.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:49e26c51ca854286bffc22b69787a8d4063a62bf7d83dc21d44d2ff426108518"}, + {file = "pydantic-1.10.18-py3-none-any.whl", hash = "sha256:06a189b81ffc52746ec9c8c007f16e5167c8b0a696e1a726369327e3db7b2a82"}, + {file = "pydantic-1.10.18.tar.gz", hash = "sha256:baebdff1907d1d96a139c25136a9bb7d17e118f133a76a2ef3b845e831e3403a"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyreadline" +version = "2.1" +description = "A python implmementation of GNU readline." +optional = false +python-versions = "*" +files = [ + {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, +] + +[[package]] +name = "pyrepl" +version = "0.9.0" +description = "A library for building flexible command line interfaces" +optional = false +python-versions = "*" +files = [ + {file = "pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775"}, +] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<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 = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyxdg" +version = "0.28" +description = "PyXDG contains implementations of freedesktop.org standards in python." +optional = false +python-versions = "*" +files = [ + {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, + {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, +] + +[[package]] +name = "redbird" +version = "0.7.1" +description = "Repository Patterns for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redbird-0.7.1-py3-none-any.whl", hash = "sha256:6a1fe83fa9dfc0c5b9cb256b54376c299c423f2daba7bcbc081a820ff5b3c6c1"}, + {file = "redbird-0.7.1.tar.gz", hash = "sha256:9ef1098f2a0e68afe40349475018f7694b05bdf92250772cd8d0126f26de58a9"}, +] + +[package.dependencies] +pydantic = "*" +typing-extensions = "*" + +[package.extras] +full = ["pydantic-sqlalchemy", "pymongo", "requests", "sqlalchemy"] +mongodb = ["pymongo"] +rest = ["requests"] +sql = ["pydantic-sqlalchemy", "sqlalchemy"] +test = ["mongomock", "pydantic-sqlalchemy", "pymongo", "pytest", "python-dotenv", "requests", "responses", "sqlalchemy"] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[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 = "rocketry" +version = "2.5.1" +description = "Advanced scheduling framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "rocketry-2.5.1-py3-none-any.whl", hash = "sha256:d8755e909026ba401174218bc0a0958044973244cd07e1405e80f85512440253"}, + {file = "rocketry-2.5.1.tar.gz", hash = "sha256:11d1fb3d2856c5b2727bb4814c4f2bfbd2803067eebeac872d34a4c7a6756825"}, +] + +[package.dependencies] +pydantic = "*" +python-dateutil = "*" +redbird = ">=0.5.0" + +[package.extras] +docs = ["pydata-sphinx-theme", "sphinx (>=1.7.5)", "sphinx-book-theme", "sphinx-copybutton", "sphinx-material"] +test = ["pytest", "pytest-asyncio"] + +[[package]] +name = "setuptools" +version = "75.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] + +[[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 = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "ssh-python" +version = "1.0.0" +description = "libssh C library bindings for Python." +optional = false +python-versions = "*" +files = [ + {file = "ssh-python-1.0.0.tar.gz", hash = "sha256:68bee3f5a657e657d6dcc94e1b6c9ec332fd5d59bf4976a54150b997a800bf68"}, + {file = "ssh_python-1.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:ab7e01059b5a40c3de08a094b51a57ee23b41b1b0c52dc8f4a07e1524932778d"}, + {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c51998b2ea7de00ef5ff557893df28d8b07c4392a31bccbb60e1142451b949df"}, + {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6ee326dc3767f28f58691d92ad4543ac8a2ebbac51aa49d6f6f448593cb6bf7"}, + {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73f5ea92e82c2a928ef9d01dee8e684713e1b08a97f9c7d583b34a4720658cb"}, + {file = "ssh_python-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:43da4a16e88dccd54db4e2dac50e09ae424811a7d973a1a1f1c081ebec2a52bb"}, + {file = "ssh_python-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5985e71fee63d34ae15eb660cdf21c3ed4f712b2326c48d10be3c5a2bd1fa51"}, + {file = "ssh_python-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92b95293395924953f537b732a3d8fa1aba7ddb26b2137be80f53111852fa289"}, + {file = "ssh_python-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:82f77cd5d9ddedcc312c37a2848b9d76a0a6162c81f52a8051cc18b644b18b4f"}, + {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6cb934aaa0d8a89ecdc4d37f06d009a7bad50eea18fa3e9763193e85633669be"}, + {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3259ce8cf7b2169abdaab117db466c06c6185db1a65b8e97fa5e475f1ec399fb"}, + {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d176c778f02b3066c608c119e94d5b8c3d0d211e83b363bee0bd0d78e0eaa56"}, + {file = "ssh_python-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:58f2fe1899add41d3954ae655caf09119491a468cc93b4575b199f29ecb4c49f"}, + {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd56baaef157052755cfdbef718911a062e008cc59b267deb89952b269313995"}, + {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44c1509fa26ad699de921e53347a86cef18796b5c8582d9a2cdd3e21cbd667a"}, + {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e438710ffbf34f800e2c051091da478191c02a429abd49f565f82a0b149d01f"}, + {file = "ssh_python-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:364d3b075001d9a1e7d83a545e43cb28420171ddd779a286d4e267e52b9df2b7"}, + {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d8e386c137414d7ef3bbac939be37f5e2bd11d1f83c78274c1e68efe184bc5d2"}, + {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ebd8ebfad53f47c938634645c8b4b5870e444e190de90a0d5ceb5422a48fe1"}, + {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d0fca290cb88d73c12d7fad162ad36d79beb32b4fb3e39de87cd37330404f5"}, + {file = "ssh_python-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3af87fd53e314cef8cc65f00cd69a2b1aca84863028b161721800bb7e89526f2"}, + {file = "ssh_python-1.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a50af7f32517cd4067b3e68b723ec5c4efbbde2fb92263cbf11d7da5eacd65b4"}, + {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c83f5f7f3ded106d1088f33bc8aba03ae60126baa767d91a9bb9dc0372d73bda"}, + {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2566ffb2725a05e0b650ccf63b5d4f46fd5e47328d22d65f8111183e7dc4a62d"}, + {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bfa28c5b159ab804bc5b496819e5af8ebb7e5f6b442e8a87136296cebb172c"}, + {file = "ssh_python-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:c755afbe8ce3e926c80d8301f7bf6941781a2a1d497d3cb2692ea73bdf064f97"}, + {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6190ae4afedf0c35f84a1a5cd52c2b68935cd5c4b1330b491ca381006fa8fae6"}, + {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422140695d14796e9eb6f608365c37d5b780092ca6dbfedde9e6ffd15722f2b0"}, + {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e36540aba6f3a7bc18cdb8321808f9b7ea3d551fad53069b0806189c9eab989"}, + {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b695ff0561f16746b0d47f4a51cca205088a06a06c6d0ab29127b6f21bdf286"}, + {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56298bc06afa7986f76b1a5add539f462bc040c9c8bd9df1308a5b7b62af090"}, + {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1abed03383d3c40e3a3687dfade03b881b055f29baeb3d3a4d60eb80580b01a8"}, + {file = "ssh_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26380e2f06ea8b4f79c6ada4b2016079c0f6d47752785dd129c914f9aeda23f4"}, + {file = "ssh_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54731a4c3c06968ff41fd72a62ac40c1aa163d4fb51c82a4367e74398c9968ae"}, +] + +[[package]] +name = "ssh2-python" +version = "1.0.0" +description = "Bindings for libssh2 C library" +optional = false +python-versions = "*" +files = [ + {file = "ssh2-python-1.0.0.tar.gz", hash = "sha256:af89e80c3203e7829b24eea688eaf5c5e279071aed1882238b4f44ec2144e7c5"}, + {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:743df09165bbfcf754c6faf8a5feba414605ed73a6eee79e323d525c60f0cb41"}, + {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3c2910976da579c337dbd2134601d462b7bd861772dcb6e6dbb298749d1fd9"}, + {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:631375d2338aa373b9d2f2fe2af7ffc7c552845661a2c7456757b4c39c9ae15b"}, + {file = "ssh2_python-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5fca1744fe977eec920d7fdfeb1a79dd3fe4747f9455fb0e76aaa02b2e2dee7a"}, + {file = "ssh2_python-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:016c2fb027bb8a1720b71cf144c423f8d1acfdfef4238ada47c8452c7c5273fd"}, + {file = "ssh2_python-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbbdaaedc094364ee5027942bc40d68d1c5f9da7ec4cccb615036309b980bd8b"}, + {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9abd88161ce5e40f08a3b0af2173acafc7b19dd3dd140ccbd7c0b40094e558f"}, + {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ca21a64c1aa77763691de70ed29143b457fafc323a108bfeb4d249e1cca84f"}, + {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1ba1b05256277c78f4d4e158c4b024427bda94761426b9b37e77a551cc85158"}, + {file = "ssh2_python-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3270c77d24a35b1b52c312c42f14f0c27b013ab00cb8fdb67d4eb55e999b715a"}, + {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8582b2965116435c35f9f2f3da4297eae8a5daea0fcebfdfd9388a9dfc5cd922"}, + {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114b27c130144f6fad0fbc5d35e71d0a841059efd3f868b86b5443de89b208af"}, + {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddcba606818f28719d25c4a14789cc9b88714385e26e52d45ce967068ed7ad83"}, + {file = "ssh2_python-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:aca4967a67a8fe4a4cabdac710065cca426e8200e5d426f6daae4c6a3a7e1921"}, + {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:190b1c1389206aed43b40a1351732c1d854578480a566d5f6b899587ff989c74"}, + {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f288aef1ab4d1e8bbc8a38bcebc1b21849fa8448fb68e851b9ff136eab46da6"}, + {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:161eebd09258fe7fe47652561cefca9116c0df3b1128e1514d10d3d97e4f1521"}, + {file = "ssh2_python-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b315b754392e3b3c3ac546f4d35acdedb174d104212242a6f4778695beda9bb7"}, + {file = "ssh2_python-1.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:c5635f196a96bbe6d12b6cdb2f9d5fd464a0e82edca58f56391cc25aa61a4379"}, + {file = "ssh2_python-1.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:fc8f46d0d883d77462122a4cdb1dcff9a890923bd3525f061a2eb3563c7c5612"}, + {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0f80a96dbffb34f09d84532178d7d24e404b0b313c76037a8b87187990ddddbd"}, + {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc837e44189c9092af9af5ff25138a8efd05ad6fb10623c49039aa8f2f6ce03e"}, + {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:092c7f63c7ab8291748c7af8eb7809c81c53a761e9f4a8eecfc8705c92c636cb"}, + {file = "ssh2_python-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:6d52034fdb35872fb7082d0a325cb90e0dca41050bc1d7f65cf90b951d5f7caa"}, + {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:489ae221149a1049bdda48f12bb99e20d51769ac67f7f0c65468f298f81c10c0"}, + {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2593b1659f8551a06730579ad70390939f50094b71dc8acd9b86cba0d95ab5c0"}, + {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b304bcda607935375eeb5caa8f798021174893de7ea1b72416ad28709793695"}, + {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b139b8b47e230750b5b3b2f9977a7af996e614e334565bce5bf70e018d6536c7"}, + {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93d9d7c48f7b729e9e0250b09dff5e712aab7df10718764f6ec00f88521b8a12"}, + {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c1e9cdcf416e72934bf2af8faf977ea3836c98e248d4966606410b2a71f5de"}, + {file = "ssh2_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3771b58ae9944aff2aa86aadadc4f2dea174e3a30c8503bed022587bedd7e1c5"}, + {file = "ssh2_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99ee7e9aaaba60fc91ecb21d6233272bfa8acb7aec28b599eee9f374327e5f9c"}, +] + +[[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.2" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[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 = "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 = "wmctrl" +version = "0.5" +description = "A tool to programmatically control windows inside X" +optional = false +python-versions = ">=2.7" +files = [ + {file = "wmctrl-0.5-py2.py3-none-any.whl", hash = "sha256:ae695c1863a314c899e7cf113f07c0da02a394b968c4772e1936219d9234ddd7"}, + {file = "wmctrl-0.5.tar.gz", hash = "sha256:7839a36b6fe9e2d6fd22304e5dc372dbced2116ba41283ea938b2da57f53e962"}, +] + +[package.dependencies] +attrs = "*" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "zope-event" +version = "5.0" +description = "Very basic event publishing system" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, + {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, +] + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx"] +test = ["zope.testrunner"] + +[[package]] +name = "zope-interface" +version = "7.1.0" +description = "Interfaces for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zope.interface-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bd9e9f366a5df08ebbdc159f8224904c1c5ce63893984abb76954e6fbe4381a"}, + {file = "zope.interface-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:661d5df403cd3c5b8699ac480fa7f58047a3253b029db690efa0c3cf209993ef"}, + {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91b6c30689cfd87c8f264acb2fc16ad6b3c72caba2aec1bf189314cf1a84ca33"}, + {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6a4924f5bad9fe21d99f66a07da60d75696a136162427951ec3cb223a5570d"}, + {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a3c00b35f6170be5454b45abe2719ea65919a2f09e8a6e7b1362312a872cd3"}, + {file = "zope.interface-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b936d61dbe29572fd2cfe13e30b925e5383bed1aba867692670f5a2a2eb7b4e9"}, + {file = "zope.interface-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ac20581fc6cd7c754f6dff0ae06fedb060fa0e9ea6309d8be8b2701d9ea51c4"}, + {file = "zope.interface-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:848b6fa92d7c8143646e64124ed46818a0049a24ecc517958c520081fd147685"}, + {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1ef1fdb6f014d5886b97e52b16d0f852364f447d2ab0f0c6027765777b6667"}, + {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bcff5c09d0215f42ba64b49205a278e44413d9bf9fa688fd9e42bfe472b5f4f"}, + {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07add15de0cc7e69917f7d286b64d54125c950aeb43efed7a5ea7172f000fbc1"}, + {file = "zope.interface-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:9940d5bc441f887c5f375ec62bcf7e7e495a2d5b1da97de1184a88fb567f06af"}, + {file = "zope.interface-7.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f245d039f72e6f802902375755846f5de1ee1e14c3e8736c078565599bcab621"}, + {file = "zope.interface-7.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6159e767d224d8f18deff634a1d3722e68d27488c357f62ebeb5f3e2f5288b1f"}, + {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e956b1fd7f3448dd5e00f273072e73e50dfafcb35e4227e6d5af208075593c9"}, + {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff115ef91c0eeac69cd92daeba36a9d8e14daee445b504eeea2b1c0b55821984"}, + {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec001798ab62c3fc5447162bf48496ae9fba02edc295a9e10a0b0c639a6452e"}, + {file = "zope.interface-7.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:124149e2d42067b9c6597f4dafdc7a0983d0163868f897b7bb5dc850b14f9a87"}, + {file = "zope.interface-7.1.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9733a9a0f94ef53d7aa64661811b20875b5bc6039034c6e42fb9732170130573"}, + {file = "zope.interface-7.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5fcf379b875c610b5a41bc8a891841533f98de0520287d7f85e25386cd10d3e9"}, + {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0a45b5af9f72c805ee668d1479480ca85169312211bed6ed18c343e39307d5f"}, + {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af4a12b459a273b0b34679a5c3dc5e34c1847c3dd14a628aa0668e19e638ea2"}, + {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a735f82d2e3ed47ca01a20dfc4c779b966b16352650a8036ab3955aad151ed8a"}, + {file = "zope.interface-7.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:5501e772aff595e3c54266bc1bfc5858e8f38974ce413a8f1044aae0f32a83a3"}, + {file = "zope.interface-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec59fe53db7d32abb96c6d4efeed84aab4a7c38c62d7a901a9b20c09dd936e7a"}, + {file = "zope.interface-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e53c291debef523b09e1fe3dffe5f35dde164f1c603d77f770b88a1da34b7ed6"}, + {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:711eebc77f2092c6a8b304bad0b81a6ce3cf5490b25574e7309fbc07d881e3af"}, + {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a00ead2e24c76436e1b457a5132d87f83858330f6c923640b7ef82d668525d1"}, + {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e28ea0bc4b084fc93a483877653a033062435317082cdc6388dec3438309faf"}, + {file = "zope.interface-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:27cfb5205d68b12682b6e55ab8424662d96e8ead19550aad0796b08dd2c9a45e"}, + {file = "zope.interface-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e3e48f3dea21c147e1b10c132016cb79af1159facca9736d231694ef5a740a8"}, + {file = "zope.interface-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a99240b1d02dc469f6afbe7da1bf617645e60290c272968f4e53feec18d7dce8"}, + {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc8a318162123eddbdf22fcc7b751288ce52e4ad096d3766ff1799244352449d"}, + {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7b25db127db3e6b597c5f74af60309c4ad65acd826f89609662f0dc33a54728"}, + {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a29ac607e970b5576547f0e3589ec156e04de17af42839eedcf478450687317"}, + {file = "zope.interface-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a14c9decf0eb61e0892631271d500c1e306c7b6901c998c7035e194d9150fdd1"}, + {file = "zope_interface-7.1.0.tar.gz", hash = "sha256:3f005869a1a05e368965adb2075f97f8ee9a26c61898a9e52a9764d93774f237"}, +] + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx", "furo", "repoze.sphinx.autointerface"] +test = ["coverage[toml]", "zope.event", "zope.testing"] +testing = ["coverage[toml]", "zope.event", "zope.testing"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "e9339bf4b2411c31c765b828121c7579bfb56d950cce3adcbbe00e1e8b5a05ee" diff --git a/pyproject.toml b/agent/pyproject.toml similarity index 81% rename from pyproject.toml rename to agent/pyproject.toml index 158009d..a4b01b8 100644 --- a/pyproject.toml +++ b/agent/pyproject.toml @@ -1,20 +1,17 @@ [tool.poetry] name = "humitifier" -version = "2.1.0" +version = "3.0.0" description = "Tools and interfaces for displaying server resources" authors = ["Donatas Rasiukevicius "] [tool.poetry.dependencies] python = "^3.10" -fastapi = "^0.93.0" -uvicorn = "^0.21.1" -jinja2 = "^3.1.2" toml = "^0.10.2" parallel-ssh = "^2.12.0" rocketry = "^2.5.1" +pydantic = "<2.0.0" # Needed to keep compatibility with rocketry asyncpg = "^0.29.0" -sentry-sdk = { extras = ["fastapi"], version = "^1.41.0" } [tool.poetry.group.dev.dependencies] diff --git a/supabase/config.toml b/agent/supabase/config.toml similarity index 100% rename from supabase/config.toml rename to agent/supabase/config.toml diff --git a/supabase/migrations/20240229081901_init_tables.sql b/agent/supabase/migrations/20240229081901_init_tables.sql similarity index 100% rename from supabase/migrations/20240229081901_init_tables.sql rename to agent/supabase/migrations/20240229081901_init_tables.sql diff --git a/supabase/migrations/20240304085952_init_fn_sync_hosts.sql b/agent/supabase/migrations/20240304085952_init_fn_sync_hosts.sql similarity index 100% rename from supabase/migrations/20240304085952_init_fn_sync_hosts.sql rename to agent/supabase/migrations/20240304085952_init_fn_sync_hosts.sql diff --git a/supabase/seed.sql b/agent/supabase/seed.sql similarity index 100% rename from supabase/seed.sql rename to agent/supabase/seed.sql diff --git a/entrypoint/main.py b/entrypoint/main.py deleted file mode 100644 index 308035a..0000000 --- a/entrypoint/main.py +++ /dev/null @@ -1,31 +0,0 @@ -import uvicorn -import asyncio -from humitifier.dashboard import app as dashboard -from humitifier.tasks import app as task_app -from humitifier.logging import logging - - -class Server(uvicorn.Server): - """Customized uvicorn.Server - - Uvicorn server overrides signals and we need to include - Rocketry to the signals.""" - - def handle_exit(self, sig: int, frame) -> None: - task_app.session.shut_down() - return super().handle_exit(sig, frame) - - -async def main(): - "Run scheduler and the API" - logging.info("Starting scheduler and API") - server = Server(config=uvicorn.Config(dashboard, workers=1, loop="asyncio", host="0.0.0.0", port=8000)) - - dash = asyncio.create_task(server.serve()) - sched = asyncio.create_task(task_app.serve()) - - await asyncio.wait([dash, sched]) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/humitifier/dashboard.py b/humitifier/dashboard.py deleted file mode 100644 index e6e0eaf..0000000 --- a/humitifier/dashboard.py +++ /dev/null @@ -1,142 +0,0 @@ -import asyncpg -import json -import os -from dataclasses import dataclass -from typing import Callable, Literal -from fastapi import FastAPI, Request -from fastapi.staticfiles import StaticFiles -from fastapi.responses import HTMLResponse -from jinja2 import Environment, FileSystemLoader -from humitifier.alerts import ALERTS -from humitifier.config import CONFIG -from humitifier.models import Host, get_hosts -from humitifier.logging import logging - -import sentry_sdk - -logger = logging.getLogger(__name__) - -if sentry_dsn := os.getenv("SENTRY_DSN"): - logging.info("Sentry enabled") - sentry_sdk.init(dsn=sentry_dsn, traces_sample_rate=1.0, profiles_sample_rate=1.0) - - -template_env = Environment(loader=FileSystemLoader("humitifier/templates")) -template_env.filters["json"] = lambda x: json.dumps(x, indent=4, sort_keys=True, separators=(",", ": ")) -app = FastAPI() -app.mount("/static", StaticFiles(directory="static"), name="static") - - -@dataclass -class Filter: - typ: Literal["search", "select", "hidden"] - id: str - label: str - options: set[str] - value: str | None - fn: Callable[[Host], bool] - - -def host_filters(request: Request | None, all_hosts: list[Host]) -> list[Filter]: - - return [ - Filter( - typ="search", - id="fqdn", - label="Search Hosts", - options={str(h.fqdn) for h in all_hosts}, - value=request.query_params.get("fqdn") if request else None, - fn=lambda h, p: not p.get("fqdn") or p.get("fqdn") in h.fqdn, - ), - Filter( - typ="select", - id="os", - label="Operating System", - options={str(h.os) for h in all_hosts}, - value=request.query_params.get("os") if request else None, - fn=lambda h, p: not p.get("os") or p.get("os") == h.os, - ), - Filter( - typ="select", - id="alert", - label="Alert", - options={a.__name__ for a in ALERTS}, - value=request.query_params.get("alert") if request else None, - fn=lambda h, p: not p.get("alert") or p.get("alert") in h.alert_codes, - ), - Filter( - typ="select", - id="department", - label="Department", - options={str(h.department) for h in all_hosts}, - value=request.query_params.get("department") if request else None, - fn=lambda h, p: not p.get("department") or p.get("department") == h.department, - ), - Filter( - typ="select", - id="contact", - label="Contact", - options={str(h.contact) for h in all_hosts}, - value=request.query_params.get("contact") if request else None, - fn=lambda h, p: not p.get("contact") or p.get("contact") == h.contact, - ), - Filter( - typ="search", - id="package", - label="Package", - options={pkg.name for h in all_hosts for pkg in h.packages or []}, - value=request.query_params.get("package") if request else None, - fn=lambda h, p: not p.get("package") or p.get("package") in {pkg.name for pkg in h.packages}, - ), - Filter( - typ="hidden", - id="severity", - label="Severity", - options={"info", "warning", "critical"}, - value=request.query_params.get("severity") if request else None, - fn=lambda h, p: not p.get("severity") or p.get("severity") == h.severity, - ), - Filter( - typ="select", - label="Is Wordpress", - id="is_wp", - options={"true", "false"}, - value=request.query_params.get("is_wp") if request else None, - fn=lambda h, p: not p.get("is_wp") or p.get("is_wp") == str(h.is_wp).lower(), - ), - ] - - -def _current_hosts(request: Request, all_hosts: list[Host]) -> list[Host]: - filters = host_filters(request, all_hosts) - return [h for h in all_hosts if all(f.fn(h, request.query_params) for f in filters)] - - -@app.get("/", response_class=HTMLResponse) -async def index(request: Request): - if not {k: v for k, v in request.query_params.items() if v} and os.path.exists("static/index_prerender.html"): - with open("static/index_prerender.html") as f: - return HTMLResponse(f.read()) - hosts = await get_hosts() - current_hosts = _current_hosts(request, hosts) - filters = host_filters(request, hosts) - template = template_env.get_template("page_index.jinja2") - return HTMLResponse( - template.render( - current_hosts=current_hosts, - critical_count=len([h for h in current_hosts if h.severity == "critical"]), - warning_count=len([h for h in current_hosts if h.severity == "warning"]), - info_count=len([h for h in current_hosts if h.severity == "info"]), - filters=filters, - ) - ) - - -@app.on_event("startup") -async def run_migrations(): - logger.info("Applying migrations...") - conn = await asyncpg.connect(CONFIG.db) - for f in os.listdir(CONFIG.migrations_dir): - logger.info(f"Applying {f}") - with open(f"{CONFIG.migrations_dir}/{f}") as sql_file: - await conn.execute(sql_file.read()) diff --git a/humitifier/templates/base.jinja2 b/humitifier/templates/base.jinja2 deleted file mode 100644 index 7ad36b4..0000000 --- a/humitifier/templates/base.jinja2 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Humitifier - - - - - - - - - {% block content %}{% endblock %} - - - - \ No newline at end of file diff --git a/humitifier/templates/components.jinja2 b/humitifier/templates/components.jinja2 deleted file mode 100644 index 0ab774d..0000000 --- a/humitifier/templates/components.jinja2 +++ /dev/null @@ -1,187 +0,0 @@ -{% macro search_input(id, label, options, value) %} - - - - {% for opt in options|sort %} - - {% endfor %} - -{% endmacro %} - -{% macro select_input(id, label, options, value) %} - - -{% endmacro %} - -{% macro host_filter_form(filters)%} -
-

Filters

- {% for f in filters%} - {% if f.typ == 'search'%} - {{ search_input(f.id, f.label, f.options, f.value) }} - {% elif f.typ == 'select' %} - {{ select_input(f.id, f.label, f.options, f.value) }} - {% else %} - {% endif %} - {% endfor %} - - -
-{% endmacro %} - - -{% macro table_kv_text(label, value) %} - - {{ label }} - {{ value or "Unknown" }} - -{% endmacro %} - -{% macro table_kv_contact(email) %} - - Contact - - {% if email %} - {{ email }} - {% else %} - Unknown - {% endif %} - - -{% endmacro %} - -{% macro table_kv_code(label, code) %} - - {{ label }} - - {% if code %} -
{{ code }}
- {% else %} - Unknown - {% endif %} - - -{% endmacro %} - - -{% macro table_kv_db(databases) %} - - Databases - - {% if databases %} - {% for dbtype, dbs in databases.items() %} - - {% endfor %} - {% else %} - Unknown - {% endif %} - - -{% endmacro %} - -{% macro table_kv_alerts(alerts) %} - - Alerts - - {% if alerts|length > 0 %} - - {% else %} - No alerts - {% endif %} - - -{% endmacro %} - -{% macro table_kv_packages(packages) %} - - Packages - - {% if packages %} -
- Expand... - {% for pkg in packages %} - {{ pkg.name }}=={{ pkg.version }} - {% endfor %} -
- {% else %} - Unknown - {% endif %} - - -{% endmacro %} - -{% macro table_host_details(host) %} - - - - - {{ table_kv_text("Hostname", host.hostname) }} - {{ table_kv_text("Department", host.department) }} - {{ table_kv_contact(host.contact) }} - {{ table_kv_text("OS", host.os) }} - {{ table_kv_code("WebDAV", host.webdav) }} - {{ table_kv_code("VHosts", host.vhosts|json) }} - {{ table_kv_code("File Servers", host.fileservers|json) }} - {{ table_kv_db(host.databases) }} - {{ table_kv_alerts(host.alerts) }} - {{ table_kv_packages(host.packages) }} - -
-{% endmacro %} - -{% macro table_host_list(hosts) %} - - - - - - - - - - - {% for host in hosts %} - - - - - {% endfor %} -
ServerStatus
-
- {{ host.fqdn }} - {{ table_host_details(host) }} -
-
- {% if host.alerts|length > 0 %} - {{ host.alerts|length }} Alert(s) - {% else %} - No alerts - {% endif %} -
-{% endmacro %} - -{% macro stat_box(label, count, severity, href) %} - -{% endmacro %} \ No newline at end of file diff --git a/humitifier/templates/page_index.jinja2 b/humitifier/templates/page_index.jinja2 deleted file mode 100644 index 0c710a7..0000000 --- a/humitifier/templates/page_index.jinja2 +++ /dev/null @@ -1,19 +0,0 @@ -{% import "components.jinja2" as comp %} -{% extends "base.jinja2" %} -{% block content %} - -
- {{ comp.host_filter_form(filters) }} -
- -
- {{ comp.stat_box("Hosts", current_hosts|length, "ok") }} - {{ comp.stat_box("Critical", critical_count, "critical", "severity=critical") }} - {{ comp.stat_box("Warning", warning_count, "warning", "severity=warning") }} - {{ comp.stat_box("Info", info_count, "info", "severity=info")}} -
- -
- {{ comp.table_host_list(current_hosts) }} -
-{% endblock %} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 80c813b..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1640 +0,0 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. - -[[package]] -name = "ansicon" -version = "1.89.0" -description = "Python wrapper for loading Jason Hood's ANSICON" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, - {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, -] - -[[package]] -name = "anyio" -version = "4.3.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -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 = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -category = "main" -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 = "asyncpg" -version = "0.29.0" -description = "An asyncio PostgreSQL driver" -category = "main" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, - {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, - {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, - {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, - {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, - {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, - {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, - {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, - {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, - {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, - {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, - {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, - {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, - {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, - {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, - {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, - {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, - {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, - {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, - {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, - {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, - {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, - {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, - {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, - {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, - {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, - {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, - {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, - {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, - {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, - {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, - {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, - {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, - {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, - {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, - {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, - {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, - {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, - {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, - {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, - {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} - -[package.extras] -docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] - -[[package]] -name = "attrs" -version = "23.2.0" -description = "Classes Without Boilerplate" -category = "dev" -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 = "black" -version = "23.12.1" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, -] - -[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 = "blessed" -version = "1.20.0" -description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." -category = "dev" -optional = false -python-versions = ">=2.7" -files = [ - {file = "blessed-1.20.0-py2.py3-none-any.whl", hash = "sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058"}, - {file = "blessed-1.20.0.tar.gz", hash = "sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680"}, -] - -[package.dependencies] -jinxed = {version = ">=1.1.0", markers = "platform_system == \"Windows\""} -six = ">=1.9.0" -wcwidth = ">=0.1.4" - -[[package]] -name = "bpython" -version = "0.24" -description = "" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "bpython-0.24-py3-none-any.whl", hash = "sha256:0d196ae3d1ce3dcd559a3fb89ed2c468dfbd1504af0d680b906dd65a9c7a32eb"}, - {file = "bpython-0.24.tar.gz", hash = "sha256:98736ffd7a8c48fd2bfb53d898a475f4241bde0b672125706af04d9d08fd3dbd"}, -] - -[package.dependencies] -curtsies = ">=0.4.0" -cwcwidth = "*" -greenlet = "*" -pygments = "*" -pyxdg = "*" -requests = "*" - -[package.extras] -clipboard = ["pyperclip"] -jedi = ["jedi (>=0.16)"] -urwid = ["urwid"] -watch = ["watchdog"] - -[[package]] -name = "certifi" -version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -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." -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" -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" -category = "main" -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." -category = "main" -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 = "curtsies" -version = "0.4.2" -description = "Curses-like terminal wrapper, with colored strings!" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "curtsies-0.4.2-py3-none-any.whl", hash = "sha256:f24d676a8c4711fb9edba1ab7e6134bc52305a222980b3b717bb303f5e94cec6"}, - {file = "curtsies-0.4.2.tar.gz", hash = "sha256:6ebe33215bd7c92851a506049c720cca4cf5c192c1665c1d7a98a04c4702760e"}, -] - -[package.dependencies] -blessed = ">=1.5" -cwcwidth = "*" - -[[package]] -name = "cwcwidth" -version = "0.1.9" -description = "Python bindings for wc(s)width" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cwcwidth-0.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704f0d6888aa5e81e76d9f76709385f9f55aca8c450ee82cc722054814a7791f"}, - {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0633158205b50f253ad04e301156807e309a9fb9479a520418e010da6df13604"}, - {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a5407d0933c3aab8ee92cffd9e4f09010f25af10ebdfa19776748402bba9261"}, - {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:72490e07dfbc599fdf6efe26a13cfbf725f0513b181c3386d65bfd84f6175924"}, - {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf71151ae06e95f266bef91392c1562539b2eed847fd1f00f7b5b4ca3fd41a67"}, - {file = "cwcwidth-0.1.9-cp310-cp310-win32.whl", hash = "sha256:3e3c186b5c171d85f2b7f093e7efb33fd9b6e55b791ff75a0f101b18ec0433cd"}, - {file = "cwcwidth-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:ae17e493ffc18497c2602f8f42a0d8e490ea42ab3ccfbe5e4a6069a6d24f3b36"}, - {file = "cwcwidth-0.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1c3eb0a8c1b25c4a17b6b9bbf7d25ce9df3ea43b6f87903c51bc12434a2cc29"}, - {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c8752815ce4e40e7b34b7fe039276a5fbfb1b077131614381b13ef3b7bb21ff"}, - {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368ace13080dbaacdc247370d8a965a749b124aa50d0b1b6eb87601826db870f"}, - {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ca9a653661e152a426bdb51a272f36bc79f9830e6a7169abe8110ec367c3518c"}, - {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f96386cc29e6eef8ef066d7dd3c767c5119d66506dabea20dd344dabb3f2d225"}, - {file = "cwcwidth-0.1.9-cp311-cp311-win32.whl", hash = "sha256:f6ba88970ec12fdbed5554beb1b9a25d8271fc3d0d9e60639db700a79bed1863"}, - {file = "cwcwidth-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:aa6725e7b3571fdf6ce7c02d1dd2d69e00d166bb6df44e46ab215067028b3a03"}, - {file = "cwcwidth-0.1.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:42de102d5191fc68ef3ff6530f60c4895148ddc21aa0acaaf4612e5f7f0c38c4"}, - {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877e48c615b3fec88b7e640f9cf9d96704497657fb5aad2b7c0b0c59ecabff69"}, - {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdbaf0a8dad20eb685df11a195a2449fe230b08a5b356d036c8d7e59d4128a88"}, - {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f6e0e023c4b127c47fd4c44cf537be209b9a28d8725f4f576f4d63744a23aa38"}, - {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b4f7d24236ce3c9d3b5e07fd75d232452f19bdddb6ae8bbfdcb97b6cb02835e8"}, - {file = "cwcwidth-0.1.9-cp312-cp312-win32.whl", hash = "sha256:ba9da6c911bf108334426890bc9f57b839a38e1afc4383a41bd70adbce470db3"}, - {file = "cwcwidth-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:40466f16e85c338e8fc3eee87a8c9ca23416cc68b3049f68cb4cead5fb8b71b3"}, - {file = "cwcwidth-0.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:167f59c3c1e1d8e231a1abd666af4e73dd8a94917efb6522e9b610ac4587903a"}, - {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afc745f18c9e3c38851a931c0c0a7e479d6494911ba1353f998d707f95a895b4"}, - {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8d55c47cbec4796e89cfedc89c52e6c4c2faeb77489a763415b9f76d8fc14db"}, - {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c858842849ce2cfdf207095253da83831d9407771c8073f6b75f24d3faf1a1eb"}, - {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc049ce273f32b632f5ead649b2120f8b2b78035d7b069fdc460c4be9affddb5"}, - {file = "cwcwidth-0.1.9-cp38-cp38-win32.whl", hash = "sha256:1bafe978a5b7915848244a952829e3f8757c0cebef581c8250da6064c906c38c"}, - {file = "cwcwidth-0.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:024d1b21e6123bf30a849e60eea3482f556bbd00d39215f86c904e5bd81fc1b6"}, - {file = "cwcwidth-0.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d367da5e6fb538388817bf5b2d6dd4db90e5e631d99c34055589d007b5c94bc"}, - {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad721d9dbc14eafd06176e4f5594942336b1e813de2a5ab7bd64254393c5713f"}, - {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711ace9796cb6767ff29095ff5b0ec4619e7297854eb4b91ba99154590eddcc9"}, - {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:945615a7b8cdcbcd8e06d399f96a2b09440c3a4c5cb3c2d0109f00d80da27a12"}, - {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ffaf706abe400282f299463594d8887566e2a280cd0255110bd4397cc7be2910"}, - {file = "cwcwidth-0.1.9-cp39-cp39-win32.whl", hash = "sha256:03093cac6f8e4326b1c30169e024fe2894f76c6ffddf6464e489bb33cb3a2897"}, - {file = "cwcwidth-0.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:0ddef2c504e6f4fd6122b46d55061f487add1ebb86596ae70ffc2a8b8955c8bc"}, - {file = "cwcwidth-0.1.9.tar.gz", hash = "sha256:f19d11a0148d4a8cacd064c96e93bca8ce3415a186ae8204038f45e108db76b8"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -category = "main" -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 = "faker" -version = "17.6.0" -description = "Faker is a Python package that generates fake data for you." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Faker-17.6.0-py3-none-any.whl", hash = "sha256:5aaa16fa9cfde7d117eef70b6b293a705021e57158f3fa6b44ed1b70202d2065"}, - {file = "Faker-17.6.0.tar.gz", hash = "sha256:51f37ff9df710159d6d736d0ba1c75e063430a8c806b91334d7794305b5a6114"}, -] - -[package.dependencies] -python-dateutil = ">=2.4" - -[[package]] -name = "fancycompleter" -version = "0.9.1" -description = "colorful TAB completion for Python prompt" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, - {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, -] - -[package.dependencies] -pyreadline = {version = "*", markers = "platform_system == \"Windows\""} -pyrepl = ">=0.8.2" - -[[package]] -name = "fastapi" -version = "0.93.0" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "fastapi-0.93.0-py3-none-any.whl", hash = "sha256:d6e6db5f096d67b475e2a09e1124983554f634fad50297de85fc3de0583df13a"}, - {file = "fastapi-0.93.0.tar.gz", hash = "sha256:c2944febec6da706f4c82cdfa0de48afda960c8fbde29dec88697d55a67d7718"}, -] - -[package.dependencies] -pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" -starlette = ">=0.25.0,<0.26.0" - -[package.extras] -all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] -doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] -test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.6.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] - -[[package]] -name = "gevent" -version = "24.2.1" -description = "Coroutine-based network library" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789"}, - {file = "gevent-24.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d5325ccfadfd3dcf72ff88a92fb8fc0b56cacc7225f0f4b6dcf186c1a6eeabc"}, - {file = "gevent-24.2.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d"}, - {file = "gevent-24.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1dffb395e500613e0452b9503153f8f7ba587c67dd4a85fc7cd7aa7430cb02cc"}, - {file = "gevent-24.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:6c47ae7d1174617b3509f5d884935e788f325eb8f1a7efc95d295c68d83cce40"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7cac622e11b4253ac4536a654fe221249065d9a69feb6cdcd4d9af3503602e0"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf5b9c72b884c6f0c4ed26ef204ee1f768b9437330422492c319470954bc4cc7"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5de3c676e57177b38857f6e3cdfbe8f38d1cd754b63200c0615eaa31f514b4f"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4faf846ed132fd7ebfbbf4fde588a62d21faa0faa06e6f468b7faa6f436b661"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:368a277bd9278ddb0fde308e6a43f544222d76ed0c4166e0d9f6b036586819d9"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f8a04cf0c5b7139bc6368b461257d4a757ea2fe89b3773e494d235b7dd51119f"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9d8d0642c63d453179058abc4143e30718b19a85cbf58c2744c9a63f06a1d388"}, - {file = "gevent-24.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:94138682e68ec197db42ad7442d3cf9b328069c3ad8e4e5022e6b5cd3e7ffae5"}, - {file = "gevent-24.2.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19"}, - {file = "gevent-24.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800"}, - {file = "gevent-24.2.1-cp38-cp38-win32.whl", hash = "sha256:44098038d5e2749b0784aabb27f1fcbb3f43edebedf64d0af0d26955611be8d6"}, - {file = "gevent-24.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:117e5837bc74a1673605fb53f8bfe22feb6e5afa411f524c835b2ddf768db0de"}, - {file = "gevent-24.2.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7"}, - {file = "gevent-24.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1"}, - {file = "gevent-24.2.1-cp39-cp39-win32.whl", hash = "sha256:782a771424fe74bc7e75c228a1da671578c2ba4ddb2ca09b8f959abdf787331e"}, - {file = "gevent-24.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:3adfb96637f44010be8abd1b5e73b5070f851b817a0b182e601202f20fa06533"}, - {file = "gevent-24.2.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8"}, - {file = "gevent-24.2.1.tar.gz", hash = "sha256:432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056"}, -] - -[package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = [ - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, -] -"zope.event" = "*" -"zope.interface" = "*" - -[package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] -docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] - -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -category = "main" -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 = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -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 = "hypothesis" -version = "5.49.0" -description = "A library for property-based testing" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "hypothesis-5.49.0-py3-none-any.whl", hash = "sha256:e91111f2f01abf2566041c4c86366aa7f08bfd5b3d858cc77a545fcf67df335e"}, - {file = "hypothesis-5.49.0.tar.gz", hash = "sha256:36a4d5587c34193125d654b61bf9284e24a227d1edd339c49143378658a10c7d"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -sortedcontainers = ">=2.1.0,<3.0.0" - -[package.extras] -all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "importlib-metadata", "importlib-resources (>=3.3.0)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "tzdata (>=2020.4)"] -cli = ["black (>=19.10b0)", "click (>=7.0)"] -codemods = ["libcst (>=0.3.16)"] -dateutil = ["python-dateutil (>=1.4)"] -django = ["django (>=2.2)", "pytz (>=2014.1)"] -dpcontracts = ["dpcontracts (>=0.4)"] -ghostwriter = ["black (>=19.10b0)"] -lark = ["lark-parser (>=0.6.5)"] -numpy = ["numpy (>=1.9.0)"] -pandas = ["pandas (>=0.25)"] -pytest = ["pytest (>=4.3)"] -pytz = ["pytz (>=2014.1)"] -redis = ["redis (>=3.0.0)"] -zoneinfo = ["backports.zoneinfo (>=0.2.1)", "importlib-resources (>=3.3.0)", "tzdata (>=2020.4)"] - -[[package]] -name = "hypothesis-auto" -version = "1.1.5" -description = "Extends Hypothesis to add fully automatic testing of type annotated functions" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "hypothesis_auto-1.1.5-py3-none-any.whl", hash = "sha256:7e962789a9ac691d4d9b6996f06f7afa1708013b67fd2df93a8857a92d6d7854"}, - {file = "hypothesis_auto-1.1.5.tar.gz", hash = "sha256:534bdc381f635e6515e6fd88c326d5bb66ab6351693e72a43fb9b691b0f5911c"}, -] - -[package.dependencies] -hypothesis = ">=4.36,<6.0.0" -pydantic = ">=0.32.2,<2.0.0" - -[package.extras] -pytest = ["pytest (>=4.0.0)"] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -category = "dev" -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 = "jinja2" -version = "3.1.3" -description = "A very fast and expressive template engine." -category = "main" -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 = "jinxed" -version = "1.2.1" -description = "Jinxed Terminal Library" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "jinxed-1.2.1-py2.py3-none-any.whl", hash = "sha256:37422659c4925969c66148c5e64979f553386a4226b9484d910d3094ced37d30"}, - {file = "jinxed-1.2.1.tar.gz", hash = "sha256:30c3f861b73279fea1ed928cfd4dfb1f273e16cd62c8a32acfac362da0f78f3f"}, -] - -[package.dependencies] -ansicon = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -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 = "mypy" -version = "1.9.0" -description = "Optional static typing for Python" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, -] - -[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." -category = "dev" -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 = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "parallel-ssh" -version = "2.12.0" -description = "Asynchronous parallel SSH library" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "parallel-ssh-2.12.0.tar.gz", hash = "sha256:be2c06ee8765273d204e00f82afae2dace5eebaefc5343d1dfc64513642161e0"}, - {file = "parallel_ssh-2.12.0-py3-none-any.whl", hash = "sha256:49979a0c6244a8bbd05cfba7d8666e719ad5b539b9e34a45a6df2c6f075f7295"}, -] - -[package.dependencies] -gevent = ">=1.3.0" -ssh-python = "*" -ssh2-python = "*" - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -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 = "pdbpp" -version = "0.10.3" -description = "pdb++, a drop-in replacement for pdb" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pdbpp-0.10.3-py2.py3-none-any.whl", hash = "sha256:79580568e33eb3d6f6b462b1187f53e10cd8e4538f7d31495c9181e2cf9665d1"}, - {file = "pdbpp-0.10.3.tar.gz", hash = "sha256:d9e43f4fda388eeb365f2887f4e7b66ac09dce9b6236b76f63616530e2f669f5"}, -] - -[package.dependencies] -fancycompleter = ">=0.8" -pygments = "*" -wmctrl = "*" - -[package.extras] -funcsigs = ["funcsigs"] -testing = ["funcsigs", "pytest"] - -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - -[[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -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 = "pycparser" -version = "2.22" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[[package]] -name = "pydantic" -version = "1.10.15" -description = "Data validation and settings management using python type hints" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, - {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, - {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, - {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, - {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, - {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, - {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, - {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, - {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, - {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pygments" -version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -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 = "pyreadline" -version = "2.1" -description = "A python implmementation of GNU readline." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, -] - -[[package]] -name = "pyrepl" -version = "0.9.0" -description = "A library for building flexible command line interfaces" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pyrepl-0.9.0.tar.gz", hash = "sha256:292570f34b5502e871bbb966d639474f2b57fbfcd3373c2d6a2f3d56e681a775"}, -] - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<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 = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyxdg" -version = "0.28" -description = "PyXDG contains implementations of freedesktop.org standards in python." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, - {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, -] - -[[package]] -name = "redbird" -version = "0.7.1" -description = "Repository Patterns for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "redbird-0.7.1-py3-none-any.whl", hash = "sha256:6a1fe83fa9dfc0c5b9cb256b54376c299c423f2daba7bcbc081a820ff5b3c6c1"}, - {file = "redbird-0.7.1.tar.gz", hash = "sha256:9ef1098f2a0e68afe40349475018f7694b05bdf92250772cd8d0126f26de58a9"}, -] - -[package.dependencies] -pydantic = "*" -typing-extensions = "*" - -[package.extras] -full = ["pydantic-sqlalchemy", "pymongo", "requests", "sqlalchemy"] -mongodb = ["pymongo"] -rest = ["requests"] -sql = ["pydantic-sqlalchemy", "sqlalchemy"] -test = ["mongomock", "pydantic-sqlalchemy", "pymongo", "pytest", "python-dotenv", "requests", "responses", "sqlalchemy"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -category = "dev" -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 = "rocketry" -version = "2.5.1" -description = "Advanced scheduling framework" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "rocketry-2.5.1-py3-none-any.whl", hash = "sha256:d8755e909026ba401174218bc0a0958044973244cd07e1405e80f85512440253"}, - {file = "rocketry-2.5.1.tar.gz", hash = "sha256:11d1fb3d2856c5b2727bb4814c4f2bfbd2803067eebeac872d34a4c7a6756825"}, -] - -[package.dependencies] -pydantic = "*" -python-dateutil = "*" -redbird = ">=0.5.0" - -[package.extras] -docs = ["pydata-sphinx-theme", "sphinx (>=1.7.5)", "sphinx-book-theme", "sphinx-copybutton", "sphinx-material"] -test = ["pytest", "pytest-asyncio"] - -[[package]] -name = "sentry-sdk" -version = "1.45.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.45.0.tar.gz", hash = "sha256:509aa9678c0512344ca886281766c2e538682f8acfa50fd8d405f8c417ad0625"}, - {file = "sentry_sdk-1.45.0-py2.py3-none-any.whl", hash = "sha256:1ce29e30240cc289a027011103a8c83885b15ef2f316a60bcc7c5300afa144f1"}, -] - -[package.dependencies] -certifi = "*" -fastapi = {version = ">=0.79.0", optional = true, markers = "extra == \"fastapi\""} -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)"] -celery-redbeat = ["celery-redbeat (>=2)"] -chalice = ["chalice (>=1.16.0)"] -clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] -grpcio = ["grpcio (>=1.21.1)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "69.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -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 = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, - {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, -] - -[[package]] -name = "ssh-python" -version = "1.0.0" -description = "libssh C library bindings for Python." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "ssh-python-1.0.0.tar.gz", hash = "sha256:68bee3f5a657e657d6dcc94e1b6c9ec332fd5d59bf4976a54150b997a800bf68"}, - {file = "ssh_python-1.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:ab7e01059b5a40c3de08a094b51a57ee23b41b1b0c52dc8f4a07e1524932778d"}, - {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c51998b2ea7de00ef5ff557893df28d8b07c4392a31bccbb60e1142451b949df"}, - {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6ee326dc3767f28f58691d92ad4543ac8a2ebbac51aa49d6f6f448593cb6bf7"}, - {file = "ssh_python-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73f5ea92e82c2a928ef9d01dee8e684713e1b08a97f9c7d583b34a4720658cb"}, - {file = "ssh_python-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:43da4a16e88dccd54db4e2dac50e09ae424811a7d973a1a1f1c081ebec2a52bb"}, - {file = "ssh_python-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5985e71fee63d34ae15eb660cdf21c3ed4f712b2326c48d10be3c5a2bd1fa51"}, - {file = "ssh_python-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92b95293395924953f537b732a3d8fa1aba7ddb26b2137be80f53111852fa289"}, - {file = "ssh_python-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:82f77cd5d9ddedcc312c37a2848b9d76a0a6162c81f52a8051cc18b644b18b4f"}, - {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6cb934aaa0d8a89ecdc4d37f06d009a7bad50eea18fa3e9763193e85633669be"}, - {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3259ce8cf7b2169abdaab117db466c06c6185db1a65b8e97fa5e475f1ec399fb"}, - {file = "ssh_python-1.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d176c778f02b3066c608c119e94d5b8c3d0d211e83b363bee0bd0d78e0eaa56"}, - {file = "ssh_python-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:58f2fe1899add41d3954ae655caf09119491a468cc93b4575b199f29ecb4c49f"}, - {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd56baaef157052755cfdbef718911a062e008cc59b267deb89952b269313995"}, - {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44c1509fa26ad699de921e53347a86cef18796b5c8582d9a2cdd3e21cbd667a"}, - {file = "ssh_python-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e438710ffbf34f800e2c051091da478191c02a429abd49f565f82a0b149d01f"}, - {file = "ssh_python-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:364d3b075001d9a1e7d83a545e43cb28420171ddd779a286d4e267e52b9df2b7"}, - {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d8e386c137414d7ef3bbac939be37f5e2bd11d1f83c78274c1e68efe184bc5d2"}, - {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ebd8ebfad53f47c938634645c8b4b5870e444e190de90a0d5ceb5422a48fe1"}, - {file = "ssh_python-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d0fca290cb88d73c12d7fad162ad36d79beb32b4fb3e39de87cd37330404f5"}, - {file = "ssh_python-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3af87fd53e314cef8cc65f00cd69a2b1aca84863028b161721800bb7e89526f2"}, - {file = "ssh_python-1.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a50af7f32517cd4067b3e68b723ec5c4efbbde2fb92263cbf11d7da5eacd65b4"}, - {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c83f5f7f3ded106d1088f33bc8aba03ae60126baa767d91a9bb9dc0372d73bda"}, - {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2566ffb2725a05e0b650ccf63b5d4f46fd5e47328d22d65f8111183e7dc4a62d"}, - {file = "ssh_python-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bfa28c5b159ab804bc5b496819e5af8ebb7e5f6b442e8a87136296cebb172c"}, - {file = "ssh_python-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:c755afbe8ce3e926c80d8301f7bf6941781a2a1d497d3cb2692ea73bdf064f97"}, - {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6190ae4afedf0c35f84a1a5cd52c2b68935cd5c4b1330b491ca381006fa8fae6"}, - {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422140695d14796e9eb6f608365c37d5b780092ca6dbfedde9e6ffd15722f2b0"}, - {file = "ssh_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e36540aba6f3a7bc18cdb8321808f9b7ea3d551fad53069b0806189c9eab989"}, - {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b695ff0561f16746b0d47f4a51cca205088a06a06c6d0ab29127b6f21bdf286"}, - {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56298bc06afa7986f76b1a5add539f462bc040c9c8bd9df1308a5b7b62af090"}, - {file = "ssh_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1abed03383d3c40e3a3687dfade03b881b055f29baeb3d3a4d60eb80580b01a8"}, - {file = "ssh_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26380e2f06ea8b4f79c6ada4b2016079c0f6d47752785dd129c914f9aeda23f4"}, - {file = "ssh_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54731a4c3c06968ff41fd72a62ac40c1aa163d4fb51c82a4367e74398c9968ae"}, -] - -[[package]] -name = "ssh2-python" -version = "1.0.0" -description = "Bindings for libssh2 C library" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "ssh2-python-1.0.0.tar.gz", hash = "sha256:af89e80c3203e7829b24eea688eaf5c5e279071aed1882238b4f44ec2144e7c5"}, - {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:743df09165bbfcf754c6faf8a5feba414605ed73a6eee79e323d525c60f0cb41"}, - {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c3c2910976da579c337dbd2134601d462b7bd861772dcb6e6dbb298749d1fd9"}, - {file = "ssh2_python-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:631375d2338aa373b9d2f2fe2af7ffc7c552845661a2c7456757b4c39c9ae15b"}, - {file = "ssh2_python-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5fca1744fe977eec920d7fdfeb1a79dd3fe4747f9455fb0e76aaa02b2e2dee7a"}, - {file = "ssh2_python-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:016c2fb027bb8a1720b71cf144c423f8d1acfdfef4238ada47c8452c7c5273fd"}, - {file = "ssh2_python-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbbdaaedc094364ee5027942bc40d68d1c5f9da7ec4cccb615036309b980bd8b"}, - {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9abd88161ce5e40f08a3b0af2173acafc7b19dd3dd140ccbd7c0b40094e558f"}, - {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ca21a64c1aa77763691de70ed29143b457fafc323a108bfeb4d249e1cca84f"}, - {file = "ssh2_python-1.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1ba1b05256277c78f4d4e158c4b024427bda94761426b9b37e77a551cc85158"}, - {file = "ssh2_python-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3270c77d24a35b1b52c312c42f14f0c27b013ab00cb8fdb67d4eb55e999b715a"}, - {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8582b2965116435c35f9f2f3da4297eae8a5daea0fcebfdfd9388a9dfc5cd922"}, - {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114b27c130144f6fad0fbc5d35e71d0a841059efd3f868b86b5443de89b208af"}, - {file = "ssh2_python-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddcba606818f28719d25c4a14789cc9b88714385e26e52d45ce967068ed7ad83"}, - {file = "ssh2_python-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:aca4967a67a8fe4a4cabdac710065cca426e8200e5d426f6daae4c6a3a7e1921"}, - {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:190b1c1389206aed43b40a1351732c1d854578480a566d5f6b899587ff989c74"}, - {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f288aef1ab4d1e8bbc8a38bcebc1b21849fa8448fb68e851b9ff136eab46da6"}, - {file = "ssh2_python-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:161eebd09258fe7fe47652561cefca9116c0df3b1128e1514d10d3d97e4f1521"}, - {file = "ssh2_python-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b315b754392e3b3c3ac546f4d35acdedb174d104212242a6f4778695beda9bb7"}, - {file = "ssh2_python-1.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:c5635f196a96bbe6d12b6cdb2f9d5fd464a0e82edca58f56391cc25aa61a4379"}, - {file = "ssh2_python-1.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:fc8f46d0d883d77462122a4cdb1dcff9a890923bd3525f061a2eb3563c7c5612"}, - {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0f80a96dbffb34f09d84532178d7d24e404b0b313c76037a8b87187990ddddbd"}, - {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc837e44189c9092af9af5ff25138a8efd05ad6fb10623c49039aa8f2f6ce03e"}, - {file = "ssh2_python-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:092c7f63c7ab8291748c7af8eb7809c81c53a761e9f4a8eecfc8705c92c636cb"}, - {file = "ssh2_python-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:6d52034fdb35872fb7082d0a325cb90e0dca41050bc1d7f65cf90b951d5f7caa"}, - {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:489ae221149a1049bdda48f12bb99e20d51769ac67f7f0c65468f298f81c10c0"}, - {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2593b1659f8551a06730579ad70390939f50094b71dc8acd9b86cba0d95ab5c0"}, - {file = "ssh2_python-1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b304bcda607935375eeb5caa8f798021174893de7ea1b72416ad28709793695"}, - {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b139b8b47e230750b5b3b2f9977a7af996e614e334565bce5bf70e018d6536c7"}, - {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93d9d7c48f7b729e9e0250b09dff5e712aab7df10718764f6ec00f88521b8a12"}, - {file = "ssh2_python-1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c1e9cdcf416e72934bf2af8faf977ea3836c98e248d4966606410b2a71f5de"}, - {file = "ssh2_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3771b58ae9944aff2aa86aadadc4f2dea174e3a30c8503bed022587bedd7e1c5"}, - {file = "ssh2_python-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99ee7e9aaaba60fc91ecb21d6233272bfa8acb7aec28b599eee9f374327e5f9c"}, -] - -[[package]] -name = "starlette" -version = "0.25.0" -description = "The little ASGI library that shines." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "starlette-0.25.0-py3-none-any.whl", hash = "sha256:774f1df1983fd594b9b6fb3ded39c2aa1979d10ac45caac0f4255cbe2acb8628"}, - {file = "starlette-0.25.0.tar.gz", hash = "sha256:854c71e73736c429c2bdb07801f2c76c9cba497e7c3cf4988fde5e95fe4cdb3c"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" -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" -category = "dev" -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 = "typing-extensions" -version = "4.11.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, -] - -[[package]] -name = "urllib3" -version = "2.2.1" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -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 = "uvicorn" -version = "0.21.1" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uvicorn-0.21.1-py3-none-any.whl", hash = "sha256:e47cac98a6da10cd41e6fd036d472c6f58ede6c5dbee3dbee3ef7a100ed97742"}, - {file = "uvicorn-0.21.1.tar.gz", hash = "sha256:0fac9cb342ba099e0d582966005f3fdba5b0290579fed4a6266dc702ca7bb032"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[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 = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" -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 = "wmctrl" -version = "0.5" -description = "A tool to programmatically control windows inside X" -category = "dev" -optional = false -python-versions = ">=2.7" -files = [ - {file = "wmctrl-0.5-py2.py3-none-any.whl", hash = "sha256:ae695c1863a314c899e7cf113f07c0da02a394b968c4772e1936219d9234ddd7"}, - {file = "wmctrl-0.5.tar.gz", hash = "sha256:7839a36b6fe9e2d6fd22304e5dc372dbced2116ba41283ea938b2da57f53e962"}, -] - -[package.dependencies] -attrs = "*" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "zope-event" -version = "5.0" -description = "Very basic event publishing system" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, - {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx"] -test = ["zope.testrunner"] - -[[package]] -name = "zope-interface" -version = "6.2" -description = "Interfaces for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.interface-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab"}, - {file = "zope.interface-6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328"}, - {file = "zope.interface-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd"}, - {file = "zope.interface-6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037"}, - {file = "zope.interface-6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac"}, - {file = "zope.interface-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe"}, - {file = "zope.interface-6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48"}, - {file = "zope.interface-6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b"}, - {file = "zope.interface-6.2-cp312-cp312-win_amd64.whl", hash = "sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe"}, - {file = "zope.interface-6.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b"}, - {file = "zope.interface-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550"}, - {file = "zope.interface-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532"}, - {file = "zope.interface-6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3"}, - {file = "zope.interface-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0"}, - {file = "zope.interface-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099"}, - {file = "zope.interface-6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a"}, - {file = "zope.interface-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1"}, - {file = "zope.interface-6.2.tar.gz", hash = "sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx_rtd_theme"] -test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] -testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "340600ae71a39c6b4985c68de6584864896640188a4a6752b041fdb15db6d780" From f7ca4d28a1aa05f3de0b1c0b9db9dd0b20ea94b6 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 23 Oct 2024 13:28:57 +0200 Subject: [PATCH 02/20] Specify you need to use python 3.11 --- agent/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/pyproject.toml b/agent/pyproject.toml index a4b01b8..9c405b0 100644 --- a/agent/pyproject.toml +++ b/agent/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Donatas Rasiukevicius "] [tool.poetry.dependencies] -python = "^3.10" +python = "~3.11" toml = "^0.10.2" parallel-ssh = "^2.12.0" rocketry = "^2.5.1" From 08135504a9462f1c9a7aa70934c95575afbf90b0 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 23 Oct 2024 13:34:35 +0200 Subject: [PATCH 03/20] Ignore jetbrains IDE stuff --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b550cdf..97b612e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ data/ docker-compose.yaml .local/ .envrc +.idea # Supabase .branches From 49326241e40421b6e60acaacde5908082da37737 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 12:18:30 +0100 Subject: [PATCH 04/20] Implement new server app --- humitifier-server/package.json | 15 + humitifier-server/poetry.lock | 840 ++++++++ humitifier-server/pyproject.toml | 30 + humitifier-server/src/api/__init__.py | 0 humitifier-server/src/api/admin.py | 3 + humitifier-server/src/api/apps.py | 6 + .../src/api/migrations/__init__.py | 0 humitifier-server/src/api/models.py | 3 + humitifier-server/src/api/tests.py | 3 + humitifier-server/src/api/urls.py | 9 + humitifier-server/src/api/views.py | 37 + humitifier-server/src/hosts/__init__.py | 0 humitifier-server/src/hosts/admin.py | 3 + humitifier-server/src/hosts/alerts.py | 79 + humitifier-server/src/hosts/apps.py | 6 + humitifier-server/src/hosts/filters.py | 162 ++ .../src/hosts/management/__init__.py | 0 .../src/hosts/management/commands/__init__.py | 0 .../management/commands/recreate_alerts.py | 13 + humitifier-server/src/hosts/menus.py | 43 + .../src/hosts/migrations/0001_initial.py | 58 + ...st_scan_cache_alter_host_last_scan_date.py | 23 + .../hosts/migrations/0003_alter_scan_host.py | 23 + .../0004_host_contact_host_department.py | 50 + .../src/hosts/migrations/0005_alert.py | 53 + .../0006_alter_host_options_host_os.py | 34 + .../src/hosts/migrations/__init__.py | 0 humitifier-server/src/hosts/models.py | 199 ++ .../src/hosts/templates/hosts/detail.html | 79 + .../hosts/detail_parts/alerts/critical.html | 4 + .../hosts/detail_parts/alerts/info.html | 4 + .../hosts/detail_parts/alerts/warning.html | 4 + .../hosts/detail_parts/facts/blocks.html | 15 + .../hosts/detail_parts/facts/databases.html | 11 + .../hosts/detail_parts/facts/groups.html | 19 + .../hosts/detail_parts/facts/host_meta.html | 151 ++ .../hosts/detail_parts/facts/memory.html | 17 + .../hosts/detail_parts/facts/packages.html | 19 + .../hosts/detail_parts/facts/users.html | 25 + .../hosts/detail_parts/facts/vhosts.html | 13 + .../src/hosts/templates/hosts/list.html | 72 + .../templates/hosts/list_parts/host.html | 41 + .../src/hosts/templatetags/__init__.py | 0 .../src/hosts/templatetags/host_tags.py | 25 + humitifier-server/src/hosts/tests.py | 3 + humitifier-server/src/hosts/urls.py | 17 + humitifier-server/src/hosts/utils.py | 53 + humitifier-server/src/hosts/views.py | 119 ++ .../src/humitifier_server/__init__.py | 0 .../src/humitifier_server/asgi.py | 16 + .../src/humitifier_server/settings.py | 154 ++ .../src/humitifier_server/urls.py | 34 + .../src/humitifier_server/wsgi.py | 16 + humitifier-server/src/main/__init__.py | 0 humitifier-server/src/main/admin.py | 7 + humitifier-server/src/main/apps.py | 6 + .../src/main/context_processors.py | 46 + humitifier-server/src/main/forms.py | 6 + .../src/main/management/__init__.py | 0 .../src/main/management/commands/__init__.py | 0 .../main/management/commands/fake_hosts.py | 27 + humitifier-server/src/main/menus.py | 55 + .../src/main/migrations/0001_initial.py | 132 ++ .../src/main/migrations/__init__.py | 0 humitifier-server/src/main/models.py | 7 + .../src/main/static/main/css/tailwind.css | 1874 +++++++++++++++++ .../main/static/main/css/tailwind.input.css | 58 + .../main/img/cm_hs_avatar_corporate.png | Bin 0 -> 99366 bytes .../src/main/static/main}/img/favicon.ico | Bin .../src/main/static/main}/img/uu-black.png | Bin .../main/static/main}/img/uu-rgb-black.png | Bin .../main/static/main}/img/uu-rgb-white.png | Bin .../src/main/static/main}/img/uu-white.png | Bin .../src/main/static/main/js/alpine.min.js | 6 + .../templates/base/base_page_template.html | 42 + .../templates/base/forms/field_template.html | 8 + .../templates/base/forms/form_template.html | 14 + .../page_parts/filters_form_template.html | 16 + .../templates/base/page_parts/header.html | 36 + .../base/page_parts/paginator_bottom.html | 32 + .../base/page_parts/paginator_top.html | 63 + .../templates/base/page_parts/sidebar.html | 80 + .../templates/django/forms/widgets/input.html | 9 + .../django/forms/widgets/select.html | 21 + .../src/main/templates/icons/admin.html | 3 + .../src/main/templates/icons/api.html | 3 + .../src/main/templates/icons/critical.html | 14 + .../src/main/templates/icons/dashboard.html | 3 + .../src/main/templates/icons/databases.html | 3 + .../src/main/templates/icons/host.html | 14 + .../src/main/templates/icons/info.html | 14 + .../src/main/templates/icons/shield.html | 3 + .../src/main/templates/icons/tasks.html | 3 + .../src/main/templates/icons/terminal.html | 3 + .../src/main/templates/icons/users.html | 3 + .../src/main/templates/icons/warning.html | 14 + .../src/main/templates/main/dashboard.html | 181 ++ .../main/templates/main/not_implemented.html | 10 + .../src/main/templatetags/__init__.py | 0 .../src/main/templatetags/param_replace.py | 32 + .../src/main/templatetags/strip_quotes.py | 15 + humitifier-server/src/main/tests.py | 3 + humitifier-server/src/main/urls.py | 15 + humitifier-server/src/main/views.py | 136 ++ humitifier-server/src/manage.py | 22 + humitifier-server/tailwind.config.js | 41 + humitifier-server/yarn.lock | 736 +++++++ static/prism.css | 3 - static/prism.js | 5 - web/core/_inputs.scss | 25 - web/core/_page_header.scss | 15 - web/core/_table.scss | 49 - web/core/_typography.scss | 67 - web/core/main.scss | 104 - web/style.scss | 2 - web/utils/_animations.scss | 8 - web/utils/_box.scss | 8 - web/utils/_button.scss | 19 - web/utils/_colors.scss | 14 - web/utils/_fonts.scss | 5 - web/utils/_lists.scss | 35 - web/utils/_progress.scss | 32 - web/utils/_table.scss | 23 - web/utils/main.scss | 4 - 124 files changed, 6419 insertions(+), 418 deletions(-) create mode 100644 humitifier-server/package.json create mode 100644 humitifier-server/poetry.lock create mode 100644 humitifier-server/pyproject.toml create mode 100644 humitifier-server/src/api/__init__.py create mode 100644 humitifier-server/src/api/admin.py create mode 100644 humitifier-server/src/api/apps.py create mode 100644 humitifier-server/src/api/migrations/__init__.py create mode 100644 humitifier-server/src/api/models.py create mode 100644 humitifier-server/src/api/tests.py create mode 100644 humitifier-server/src/api/urls.py create mode 100644 humitifier-server/src/api/views.py create mode 100644 humitifier-server/src/hosts/__init__.py create mode 100644 humitifier-server/src/hosts/admin.py create mode 100644 humitifier-server/src/hosts/alerts.py create mode 100644 humitifier-server/src/hosts/apps.py create mode 100644 humitifier-server/src/hosts/filters.py create mode 100644 humitifier-server/src/hosts/management/__init__.py create mode 100644 humitifier-server/src/hosts/management/commands/__init__.py create mode 100644 humitifier-server/src/hosts/management/commands/recreate_alerts.py create mode 100644 humitifier-server/src/hosts/menus.py create mode 100644 humitifier-server/src/hosts/migrations/0001_initial.py create mode 100644 humitifier-server/src/hosts/migrations/0002_alter_host_last_scan_cache_alter_host_last_scan_date.py create mode 100644 humitifier-server/src/hosts/migrations/0003_alter_scan_host.py create mode 100644 humitifier-server/src/hosts/migrations/0004_host_contact_host_department.py create mode 100644 humitifier-server/src/hosts/migrations/0005_alert.py create mode 100644 humitifier-server/src/hosts/migrations/0006_alter_host_options_host_os.py create mode 100644 humitifier-server/src/hosts/migrations/__init__.py create mode 100644 humitifier-server/src/hosts/models.py create mode 100644 humitifier-server/src/hosts/templates/hosts/detail.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/critical.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/info.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/warning.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/blocks.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/databases.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/groups.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/host_meta.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/memory.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/packages.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/users.html create mode 100644 humitifier-server/src/hosts/templates/hosts/detail_parts/facts/vhosts.html create mode 100644 humitifier-server/src/hosts/templates/hosts/list.html create mode 100644 humitifier-server/src/hosts/templates/hosts/list_parts/host.html create mode 100644 humitifier-server/src/hosts/templatetags/__init__.py create mode 100644 humitifier-server/src/hosts/templatetags/host_tags.py create mode 100644 humitifier-server/src/hosts/tests.py create mode 100644 humitifier-server/src/hosts/urls.py create mode 100644 humitifier-server/src/hosts/utils.py create mode 100644 humitifier-server/src/hosts/views.py create mode 100644 humitifier-server/src/humitifier_server/__init__.py create mode 100644 humitifier-server/src/humitifier_server/asgi.py create mode 100644 humitifier-server/src/humitifier_server/settings.py create mode 100644 humitifier-server/src/humitifier_server/urls.py create mode 100644 humitifier-server/src/humitifier_server/wsgi.py create mode 100644 humitifier-server/src/main/__init__.py create mode 100644 humitifier-server/src/main/admin.py create mode 100644 humitifier-server/src/main/apps.py create mode 100644 humitifier-server/src/main/context_processors.py create mode 100644 humitifier-server/src/main/forms.py create mode 100644 humitifier-server/src/main/management/__init__.py create mode 100644 humitifier-server/src/main/management/commands/__init__.py create mode 100644 humitifier-server/src/main/management/commands/fake_hosts.py create mode 100644 humitifier-server/src/main/menus.py create mode 100644 humitifier-server/src/main/migrations/0001_initial.py create mode 100644 humitifier-server/src/main/migrations/__init__.py create mode 100644 humitifier-server/src/main/models.py create mode 100644 humitifier-server/src/main/static/main/css/tailwind.css create mode 100644 humitifier-server/src/main/static/main/css/tailwind.input.css create mode 100644 humitifier-server/src/main/static/main/img/cm_hs_avatar_corporate.png rename {static => humitifier-server/src/main/static/main}/img/favicon.ico (100%) rename {static => humitifier-server/src/main/static/main}/img/uu-black.png (100%) rename {static => humitifier-server/src/main/static/main}/img/uu-rgb-black.png (100%) rename {static => humitifier-server/src/main/static/main}/img/uu-rgb-white.png (100%) rename {static => humitifier-server/src/main/static/main}/img/uu-white.png (100%) create mode 100644 humitifier-server/src/main/static/main/js/alpine.min.js create mode 100644 humitifier-server/src/main/templates/base/base_page_template.html create mode 100644 humitifier-server/src/main/templates/base/forms/field_template.html create mode 100644 humitifier-server/src/main/templates/base/forms/form_template.html create mode 100644 humitifier-server/src/main/templates/base/page_parts/filters_form_template.html create mode 100644 humitifier-server/src/main/templates/base/page_parts/header.html create mode 100644 humitifier-server/src/main/templates/base/page_parts/paginator_bottom.html create mode 100644 humitifier-server/src/main/templates/base/page_parts/paginator_top.html create mode 100644 humitifier-server/src/main/templates/base/page_parts/sidebar.html create mode 100644 humitifier-server/src/main/templates/django/forms/widgets/input.html create mode 100644 humitifier-server/src/main/templates/django/forms/widgets/select.html create mode 100644 humitifier-server/src/main/templates/icons/admin.html create mode 100644 humitifier-server/src/main/templates/icons/api.html create mode 100644 humitifier-server/src/main/templates/icons/critical.html create mode 100644 humitifier-server/src/main/templates/icons/dashboard.html create mode 100644 humitifier-server/src/main/templates/icons/databases.html create mode 100644 humitifier-server/src/main/templates/icons/host.html create mode 100644 humitifier-server/src/main/templates/icons/info.html create mode 100644 humitifier-server/src/main/templates/icons/shield.html create mode 100644 humitifier-server/src/main/templates/icons/tasks.html create mode 100644 humitifier-server/src/main/templates/icons/terminal.html create mode 100644 humitifier-server/src/main/templates/icons/users.html create mode 100644 humitifier-server/src/main/templates/icons/warning.html create mode 100644 humitifier-server/src/main/templates/main/dashboard.html create mode 100644 humitifier-server/src/main/templates/main/not_implemented.html create mode 100644 humitifier-server/src/main/templatetags/__init__.py create mode 100644 humitifier-server/src/main/templatetags/param_replace.py create mode 100644 humitifier-server/src/main/templatetags/strip_quotes.py create mode 100644 humitifier-server/src/main/tests.py create mode 100644 humitifier-server/src/main/urls.py create mode 100644 humitifier-server/src/main/views.py create mode 100755 humitifier-server/src/manage.py create mode 100644 humitifier-server/tailwind.config.js create mode 100644 humitifier-server/yarn.lock delete mode 100644 static/prism.css delete mode 100644 static/prism.js delete mode 100644 web/core/_inputs.scss delete mode 100644 web/core/_page_header.scss delete mode 100644 web/core/_table.scss delete mode 100644 web/core/_typography.scss delete mode 100644 web/core/main.scss delete mode 100644 web/style.scss delete mode 100644 web/utils/_animations.scss delete mode 100644 web/utils/_box.scss delete mode 100644 web/utils/_button.scss delete mode 100644 web/utils/_colors.scss delete mode 100644 web/utils/_fonts.scss delete mode 100644 web/utils/_lists.scss delete mode 100644 web/utils/_progress.scss delete mode 100644 web/utils/_table.scss delete mode 100644 web/utils/main.scss diff --git a/humitifier-server/package.json b/humitifier-server/package.json new file mode 100644 index 0000000..dbcd12f --- /dev/null +++ b/humitifier-server/package.json @@ -0,0 +1,15 @@ +{ + "name": "humitifier-server", + "version": "1.0.0", + "repository": "https://github.com/CentreForDigitalHumanities/humitifier", + "author": "Ty mees", + "license": "GPL-3.0-or-later", + "devDependencies": { + "tailwindcss": "^3.4.14" + }, + "scripts": { + "tw:build": "tailwindcss build -i src/main/static/main/css/tailwind.input.css -o src/main/static/main/css/tailwind.css", + "tw:watch": "yarn tw:build --watch", + "tw:prod": "yarn tw:build --minify" + } +} diff --git a/humitifier-server/poetry.lock b/humitifier-server/poetry.lock new file mode 100644 index 0000000..2c465b4 --- /dev/null +++ b/humitifier-server/poetry.lock @@ -0,0 +1,840 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "ansicon" +version = "1.89.0" +description = "Python wrapper for loading Jason Hood's ANSICON" +optional = false +python-versions = "*" +files = [ + {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, + {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, +] + +[[package]] +name = "asgiref" +version = "3.8.1" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, +] + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blessed" +version = "1.20.0" +description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." +optional = false +python-versions = ">=2.7" +files = [ + {file = "blessed-1.20.0-py2.py3-none-any.whl", hash = "sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058"}, + {file = "blessed-1.20.0.tar.gz", hash = "sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680"}, +] + +[package.dependencies] +jinxed = {version = ">=1.1.0", markers = "platform_system == \"Windows\""} +six = ">=1.9.0" +wcwidth = ">=0.1.4" + +[[package]] +name = "bpython" +version = "0.24" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bpython-0.24-py3-none-any.whl", hash = "sha256:0d196ae3d1ce3dcd559a3fb89ed2c468dfbd1504af0d680b906dd65a9c7a32eb"}, + {file = "bpython-0.24.tar.gz", hash = "sha256:98736ffd7a8c48fd2bfb53d898a475f4241bde0b672125706af04d9d08fd3dbd"}, +] + +[package.dependencies] +curtsies = ">=0.4.0" +cwcwidth = "*" +greenlet = "*" +pygments = "*" +pyxdg = "*" +requests = "*" + +[package.extras] +clipboard = ["pyperclip"] +jedi = ["jedi (>=0.16)"] +urwid = ["urwid"] +watch = ["watchdog"] + +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +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.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[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 = "curtsies" +version = "0.4.2" +description = "Curses-like terminal wrapper, with colored strings!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "curtsies-0.4.2-py3-none-any.whl", hash = "sha256:f24d676a8c4711fb9edba1ab7e6134bc52305a222980b3b717bb303f5e94cec6"}, + {file = "curtsies-0.4.2.tar.gz", hash = "sha256:6ebe33215bd7c92851a506049c720cca4cf5c192c1665c1d7a98a04c4702760e"}, +] + +[package.dependencies] +blessed = ">=1.5" +cwcwidth = "*" + +[[package]] +name = "cwcwidth" +version = "0.1.9" +description = "Python bindings for wc(s)width" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cwcwidth-0.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704f0d6888aa5e81e76d9f76709385f9f55aca8c450ee82cc722054814a7791f"}, + {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0633158205b50f253ad04e301156807e309a9fb9479a520418e010da6df13604"}, + {file = "cwcwidth-0.1.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a5407d0933c3aab8ee92cffd9e4f09010f25af10ebdfa19776748402bba9261"}, + {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:72490e07dfbc599fdf6efe26a13cfbf725f0513b181c3386d65bfd84f6175924"}, + {file = "cwcwidth-0.1.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf71151ae06e95f266bef91392c1562539b2eed847fd1f00f7b5b4ca3fd41a67"}, + {file = "cwcwidth-0.1.9-cp310-cp310-win32.whl", hash = "sha256:3e3c186b5c171d85f2b7f093e7efb33fd9b6e55b791ff75a0f101b18ec0433cd"}, + {file = "cwcwidth-0.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:ae17e493ffc18497c2602f8f42a0d8e490ea42ab3ccfbe5e4a6069a6d24f3b36"}, + {file = "cwcwidth-0.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1c3eb0a8c1b25c4a17b6b9bbf7d25ce9df3ea43b6f87903c51bc12434a2cc29"}, + {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c8752815ce4e40e7b34b7fe039276a5fbfb1b077131614381b13ef3b7bb21ff"}, + {file = "cwcwidth-0.1.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:368ace13080dbaacdc247370d8a965a749b124aa50d0b1b6eb87601826db870f"}, + {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ca9a653661e152a426bdb51a272f36bc79f9830e6a7169abe8110ec367c3518c"}, + {file = "cwcwidth-0.1.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f96386cc29e6eef8ef066d7dd3c767c5119d66506dabea20dd344dabb3f2d225"}, + {file = "cwcwidth-0.1.9-cp311-cp311-win32.whl", hash = "sha256:f6ba88970ec12fdbed5554beb1b9a25d8271fc3d0d9e60639db700a79bed1863"}, + {file = "cwcwidth-0.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:aa6725e7b3571fdf6ce7c02d1dd2d69e00d166bb6df44e46ab215067028b3a03"}, + {file = "cwcwidth-0.1.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:42de102d5191fc68ef3ff6530f60c4895148ddc21aa0acaaf4612e5f7f0c38c4"}, + {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877e48c615b3fec88b7e640f9cf9d96704497657fb5aad2b7c0b0c59ecabff69"}, + {file = "cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdbaf0a8dad20eb685df11a195a2449fe230b08a5b356d036c8d7e59d4128a88"}, + {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f6e0e023c4b127c47fd4c44cf537be209b9a28d8725f4f576f4d63744a23aa38"}, + {file = "cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b4f7d24236ce3c9d3b5e07fd75d232452f19bdddb6ae8bbfdcb97b6cb02835e8"}, + {file = "cwcwidth-0.1.9-cp312-cp312-win32.whl", hash = "sha256:ba9da6c911bf108334426890bc9f57b839a38e1afc4383a41bd70adbce470db3"}, + {file = "cwcwidth-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:40466f16e85c338e8fc3eee87a8c9ca23416cc68b3049f68cb4cead5fb8b71b3"}, + {file = "cwcwidth-0.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:167f59c3c1e1d8e231a1abd666af4e73dd8a94917efb6522e9b610ac4587903a"}, + {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afc745f18c9e3c38851a931c0c0a7e479d6494911ba1353f998d707f95a895b4"}, + {file = "cwcwidth-0.1.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8d55c47cbec4796e89cfedc89c52e6c4c2faeb77489a763415b9f76d8fc14db"}, + {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c858842849ce2cfdf207095253da83831d9407771c8073f6b75f24d3faf1a1eb"}, + {file = "cwcwidth-0.1.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc049ce273f32b632f5ead649b2120f8b2b78035d7b069fdc460c4be9affddb5"}, + {file = "cwcwidth-0.1.9-cp38-cp38-win32.whl", hash = "sha256:1bafe978a5b7915848244a952829e3f8757c0cebef581c8250da6064c906c38c"}, + {file = "cwcwidth-0.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:024d1b21e6123bf30a849e60eea3482f556bbd00d39215f86c904e5bd81fc1b6"}, + {file = "cwcwidth-0.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d367da5e6fb538388817bf5b2d6dd4db90e5e631d99c34055589d007b5c94bc"}, + {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad721d9dbc14eafd06176e4f5594942336b1e813de2a5ab7bd64254393c5713f"}, + {file = "cwcwidth-0.1.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711ace9796cb6767ff29095ff5b0ec4619e7297854eb4b91ba99154590eddcc9"}, + {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:945615a7b8cdcbcd8e06d399f96a2b09440c3a4c5cb3c2d0109f00d80da27a12"}, + {file = "cwcwidth-0.1.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ffaf706abe400282f299463594d8887566e2a280cd0255110bd4397cc7be2910"}, + {file = "cwcwidth-0.1.9-cp39-cp39-win32.whl", hash = "sha256:03093cac6f8e4326b1c30169e024fe2894f76c6ffddf6464e489bb33cb3a2897"}, + {file = "cwcwidth-0.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:0ddef2c504e6f4fd6122b46d55061f487add1ebb86596ae70ffc2a8b8955c8bc"}, + {file = "cwcwidth-0.1.9.tar.gz", hash = "sha256:f19d11a0148d4a8cacd064c96e93bca8ce3415a186ae8204038f45e108db76b8"}, +] + +[[package]] +name = "django" +version = "5.1.2" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = ">=3.10" +files = [ + {file = "Django-5.1.2-py3-none-any.whl", hash = "sha256:f11aa87ad8d5617171e3f77e1d5d16f004b79a2cf5d2e1d2b97a6a1f8e9ba5ed"}, + {file = "Django-5.1.2.tar.gz", hash = "sha256:bd7376f90c99f96b643722eee676498706c9fd7dc759f55ebfaf2c08ebcdf4f0"}, +] + +[package.dependencies] +asgiref = ">=3.8.1,<4" +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-braces" +version = "1.16.0" +description = "Reusable, generic mixins for Django" +optional = false +python-versions = "*" +files = [ + {file = "django_braces-1.16.0-py3-none-any.whl", hash = "sha256:1c7a83877a2ea0478f051049594dcec215f873f1aecdc4b3ee73121932bf01b4"}, + {file = "django_braces-1.16.0.tar.gz", hash = "sha256:1de7c2ba92147fc265ec034880aa5f2a714a5d1f1c1280278ca1fdd6f2a08e03"}, +] + +[package.dependencies] +Django = ">=2.2" + +[[package]] +name = "django-debug-toolbar" +version = "4.4.6" +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.4.6-py3-none-any.whl", hash = "sha256:3beb671c9ec44ffb817fad2780667f172bd1c067dbcabad6268ce39a81335f45"}, + {file = "django_debug_toolbar-4.4.6.tar.gz", hash = "sha256:36e421cb908c2f0675e07f9f41e3d1d8618dc386392ec82d23bcfcd5d29c7044"}, +] + +[package.dependencies] +django = ">=4.2.9" +sqlparse = ">=0.2" + +[[package]] +name = "django-filter" +version = "24.3" +description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_filter-24.3-py3-none-any.whl", hash = "sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64"}, + {file = "django_filter-24.3.tar.gz", hash = "sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3"}, +] + +[package.dependencies] +Django = ">=4.2" + +[[package]] +name = "django-simple-menu" +version = "2.1.3" +description = "Simple, yet powerful, code-based menus for Django applications" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-simple-menu-2.1.3.tar.gz", hash = "sha256:4246c12fa267598e4652793dfec5d123f97f82430965bd34a8c42d38d5be1801"}, + {file = "django_simple_menu-2.1.3-py3-none-any.whl", hash = "sha256:371ed124cc35c72496c2b99672fac759d268950454984c7f15f2cbdedb4f2840"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "djangorestframework" +version = "3.15.2" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"}, + {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "faker" +version = "30.8.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-30.8.0-py3-none-any.whl", hash = "sha256:4cd0c5ea4bc1e4c902967f6e662f5f5da69f1674d9a94f54e516d27f3c2a6a16"}, + {file = "faker-30.8.0.tar.gz", hash = "sha256:3608c7fcac2acde0eaa6da28dae97628f18f14d54eaa2a92b96ae006f1621bd7"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" +typing-extensions = "*" + +[[package]] +name = "greenlet" +version = "3.1.1" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "jinxed" +version = "1.3.0" +description = "Jinxed Terminal Library" +optional = false +python-versions = "*" +files = [ + {file = "jinxed-1.3.0-py2.py3-none-any.whl", hash = "sha256:b993189f39dc2d7504d802152671535b06d380b26d78070559551cbf92df4fc5"}, + {file = "jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf"}, +] + +[package.dependencies] +ansicon = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "markdown" +version = "3.7" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[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 = "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 = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[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 = "platformdirs" +version = "4.3.6" +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.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "psycopg2" +version = "2.9.10" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = true +python-versions = ">=3.8" +files = [ + {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, + {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, + {file = "psycopg2-2.9.10-cp311-cp311-win32.whl", hash = "sha256:47c4f9875125344f4c2b870e41b6aad585901318068acd01de93f3677a6522c2"}, + {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, + {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, + {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, + {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, + {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, + {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = true +python-versions = ">=3.8" +files = [ + {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-win32.whl", hash = "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"}, +] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyxdg" +version = "0.28" +description = "PyXDG contains implementations of freedesktop.org standards in python." +optional = false +python-versions = "*" +files = [ + {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, + {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[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 = "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 = "sqlparse" +version = "0.5.1" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, + {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, +] + +[package.extras] +dev = ["build", "hatch"] +doc = ["sphinx"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[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 = "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"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "add815003ea81e8541d45420ec291a8e9a4d946acb4013db84acb90c897971e3" diff --git a/humitifier-server/pyproject.toml b/humitifier-server/pyproject.toml new file mode 100644 index 0000000..922188e --- /dev/null +++ b/humitifier-server/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "humitifier-server" +version = "0.1.0" +description = "" +authors = ["Mees, T.D. (Ty) "] + +[tool.poetry.dependencies] +python = "^3.11" +django = "~5.1" +bpython = "^0.24" +django-braces = "^1.16.0" +djangorestframework = "^3.15.2" +django-filter = "^24.3" +markdown = "^3.7" +django-debug-toolbar = "^4.4.6" +django-simple-menu = "^2.1.3" + + +[tool.poetry.group.dev.dependencies] +black = "^24.10.0" +psycopg2-binary = {version = "^2.9.10", optional = true} +faker = "^30.8.0" + + +[tool.poetry.group.production.dependencies] +psycopg2 = {version = "^2.9.10", optional = true} + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/humitifier-server/src/api/__init__.py b/humitifier-server/src/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/api/admin.py b/humitifier-server/src/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/humitifier-server/src/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/humitifier-server/src/api/apps.py b/humitifier-server/src/api/apps.py new file mode 100644 index 0000000..878e7d5 --- /dev/null +++ b/humitifier-server/src/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "api" diff --git a/humitifier-server/src/api/migrations/__init__.py b/humitifier-server/src/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/api/models.py b/humitifier-server/src/api/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/humitifier-server/src/api/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/humitifier-server/src/api/tests.py b/humitifier-server/src/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/humitifier-server/src/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/humitifier-server/src/api/urls.py b/humitifier-server/src/api/urls.py new file mode 100644 index 0000000..920acac --- /dev/null +++ b/humitifier-server/src/api/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from .views import UploadScans + +app_name = 'api' + +urlpatterns = [ + path("upload_scans/", UploadScans.as_view(), name="upload_scans"), +] diff --git a/humitifier-server/src/api/views.py b/humitifier-server/src/api/views.py new file mode 100644 index 0000000..63db268 --- /dev/null +++ b/humitifier-server/src/api/views.py @@ -0,0 +1,37 @@ +from rest_framework.exceptions import APIException +from rest_framework.response import Response +from rest_framework.views import APIView + +from hosts.models import Host +from hosts.utils import historical_clean + + +# Create your views here. +class UploadScans(APIView): + """ + View to list all users in the system. + + * Requires token authentication. + * Only admin users are able to access this view. + """ + + def post(self, request, format=None): + """ + Return a list of all users. + """ + scans = request.data + hosts = [] + + if isinstance(scans, list): + for scan in scans: + if 'host' not in scan or 'data' not in scan: + raise APIException("Invalid scan data") + + host, created = Host.objects.get_or_create(fqdn=scan['host']) + host.add_scan(scan['data']) + + hosts.append(host) + + historical_clean() + + return Response("ok") \ No newline at end of file diff --git a/humitifier-server/src/hosts/__init__.py b/humitifier-server/src/hosts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/hosts/admin.py b/humitifier-server/src/hosts/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/humitifier-server/src/hosts/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/humitifier-server/src/hosts/alerts.py b/humitifier-server/src/hosts/alerts.py new file mode 100644 index 0000000..5d333cc --- /dev/null +++ b/humitifier-server/src/hosts/alerts.py @@ -0,0 +1,79 @@ +from hosts.models import AlertLevel, AlertType, Host + + +def outdated_os(scan_data: dict, host: Host): + """The OS is outdated""" + hostname_ctl_data = scan_data.get("HostnameCtl", None) + + OUTDATED_OS = [ + "Debian GNU/Linux 10 (buster)", + "Debian GNU/Linux 9 (stretch)", + "CentOS Linux 7 (Core)" + ] + + if not hostname_ctl_data or not isinstance(hostname_ctl_data, dict): + return None + + if os := hostname_ctl_data.get("os"): + if os in OUTDATED_OS: + return host.alerts.get_or_create( + level=AlertLevel.WARNING, + type=AlertType.OUTDATED_OS, + message="The operating system is no longer supported", + ) + + +def has_fact_error(scan_data, host): + """One or more facts could not be collected""" + + for fact, data in scan_data.items(): + if not isinstance(data, dict): + # Some facts are not dicts if they succeeded, so we can assume it + # was fine + continue + + if 'exception' in data: + return host.alerts.get_or_create( + level=AlertLevel.CRITICAL, + type=AlertType.FACT_ERROR, + message="One or more facts could not be collected", + ) + + +def puppet_agent_disabled(scan_data: dict, host: Host): + """Puppet agent is disabled""" + puppet_agent_status = scan_data.get("PuppetAgentStatus") + + + if not puppet_agent_status or not isinstance(puppet_agent_status, dict): + return None + + if puppet_agent_status.get("disabled"): + + return host.alerts.get_or_create( + level=AlertLevel.CRITICAL, + type=AlertType.DISABLED_PUPPET, + message="Puppet agent is disabled", + ) + +ALERTS = [outdated_os, has_fact_error, puppet_agent_disabled] + +def generate_alerts(scan_data: dict, host: Host, delete_old_alerts: bool = True): + alerts = [] + + if not scan_data: + return host.alerts.get_or_create( + level=AlertLevel.CRITICAL, + type="no_scan_data", + message="No scan data available", + ) + else: + for alert_func in ALERTS: + if alert_data := alert_func(scan_data, host): + alert, created = alert_data + alerts.append(alert) + + if delete_old_alerts: + host.alerts.exclude(pk__in=[alert.pk for alert in alerts]).delete() + + return alerts \ No newline at end of file diff --git a/humitifier-server/src/hosts/apps.py b/humitifier-server/src/hosts/apps.py new file mode 100644 index 0000000..542f457 --- /dev/null +++ b/humitifier-server/src/hosts/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class HostsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "hosts" diff --git a/humitifier-server/src/hosts/filters.py b/humitifier-server/src/hosts/filters.py new file mode 100644 index 0000000..de9852d --- /dev/null +++ b/humitifier-server/src/hosts/filters.py @@ -0,0 +1,162 @@ +from collections import OrderedDict + +import django_filters +from django.db.models.expressions import RawSQL +from django.forms import Form +from django_filters import ChoiceFilter + +from hosts.models import Alert, AlertLevel, AlertType, Host + +def _get_choices(field, strip_quotes=True): + # The empty order_by() is required to remove the default ordering + # which would otherwise mess up the distinct() call. + # (It would add the ordering field to the SELECT list, which means postgres + # would view every row as distinct.) + db_values = Host.objects.values_list(field, flat=True).order_by().distinct() + # Not needed, but just to be sure + db_values = set(db_values) + values = [] + + for db_value in db_values: + if db_value: + human_label = db_value + if strip_quotes: + human_label = human_label[1:-1] + + values.append((db_value, human_label)) + + return values + + +class TextSearchFilter(django_filters.Filter): + + def filter(self, qs, value): + if value: + kwargs = { + f"{self.field_name}__icontains": value + } + return qs.filter(**kwargs) + return qs + +class PackageFilter(django_filters.Filter): + + def filter(self, qs, value): + if value: + qs = qs.annotate( + package_exists=RawSQL( + "SELECT 1 FROM jsonb_array_elements(\"hosts_host\".\"last_scan_cache\"->'PackageList') AS pkg WHERE pkg->>'name' = %s", + [value] + ) + ) + return qs.filter(package_exists__isnull=False) + return qs + +class HostAlertLevelFilter(ChoiceFilter): + + def __init__(self, *args, **kwargs): + kwargs['choices'] = AlertLevel.choices + kwargs['field_name'] = 'alerts__level' + super().__init__(*args, **kwargs) + + def filter(self, qs, value): + if value: + return qs.filter(alerts__level=value) + return qs + + +class HostAlertTypeFilter(ChoiceFilter): + + def __init__(self, *args, **kwargs): + kwargs['choices'] = AlertType.choices + kwargs['field_name'] = 'alerts__type' + super().__init__(*args, **kwargs) + + def filter(self, qs, value): + if value: + return qs.filter(alerts__type=value) + return qs + + +class FiltersForm(Form): + template_name = 'base/page_parts/filters_form_template.html' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + for field_name, field in self.fields.items(): + self.fields[field_name].widget.attrs['placeholder'] = field.label + + +class HostFilters(django_filters.FilterSet): + class Meta: + model = Host + fields = ['fqdn'] + form = FiltersForm + + fqdn = TextSearchFilter( + label="Hostname", + field_name="fqdn", + ) + + os = django_filters.ChoiceFilter( + label="OS", + field_name="os", + choices=lambda: _get_choices('os'), + empty_label="Operating System" + ) + + alert_level = HostAlertLevelFilter( + empty_label="Alert level", + ) + + alert_type = HostAlertTypeFilter( + empty_label="Alert Type", + ) + + department = django_filters.ChoiceFilter( + label="Department", + field_name="department", + choices=lambda: _get_choices('department'), + empty_label="Department", + ) + + contact = django_filters.ChoiceFilter( + label="Contact", + field_name="contact", + choices=lambda: _get_choices('contact'), + empty_label="Contact", + ) + + package = PackageFilter( + label="Package", + ) + + is_wp = django_filters.ChoiceFilter( + empty_label="Is WordPress", + field_name="last_scan_cache__IsWordpress__is_wp", + choices=[ + (True, "Yes"), + (False, "No"), + ] + ) + + +class AlertFilters(django_filters.FilterSet): + class Meta: + model = Alert + fields = ['level', 'type'] + form = FiltersForm + + + level = django_filters.ChoiceFilter( + label="Alert level", + field_name="level", + choices=AlertLevel.choices, + empty_label="Alert level", + ) + + type = django_filters.MultipleChoiceFilter( + label="Alert type", + field_name="type", + choices=AlertType.choices, + ) \ No newline at end of file diff --git a/humitifier-server/src/hosts/management/__init__.py b/humitifier-server/src/hosts/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/hosts/management/commands/__init__.py b/humitifier-server/src/hosts/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/hosts/management/commands/recreate_alerts.py b/humitifier-server/src/hosts/management/commands/recreate_alerts.py new file mode 100644 index 0000000..cd07032 --- /dev/null +++ b/humitifier-server/src/hosts/management/commands/recreate_alerts.py @@ -0,0 +1,13 @@ +from django.core.management.base import BaseCommand + +from hosts.alerts import generate_alerts +from hosts.models import Host + + +class Command(BaseCommand): + + def handle(self, *args, **options): + hosts = Host.objects.all() + + for host in hosts: + generate_alerts(host.last_scan_cache, host) \ No newline at end of file diff --git a/humitifier-server/src/hosts/menus.py b/humitifier-server/src/hosts/menus.py new file mode 100644 index 0000000..1d809f4 --- /dev/null +++ b/humitifier-server/src/hosts/menus.py @@ -0,0 +1,43 @@ +from django.urls import reverse +from simple_menu import Menu, MenuItem + +Menu.add_item( + "main", + MenuItem( + "Hosts", + reverse("hosts:list"), + weight=10, + icon="icons/host.html", + separator=True, + ) +) + +Menu.add_item( + "main", + MenuItem( + "Tasks", + reverse("hosts:tasks"), + weight=10, + icon="icons/tasks.html", + ) +) + +Menu.add_item( + "main", + MenuItem( + "Scan profiles", + reverse("hosts:scan_profiles"), + weight=10, + icon="icons/terminal.html", + ) +) + +Menu.add_item( + "main", + MenuItem( + "Data sources", + reverse("hosts:data_sources"), + weight=10, + icon="icons/databases.html", + ) +) diff --git a/humitifier-server/src/hosts/migrations/0001_initial.py b/humitifier-server/src/hosts/migrations/0001_initial.py new file mode 100644 index 0000000..0173906 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# Generated by Django 5.1.2 on 2024-10-23 13:07 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Host", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("fqdn", models.CharField(max_length=255)), + ("last_scan_cache", models.JSONField()), + ("last_scan_date", models.DateTimeField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("protected", models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name="Scan", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("data", models.JSONField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ( + "host", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="hosts.host" + ), + ), + ], + options={ + "ordering": ["-created_at"], + }, + ), + ] diff --git a/humitifier-server/src/hosts/migrations/0002_alter_host_last_scan_cache_alter_host_last_scan_date.py b/humitifier-server/src/hosts/migrations/0002_alter_host_last_scan_cache_alter_host_last_scan_date.py new file mode 100644 index 0000000..522ef41 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0002_alter_host_last_scan_cache_alter_host_last_scan_date.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.2 on 2024-10-23 13:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="host", + name="last_scan_cache", + field=models.JSONField(null=True), + ), + migrations.AlterField( + model_name="host", + name="last_scan_date", + field=models.DateTimeField(null=True), + ), + ] diff --git a/humitifier-server/src/hosts/migrations/0003_alter_scan_host.py b/humitifier-server/src/hosts/migrations/0003_alter_scan_host.py new file mode 100644 index 0000000..a7dd194 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0003_alter_scan_host.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.2 on 2024-10-23 15:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0002_alter_host_last_scan_cache_alter_host_last_scan_date"), + ] + + operations = [ + migrations.AlterField( + model_name="scan", + name="host", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="scans", + to="hosts.host", + ), + ), + ] diff --git a/humitifier-server/src/hosts/migrations/0004_host_contact_host_department.py b/humitifier-server/src/hosts/migrations/0004_host_contact_host_department.py new file mode 100644 index 0000000..2928676 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0004_host_contact_host_department.py @@ -0,0 +1,50 @@ +# Generated by Django 5.1.2 on 2024-10-23 15:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0003_alter_scan_host"), + ] + + operations = [ + migrations.AddField( + model_name="host", + name="contact", + field=models.GeneratedField( + db_persist=True, + expression=models.Case( + models.When( + last_scan_cache__HostMeta__contact="null", + then=models.Value(None), + ), + models.When( + last_scan_cache__HostMeta__contact=None, then=models.Value(None) + ), + default=models.F("last_scan_cache__HostMeta__contact"), + ), + output_field=models.CharField(max_length=255), + ), + ), + migrations.AddField( + model_name="host", + name="department", + field=models.GeneratedField( + db_persist=True, + expression=models.Case( + models.When( + last_scan_cache__HostMeta__department="null", + then=models.Value(None), + ), + models.When( + last_scan_cache__HostMeta__department=None, + then=models.Value(None), + ), + default=models.F("last_scan_cache__HostMeta__department"), + ), + output_field=models.CharField(max_length=255), + ), + ), + ] diff --git a/humitifier-server/src/hosts/migrations/0005_alert.py b/humitifier-server/src/hosts/migrations/0005_alert.py new file mode 100644 index 0000000..cd26af2 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0005_alert.py @@ -0,0 +1,53 @@ +# Generated by Django 5.1.2 on 2024-10-24 13:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0004_host_contact_host_department"), + ] + + operations = [ + migrations.CreateModel( + name="Alert", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "level", + models.CharField( + choices=[ + ("info", "Info"), + ("warning", "Warning"), + ("critical", "Critical"), + ], + max_length=255, + ), + ), + ("type", models.CharField(max_length=255)), + ("message", models.TextField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ( + "host", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="alerts", + to="hosts.host", + ), + ), + ], + options={ + "ordering": ["-created_at"], + }, + ), + ] diff --git a/humitifier-server/src/hosts/migrations/0006_alter_host_options_host_os.py b/humitifier-server/src/hosts/migrations/0006_alter_host_options_host_os.py new file mode 100644 index 0000000..7ae2e97 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0006_alter_host_options_host_os.py @@ -0,0 +1,34 @@ +# Generated by Django 5.1.2 on 2024-10-24 17:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0005_alert"), + ] + + operations = [ + migrations.AlterModelOptions( + name="host", + options={"ordering": ["fqdn"]}, + ), + migrations.AddField( + model_name="host", + name="os", + field=models.GeneratedField( + db_persist=True, + expression=models.Case( + models.When( + last_scan_cache__HostnameCtl__os="null", then=models.Value(None) + ), + models.When( + last_scan_cache__HostnameCtl__os=None, then=models.Value(None) + ), + default=models.F("last_scan_cache__HostnameCtl__os"), + ), + output_field=models.CharField(max_length=255), + ), + ), + ] diff --git a/humitifier-server/src/hosts/migrations/__init__.py b/humitifier-server/src/hosts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/hosts/models.py b/humitifier-server/src/hosts/models.py new file mode 100644 index 0000000..543f6d5 --- /dev/null +++ b/humitifier-server/src/hosts/models.py @@ -0,0 +1,199 @@ +from django.db import models +from django.db.models import Case, F, Value, When +from django.utils.safestring import mark_safe + +from main.templatetags.strip_quotes import strip_quotes + + +class AlertLevel(models.TextChoices): + INFO = 'info' + WARNING = 'warning' + CRITICAL = 'critical' + + +class AlertType(models.TextChoices): + OUTDATED_OS = 'Outdated OS' + FACT_ERROR = 'Has fact error' + DISABLED_PUPPET = 'Puppet agent disabled' + + +def _json_value(field: str): + """ + Fields derived from JSON data are a bit tricky. If the field is null, + PSQL will derive 'null' as a string, which is not the same as None. + (Also, somehow 'null' as a string seems to be uploaded as well). + + So, we do a small case to transform those 'null' strings into actual nulls. + :param field: + :return: + """ + return Case( + When( + **{field: 'null', 'then': Value(None)} + ), + When( + **{field: None, 'then': Value(None)} + ), + default=F(field), + ) + + +class HostManager(models.Manager): + + def get_for_user(self, user): + # TODO: implement this when we have user support + return self.get_queryset() + + +class Host(models.Model): + class Meta: + ordering = ['fqdn'] + + objects = HostManager() + + fqdn = models.CharField(max_length=255) + + last_scan_cache = models.JSONField( + null=True, + ) + + last_scan_date = models.DateTimeField( + null=True, + ) + + created_at = models.DateTimeField(auto_now_add=True) + + protected = models.BooleanField(default=False) + + ## + ## Generated fields + ## These fields are derived from the last scan cache, and are generated + ## by the database itself. THis way, we have a very performant way + ## to filter on these values + ## + + department = models.GeneratedField( + expression=_json_value('last_scan_cache__HostMeta__department'), + output_field=models.CharField(max_length=255), + db_persist=True, + ) + + contact = models.GeneratedField( + expression=_json_value('last_scan_cache__HostMeta__contact'), + output_field=models.CharField(max_length=255), + db_persist=True, + ) + + os = models.GeneratedField( + expression=_json_value('last_scan_cache__HostnameCtl__os'), + output_field=models.CharField(max_length=255), + db_persist=True, + ) + + ## + ## Methods + ## + + def add_scan( + self, + scan_data, + *, + cache_scan: bool = True, + generate_alerts: bool = True + ): + scan = Scan(host=self, data=scan_data) + scan.save() + + if cache_scan: + self.last_scan_cache = scan_data + self.last_scan_date = scan.created_at + self.save() + + if generate_alerts: + from hosts import alerts + + alerts.generate_alerts(scan_data, self) + + return scan + + ## + ## Properties + ## + + @property + def num_critical_alerts(self): + return self.alerts.filter(level=AlertLevel.CRITICAL).count() + + @property + def num_warning_alerts(self): + return self.alerts.filter(level=AlertLevel.WARNING).count() + + @property + def num_info_alerts(self): + return self.alerts.filter(level=AlertLevel.INFO).count() + + ## + ## Display methods + ## + + def get_department_display(self): + if not self.department: + return mark_safe("Unknown") + return strip_quotes(self.department) + + def get_contact_display(self): + if not self.contact: + return mark_safe("Unknown") + return strip_quotes(self.contact) + + def get_os_display(self): + if not self.os: + return mark_safe("Unknown") + return strip_quotes(self.os) + + def __str__(self): + return self.fqdn + + def __repr__(self): + return f"" + + +class Scan(models.Model): + class Meta: + ordering = ['-created_at'] + + host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='scans') + + data = models.JSONField() + + created_at = models.DateTimeField(auto_now_add=True) + + +class AlertManager(models.Manager): + + def get_for_user(self, user): + # TODO: implement this when we have user support + return self.get_queryset() + + +class Alert(models.Model): + class Meta: + ordering = ['-created_at'] + + objects = AlertManager() + + host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='alerts') + + level = models.CharField(max_length=255, choices=AlertLevel.choices) + + type = models.CharField(max_length=255, choices=AlertType.choices) + + message = models.TextField() + + created_at = models.DateTimeField(auto_now_add=True) + + def __repr__(self): + return f"" + + def __str__(self): + return self.message diff --git a/humitifier-server/src/hosts/templates/hosts/detail.html b/humitifier-server/src/hosts/templates/hosts/detail.html new file mode 100644 index 0000000..7336803 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail.html @@ -0,0 +1,79 @@ +{% extends 'base/base_page_template.html' %} + +{% load param_replace %} + +{% block page_title %}{{ host.fqdn }} | {{ block.super }}{% endblock %} + +{% block content %} +
+
{{ host.fqdn }}
+
+ {{ current_scan_date|date:"Y-m-d H:i" }} +
+
+
+ + +
+ +
+
+ + {% if is_latest_scan %} + {% for alert in alerts %} + {% if alert.level == 'critical' %} + {% include 'hosts/detail_parts/alerts/critical.html' %} + {% elif alert.level == 'warning' %} + {% include 'hosts/detail_parts/alerts/warning.html' %} + {% elif alert.level == 'info' %} + {% include 'hosts/detail_parts/alerts/info.html' %} + {% endif %} + {% endfor %} + {% else %} + {% with alert="This is historical data, it does not reflect the current configuration." %} + {% include 'hosts/detail_parts/alerts/warning.html' %} + {% endwith %} + {% endif %} + +
+
+ {% include 'hosts/detail_parts/facts/host_meta.html' %} +
+ {% if scan_data.HostMeta and scan_data.HostMeta.vhosts %} +
+ {% include 'hosts/detail_parts/facts/vhosts.html' %} +
+ {% endif %} + {% if scan_data.HostMeta and scan_data.HostMeta.databases %} +
+ {% include 'hosts/detail_parts/facts/databases.html' %} +
+ {% endif %} +
+ {% include 'hosts/detail_parts/facts/blocks.html' %} +
+
+ {% include 'hosts/detail_parts/facts/memory.html' %} +
+
+ {% include 'hosts/detail_parts/facts/packages.html' %} +
+
+ {% include 'hosts/detail_parts/facts/users.html' %} +
+
+ {% include 'hosts/detail_parts/facts/groups.html' %} +
+
+{% endblock %} \ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/critical.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/critical.html new file mode 100644 index 0000000..4f3f308 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/critical.html @@ -0,0 +1,4 @@ +
+ {% include 'icons/critical.html' %} +
{{ alert }}
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/info.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/info.html new file mode 100644 index 0000000..8af7011 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/info.html @@ -0,0 +1,4 @@ +
+ {% include 'icons/info.html' %} +
{{ alert }}
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/warning.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/warning.html new file mode 100644 index 0000000..aef8d67 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/alerts/warning.html @@ -0,0 +1,4 @@ +
+ {% include 'icons/warning.html' %} +
{{ alert }}
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/blocks.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/blocks.html new file mode 100644 index 0000000..3ee2988 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/blocks.html @@ -0,0 +1,15 @@ +{% load host_tags %} + +
+
Storage
+
+ {% for block in scan_data.Blocks %} + {{ block.mount }} + {{ block.name }} + {{ block.used_mb|size_from_kb }} / {{ block.size_mb|size_from_kb }} +
+
+
+ {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/databases.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/databases.html new file mode 100644 index 0000000..feee590 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/databases.html @@ -0,0 +1,11 @@ +{% load host_tags %} + +
+
Databases
+
+ {% for db_type, dbs in scan_data.HostMeta.databases.items %} + {{ db_type }} + {{ dbs|join:", " }} + {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/groups.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/groups.html new file mode 100644 index 0000000..1e88a2e --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/groups.html @@ -0,0 +1,19 @@ +{% load host_tags %} + +
+
+
Groups
+ +
+ +
+ {% for group in scan_data.Groups %} +
+
{{ group.name }}
+
+ {{ group.users|join:", " }} +
+
+ {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/host_meta.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/host_meta.html new file mode 100644 index 0000000..d141d53 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/host_meta.html @@ -0,0 +1,151 @@ +{% load host_tags %} + +
+
Host Metadata
+ + {# Begin HostnameCTL #} +
+ +
Hostname
+
+ {% if scan_data.HostnameCtl.hostname %} + {{ scan_data.HostnameCtl.hostname }} + {% else %} +
Unknown
+ {% endif %} +
+ +
Operating system
+
+ {% if scan_data.HostnameCtl.os %} + {{ scan_data.HostnameCtl.os }} + {% else %} +
Unknown
+ {% endif %} +
+ +
CPE OS Name
+
+ {% if scan_data.HostnameCtl.cpe_os_name %} + {{ scan_data.HostnameCtl.cpe_os_name }} + {% else %} +
Unknown
+ {% endif %} +
+ +
Kernel
+
+ {% if scan_data.HostnameCtl.kernel %} + {{ scan_data.HostnameCtl.kernel }} + {% else %} +
Unknown
+ {% endif %} +
+ +
Virtualization
+
+ {% if scan_data.HostnameCtl.virtualization %} + {{ scan_data.HostnameCtl.virtualization }} + {% else %} +
Unknown
+ {% endif %} +
+ + {# END HostnameCTL #} + {# BEGIN Puppet agent #} + +
Puppet agent status
+
+ {% if scan_data.PuppetAgentStatus.disabled is not None %} + {% if scan_data.PuppetAgentStatus.disabled %} +
Disabled
+ {% else %} +
Enabled
+ {% endif %} + {% else %} +
Unknown
+ {% endif %} +
+ + {# END Puppet agent #} + {# BEGIN Uptime #} + +
Uptime
+
+ {% if scan_data.Uptime is not None %} + {{ scan_data.Uptime|uptime:current_scan_date }} + {% else %} +
Unknown
+ {% endif %} +
+ + {# END Uptime #} + {# BEGIN HostMeta #} + +
Department
+
+ {% if scan_data.HostMeta.department %} + {{ scan_data.HostMeta.department }} + {% else %} +
Unknown
+ {% endif %} +
+ +
Contact
+
+ {% if scan_data.HostMeta.contact %} + {{ scan_data.HostMeta.contact }} + {% else %} +
Unknown
+ {% endif %} +
+ +
Update policy
+
+ {% if scan_data.HostMeta.update_policy %} + {% if scan_data.HostMeta.update_policy.enable %} +
Enabled
+ {% else %} +
Not enabled
+ {% endif %} + {% if scan_data.HostMeta.update_policy.apply_updates %} +
Applied
+ {% else %} +
Not applied
+ {% endif %} + {% else %} +
Unknown
+ {% endif %} +
+ +
WebDav
+
+ {% if scan_data.HostMeta.webdav %} + {{ scan_data.HostMeta.webdav }} + {% else %} +
Unknown/none
+ {% endif %} +
+ +
Fileservers
+
+ {% if scan_data.HostMeta.fileservers %} +
    + {% for fileserver in scan_data.HostMeta.fileservers %} +
  • {{ fileserver }}
  • + {% endfor %} +
+ {% else %} +
Unknown/none
+ {% endif %} +
+
Is wordpress?
+
+ {% if scan_data.IsWordpress is not None %} + {{ scan_data.IsWordpress.is_wp|yesno:"Yes,No" }} + {% else %} +
Unknown
+ {% endif %} +
+
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/memory.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/memory.html new file mode 100644 index 0000000..6acf796 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/memory.html @@ -0,0 +1,17 @@ +{% load host_tags %} + +
+
Memory
+
+ Memory + {{ scan_data.Memory.used_mb|size_from_mb }} / {{ scan_data.Memory.total_mb|size_from_mb }} +
+
+
+ Swap + {{ scan_data.Memory.swap_used_mb|size_from_mb }} / {{ scan_data.Memory.swap_total_mb|size_from_mb }} +
+
+
+
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/packages.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/packages.html new file mode 100644 index 0000000..e925c33 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/packages.html @@ -0,0 +1,19 @@ +{% load host_tags %} + +
+
+
Packages
+ +
+ +
+ {% for package in scan_data.PackageList %} +
+
{{ package.name }}
+
+ {{ package.version }} +
+
+ {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/users.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/users.html new file mode 100644 index 0000000..1422200 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/users.html @@ -0,0 +1,25 @@ +{% load host_tags %} + +
+
+
Users
+ +
+ +
+ {% for user in scan_data.Users %} +
+
{{ user.name }}
+
+ {% if user.info %} +
Info: {{ user.info }}
+ {% endif %} +
Group-id: {{ user.gid }}
+
User-id: {{ user.uid }}
+
Homedir: {{ user.home }}
+
Shell: {{ user.shell }}
+
+
+ {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/vhosts.html b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/vhosts.html new file mode 100644 index 0000000..39ba586 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/detail_parts/facts/vhosts.html @@ -0,0 +1,13 @@ +{% load host_tags %} + +
+
vHosts
+
+ {% for vhost in scan_data.HostMeta.vhosts %} + {% for label, data in vhost.items %} + {{ label }} + {{ data.docroot }} + {% endfor %} + {% endfor %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/list.html b/humitifier-server/src/hosts/templates/hosts/list.html new file mode 100644 index 0000000..27cf4e7 --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/list.html @@ -0,0 +1,72 @@ +{% extends "base/base_page_template.html" %} + +{% load humanize %} +{% load param_replace %} + +{% block page_title %}Hosts | {{ block.super }}{% endblock %} + +{% block content %} +
+ + +
+ {% include 'base/page_parts/paginator_top.html' %} +
+ + + + + + + + + + + + + {% for host in object_list %} + {% with host=host %} + {% include 'hosts/list_parts/host.html' %} + {% endwith %} + {% endfor %} + {% if object_list.count == 0 %} + + + + {% endif %} + +
+ Hostname + + Status +
+ {% if layout.wild_wasteland %} + No hosts found, have you looked under the rug? + {% else %} + No hosts found + {% endif %} +
+ +
+ {% include 'base/page_parts/paginator_bottom.html' %} +
+
+{% endblock %} \ No newline at end of file diff --git a/humitifier-server/src/hosts/templates/hosts/list_parts/host.html b/humitifier-server/src/hosts/templates/hosts/list_parts/host.html new file mode 100644 index 0000000..2010b2e --- /dev/null +++ b/humitifier-server/src/hosts/templates/hosts/list_parts/host.html @@ -0,0 +1,41 @@ +{% load strip_quotes %} + + + + {{ host.fqdn }} + + + + {{ host.get_os_display }} + + + {{ host.last_scan_date|date:"Y-m-d H:i" }} + + + {{ host.created_at|date:"Y-m-d H:i" }} + + + {{ host.get_department_display }} + + +
+ {% if host.alerts.count %} + {% if host.num_info_alerts %} +
+ {% include 'icons/info.html' %} {{ host.num_info_alerts }} +
+ {% endif %} + {% if host.num_warning_alerts %} +
+ {% include 'icons/warning.html' %} {{ host.num_warning_alerts }} +
+ {% endif %} + {% if host.num_critical_alerts %} +
+ {% include 'icons/critical.html' %} {{ host.num_critical_alerts }} +
+ {% endif %} + {% endif %} +
+ + diff --git a/humitifier-server/src/hosts/templatetags/__init__.py b/humitifier-server/src/hosts/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/hosts/templatetags/host_tags.py b/humitifier-server/src/hosts/templatetags/host_tags.py new file mode 100644 index 0000000..4d545fe --- /dev/null +++ b/humitifier-server/src/hosts/templatetags/host_tags.py @@ -0,0 +1,25 @@ +from datetime import datetime, timedelta + +from django import template +from django.template.defaultfilters import filesizeformat +from django.utils.timesince import timesince + +register = template.Library() + + +@register.filter() +def size_from_kb(value, **kwargs): + # We get the value in KB, but filesizeformat expects bytes + return filesizeformat(value * 1024) + +@register.filter() +def size_from_mb(value, **kwargs): + # We get the value in MB, but filesizeformat expects bytes + return filesizeformat(value * 1024 * 1024) + +@register.filter() +def uptime(time_in_seconds: float, reference: datetime): + + startup_datetime = reference - timedelta(seconds=time_in_seconds) + + return timesince(startup_datetime, reference) \ No newline at end of file diff --git a/humitifier-server/src/hosts/tests.py b/humitifier-server/src/hosts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/humitifier-server/src/hosts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/humitifier-server/src/hosts/urls.py b/humitifier-server/src/hosts/urls.py new file mode 100644 index 0000000..bed77f7 --- /dev/null +++ b/humitifier-server/src/hosts/urls.py @@ -0,0 +1,17 @@ +from django.urls import path + +from .views import DataSourcesView, ExportView, HostDetailView, HostsListView, \ + HostsRawDownloadView, ScanProfilesView, TasksView + +app_name = 'hosts' + +urlpatterns = [ + path("", HostsListView.as_view(), name="list"), + path("host//", HostDetailView.as_view(), name="detail"), + path("host//raw/", HostsRawDownloadView.as_view(), + name="download_raw"), + path("export/", ExportView.as_view(), name="export"), + path("tasks/", TasksView.as_view(), name="tasks"), + path("scan-profiles/", ScanProfilesView.as_view(), name="scan_profiles"), + path("data-sources/", DataSourcesView.as_view(), name="data_sources"), +] diff --git a/humitifier-server/src/hosts/utils.py b/humitifier-server/src/hosts/utils.py new file mode 100644 index 0000000..d327f19 --- /dev/null +++ b/humitifier-server/src/hosts/utils.py @@ -0,0 +1,53 @@ +from datetime import timedelta + +from django.db.models import Min +from django.utils import timezone + +from hosts.models import Scan + + +def historical_clean(): + """Cleans up historical scan data, keeping only one record per day per host for the past week and one record per month + + :return: + """ + now = timezone.now() + one_day_ago = now - timedelta(days=1) + one_week_ago = now - timedelta(days=7) + + # Step 1: Reduce records older than one day but within the past week, keeping one per day per host + daily_records = ( + Scan.objects + .filter(created_at__lt=one_day_ago, created_at__gte=one_week_ago) + .values('host', 'created_at__date') # Group by host and day + .annotate(earliest_record=Min('created_at')).distinct() + ) + + # Delete all records in the past week except the earliest record per day per host + for daily_record in daily_records: + Scan.objects.filter( + created_at__lt=one_day_ago, + created_at__gte=one_week_ago, + host=daily_record['host'], + created_at__date=daily_record['created_at__date'] + ).exclude(created_at=daily_record['earliest_record']).delete() + + # Step 2: Reduce records older than one week, keeping one per month per host + monthly_records = ( + Scan.objects + .filter(created_at__lt=one_week_ago) + .extra({ + 'month': "date_trunc('month', created_at)" + }) + .values('host', 'month') # Group by host and month + .annotate(earliest_record=Min('created_at')) + ) + + # Delete all records older than a week except the earliest record per month per host + for monthly_record in monthly_records: + Scan.objects.filter( + created_at__lt=one_week_ago, + host=monthly_record['host'], + created_at__month=monthly_record['month'].month, + created_at__year=monthly_record['month'].year + ).exclude(created_at=monthly_record['earliest_record']).delete() diff --git a/humitifier-server/src/hosts/views.py b/humitifier-server/src/hosts/views.py new file mode 100644 index 0000000..d137853 --- /dev/null +++ b/humitifier-server/src/hosts/views.py @@ -0,0 +1,119 @@ +import json + +from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.http import HttpResponse +from django.views import View +from django.views.generic import TemplateView + +from main.views import FilteredListView + +from .filters import HostFilters +from .models import Host + +# Create your views here. + +class HostsListView(FilteredListView): + model = Host + filterset_class = HostFilters + paginate_by = 50 + template_name = 'hosts/list.html' + ordering_fields = { + 'fqdn': 'Hostname', + 'os': 'Operating System', + 'department': 'Department', + 'contact': 'Contact', + } + + + def get_queryset(self): + queryset = Host.objects.get_for_user(self.request.user) + + ordering = self.get_ordering() + if ordering: + if isinstance(ordering, str): + ordering = (ordering,) + queryset = queryset.order_by(*ordering) + + self.filterset = self.filterset_class(self.request.GET, queryset=queryset) + + filtered_qs = self.filterset.qs + # We're going to need the data for the alerts in the template + # So, let's prefetch it here for _performance_ + filtered_qs = filtered_qs.prefetch_related('alerts') + + return filtered_qs.distinct() + +class HostDetailView(TemplateView): + template_name = 'hosts/detail.html' + + LATEST_KEY = 'latest' + + def get_current_scan(self): + return self.request.GET.get('scan', self.LATEST_KEY) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + host = Host.objects.get_for_user(self.request.user).get(fqdn=kwargs['fqdn']) + scan_data = host.last_scan_cache + current_scan = self.get_current_scan() + current_scan_date = host.last_scan_date + scan = None + + try: + if current_scan != self.LATEST_KEY: + scan = host.scans.get(created_at=current_scan) + scan_data = scan.data + current_scan_date = scan.created_at + except (ValidationError, ObjectDoesNotExist): + current_scan = self.LATEST_KEY + + is_latest_scan = (current_scan == self.LATEST_KEY or scan.created_at == + host.last_scan_date) + + context['host'] = host + context['scan_data'] = scan_data + context['current_scan'] = current_scan + context['current_scan_date'] = current_scan_date + context['all_scans'] = host.scans.values_list('created_at', flat=True) + context['is_latest_scan'] = is_latest_scan + context['alerts'] = host.alerts.order_by('level') + + return context + + +class HostsRawDownloadView(View): + + def get(self, request, fqdn): + host = Host.objects.get_for_user(request.user).get(fqdn=fqdn) + scan_data = host.last_scan_cache + scan_date = host.last_scan_date + + requested_scan = request.GET.get('scan', None) + if requested_scan: + try: + scan = host.scans.get(created_at=requested_scan) + scan_data = scan.data + scan_date = scan.created_at + except (ObjectDoesNotExist, ValidationError): + pass + + response = HttpResponse(json.dumps(scan_data, indent=4), content_type='application/json') + response['Content-Disposition'] = ( + f'attachment; filename="scan_{fqdn}_{scan_date}.json"' + ) + + return response + + +class ExportView(TemplateView): + template_name = 'main/not_implemented.html' + +class TasksView(TemplateView): + template_name = 'main/not_implemented.html' + +class ScanProfilesView(TemplateView): + template_name = 'main/not_implemented.html' + +class DataSourcesView(TemplateView): + template_name = 'main/not_implemented.html' \ No newline at end of file diff --git a/humitifier-server/src/humitifier_server/__init__.py b/humitifier-server/src/humitifier_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/humitifier_server/asgi.py b/humitifier-server/src/humitifier_server/asgi.py new file mode 100644 index 0000000..34b1773 --- /dev/null +++ b/humitifier-server/src/humitifier_server/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for humitifier_server 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/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "humitifier_server.settings") + +application = get_asgi_application() diff --git a/humitifier-server/src/humitifier_server/settings.py b/humitifier-server/src/humitifier_server/settings.py new file mode 100644 index 0000000..df1a40a --- /dev/null +++ b/humitifier-server/src/humitifier_server/settings.py @@ -0,0 +1,154 @@ +""" +Django settings for humitifier_server project. + +Generated by 'django-admin startproject' using Django 5.1.2. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-ffdjavjsp%s%b069$aai#h7odtbd#!q8uu7=hn1tv&y$gdq17_" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] +INTERNAL_IPS = [ + # ... + "127.0.0.1", + # ... +] + + +# Application definition + +INSTALLED_APPS = [ + # Local apps with overrides here + "main", + # Django + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.humanize", + "django.forms", + # Third-party apps + "rest_framework", + "simple_menu", + # Local apps + "hosts", + "api", +] + + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + + +if DEBUG: + INSTALLED_APPS.append("debug_toolbar") + MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") + +ROOT_URLCONF = "humitifier_server.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "main.context_processors.layout_context" + ], + }, + }, +] + +FORM_RENDERER = "main.forms.CustomFormRenderer" + +WSGI_APPLICATION = "humitifier_server.wsgi.application" + +# Auth +AUTH_USER_MODEL = "main.User" + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "postgres", + "USER": "postgres", + "PASSWORD": "postgres", + "HOST": "127.0.0.1", + "PORT": "6432", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/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", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "Europe/Amsterdam" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/humitifier-server/src/humitifier_server/urls.py b/humitifier-server/src/humitifier_server/urls.py new file mode 100644 index 0000000..2a989a3 --- /dev/null +++ b/humitifier-server/src/humitifier_server/urls.py @@ -0,0 +1,34 @@ +""" +URL configuration for humitifier_server project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" + +from django.conf import settings +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path("admin/", admin.site.urls), + path("api/", include('api.urls')), + path("hosts/", include('hosts.urls')), + path("", include('main.urls')), +] + +if settings.DEBUG: + import debug_toolbar + + urlpatterns = [ + path("__debug__/", include(debug_toolbar.urls)), + ] + urlpatterns \ No newline at end of file diff --git a/humitifier-server/src/humitifier_server/wsgi.py b/humitifier-server/src/humitifier_server/wsgi.py new file mode 100644 index 0000000..d961bd1 --- /dev/null +++ b/humitifier-server/src/humitifier_server/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for humitifier_server project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "humitifier_server.settings") + +application = get_wsgi_application() diff --git a/humitifier-server/src/main/__init__.py b/humitifier-server/src/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/main/admin.py b/humitifier-server/src/main/admin.py new file mode 100644 index 0000000..fb4d3c1 --- /dev/null +++ b/humitifier-server/src/main/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin + +from .models import User + +# Register your models here. +admin.site.register(User, UserAdmin) \ No newline at end of file diff --git a/humitifier-server/src/main/apps.py b/humitifier-server/src/main/apps.py new file mode 100644 index 0000000..34f1451 --- /dev/null +++ b/humitifier-server/src/main/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MainConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "main" diff --git a/humitifier-server/src/main/context_processors.py b/humitifier-server/src/main/context_processors.py new file mode 100644 index 0000000..9a35c51 --- /dev/null +++ b/humitifier-server/src/main/context_processors.py @@ -0,0 +1,46 @@ +import random + +from django.conf import settings + +from hosts.models import Alert, AlertLevel, Host + + +def layout_context(request): + """This function is used to add data to the template-context for all views. + It adds all the little nuggets of data the base template displays. + """ + user = request.user + + hosts = Host.objects.get_for_user(user) + all_alerts = Alert.objects.get_for_user(user) + + tag_line = "HumIT CMDB" + + wild_wasteland = settings.DEBUG # TODO: user setting + + if wild_wasteland: + jokes = [ + "Performs best on a 386", + "Now with 100% more bugs!", + "Performance edition", + "I like trains", + "Prrrrrrrrrr", + "May contain traces of nuts", + "Caution: Do not eat", + "Don't look at me!", + "Who watches the watchmen?", + "How are you?", + "CAUTION: This email originated from outside of Utrecht University. Do not click links or open attachments unless you recognize the sender and know the content is safe.", + ] + tag_line = random.choice(jokes) + + return { + "layout": { + "num_hosts": hosts.count(), + "num_info_alerts": all_alerts.filter(level=AlertLevel.INFO).count(), + "num_warning_alerts": all_alerts.filter(level=AlertLevel.WARNING).count(), + "num_critical_alerts": all_alerts.filter(level=AlertLevel.CRITICAL).count(), + "wild_wasteland": wild_wasteland, + "tag_line": tag_line, + } + } \ No newline at end of file diff --git a/humitifier-server/src/main/forms.py b/humitifier-server/src/main/forms.py new file mode 100644 index 0000000..9c0f5ff --- /dev/null +++ b/humitifier-server/src/main/forms.py @@ -0,0 +1,6 @@ +from django.forms.renderers import TemplatesSetting + + +class CustomFormRenderer(TemplatesSetting): + form_template_name = 'base/forms/form_template.html' + field_template_name = "base/forms/field_template.html" diff --git a/humitifier-server/src/main/management/__init__.py b/humitifier-server/src/main/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/main/management/commands/__init__.py b/humitifier-server/src/main/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/main/management/commands/fake_hosts.py b/humitifier-server/src/main/management/commands/fake_hosts.py new file mode 100644 index 0000000..88a78c0 --- /dev/null +++ b/humitifier-server/src/main/management/commands/fake_hosts.py @@ -0,0 +1,27 @@ +from django.core.management.base import BaseCommand, CommandError +from faker import Faker + +from hosts.models import Host + + +class Command(BaseCommand): + + def handle(self, *args, **options): + hosts = Host.objects.all() + + faker = Faker() + + for host in hosts: + scan = host.scans.first() + for i in range(1000): + host.pk = None + + host.fqdn = faker.domain_name() + + host.save() + + scan.host = host + + for j in range(1000): + scan.pk = None + scan.save() \ No newline at end of file diff --git a/humitifier-server/src/main/menus.py b/humitifier-server/src/main/menus.py new file mode 100644 index 0000000..f050b55 --- /dev/null +++ b/humitifier-server/src/main/menus.py @@ -0,0 +1,55 @@ +from django.urls import reverse +from simple_menu import Menu, MenuItem + +Menu.add_item( + "main", + MenuItem( + "Dashboard", + reverse("main:dashboard"), + weight=1, + icon="icons/dashboard.html", + ) +) + +Menu.add_item( + "main", + MenuItem( + "Users", + reverse("main:users"), + weight=20, + icon="icons/users.html", + separator=True, + ) +) + +Menu.add_item( + "main", + MenuItem( + "Access profiles", + reverse("main:access_profiles"), + weight=21, + icon="icons/shield.html", + ) +) + +Menu.add_item( + "main", + MenuItem( + "OAuth2 Applications", + reverse("main:oauth_applications"), + weight=21, + icon="icons/api.html", + ) +) + +Menu.add_item( + "main", + MenuItem( + "Django Admin", + reverse("admin:index"), + weight=999, + icon="icons/admin.html", + check=lambda request: request.user.is_superuser, + separator=True, + ) +) diff --git a/humitifier-server/src/main/migrations/0001_initial.py b/humitifier-server/src/main/migrations/0001_initial.py new file mode 100644 index 0000000..e872794 --- /dev/null +++ b/humitifier-server/src/main/migrations/0001_initial.py @@ -0,0 +1,132 @@ +# Generated by Django 5.1.2 on 2024-10-23 11:53 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.utils.timezone +from django.db import migrations, 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", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=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" + ), + ), + ( + "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", django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/humitifier-server/src/main/migrations/__init__.py b/humitifier-server/src/main/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/main/models.py b/humitifier-server/src/main/models.py new file mode 100644 index 0000000..c8e51ca --- /dev/null +++ b/humitifier-server/src/main/models.py @@ -0,0 +1,7 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models + +# Create your models here. + +class User(AbstractUser): + pass \ No newline at end of file diff --git a/humitifier-server/src/main/static/main/css/tailwind.css b/humitifier-server/src/main/static/main/css/tailwind.css new file mode 100644 index 0000000..920bc4c --- /dev/null +++ b/humitifier-server/src/main/static/main/css/tailwind.css @@ -0,0 +1,1874 @@ +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* +! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e4e4e7; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #a1a1aa; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #a1a1aa; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} + +.bg { + --tw-bg-opacity: 1; + background-color: rgb(250 250 250 / var(--tw-bg-opacity)); +} + +.bg:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(24 24 27 / var(--tw-bg-opacity)); +} + +.h-full--header { + min-height: calc(100vh - 3.5rem); +} + +.section { + --tw-bg-opacity: 1; + background-color: rgb(250 250 250 / var(--tw-bg-opacity)); +} + +.section:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(24 24 27 / var(--tw-bg-opacity)); +} + +.section { + border-radius: 0.25rem; + padding: 1.75rem; + padding-top: 1.25rem; +} + +.text-green { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.input { + width: 100%; + border-radius: 0.25rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(228 228 231 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(250 250 250 / var(--tw-bg-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + --tw-text-opacity: 1; + color: rgb(63 63 70 / var(--tw-text-opacity)); +} + +.input::-moz-placeholder { + --tw-placeholder-opacity: 1; + color: rgb(161 161 170 / var(--tw-placeholder-opacity)); +} + +.input::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(161 161 170 / var(--tw-placeholder-opacity)); +} + +.input { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.input:focus-visible { + --tw-border-opacity: 1; + border-color: rgb(161 161 170 / var(--tw-border-opacity)); + outline: 2px solid transparent; + outline-offset: 2px; +} + +.input:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(63 63 70 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(24 24 27 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} + +.input:focus-visible:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(82 82 91 / var(--tw-border-opacity)); +} + +.btn { + border-radius: 0.25rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(255 205 0 / var(--tw-border-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-weight: 700; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-duration: 150ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +.btn-sm { + height: 36px; + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.btn-outline { + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(212 212 216 / var(--tw-border-opacity)); + --tw-text-opacity: 1; + color: rgb(63 63 70 / var(--tw-text-opacity)); +} + +.btn-outline:hover { + --tw-bg-opacity: 1; + background-color: rgb(250 250 250 / var(--tw-bg-opacity)); +} + +.btn-outline:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(63 63 70 / var(--tw-border-opacity)); + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} + +.btn-outline:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(63 63 70 / var(--tw-bg-opacity)); +} + +.text-blue { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.text-blue:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); +} + +.text-orange { + --tw-text-opacity: 1; + color: rgb(249 115 22 / var(--tw-text-opacity)); +} + +.text-red { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.visible { + visibility: visible; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.sticky { + position: sticky; +} + +.left-0 { + left: 0px; +} + +.top-0 { + top: 0px; +} + +.top-11 { + top: 2.75rem; +} + +.z-10 { + z-index: 10; +} + +.z-40 { + z-index: 40; +} + +.col-span-3 { + grid-column: span 3 / span 3; +} + +.col-span-2 { + grid-column: span 2 / span 2; +} + +.m-3 { + margin: 0.75rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.mb-0 { + margin-bottom: 0px; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-5 { + margin-bottom: 1.25rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.ml-auto { + margin-left: auto; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.list-item { + display: list-item; +} + +.hidden { + display: none; +} + +.size-5 { + width: 1.25rem; + height: 1.25rem; +} + +.size-6 { + width: 1.5rem; + height: 1.5rem; +} + +.h-14 { + height: 3.5rem; +} + +.h-2 { + height: 0.5rem; +} + +.h-full { + height: 100%; +} + +.h-screen { + height: 100vh; +} + +.max-h-\[50vh\] { + max-height: 50vh; +} + +.max-h-\[45vh\] { + max-height: 45vh; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-64 { + width: 16rem; +} + +.w-72 { + width: 18rem; +} + +.w-\[calc\(100vw-3\.5rem\)\] { + width: calc(100vw - 3.5rem); +} + +.w-full { + width: 100%; +} + +.w-1\/3 { + width: 33.333333%; +} + +.w-1\/2 { + width: 50%; +} + +.flex-shrink { + flex-shrink: 1; +} + +.flex-grow { + flex-grow: 1; +} + +.basis-full { + flex-basis: 100%; +} + +.table-auto { + table-layout: auto; +} + +.-translate-x-full { + --tw-translate-x: -100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.translate-x-0 { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.cursor-not-allowed { + cursor: not-allowed; +} + +.cursor-pointer { + cursor: pointer; +} + +.list-disc { + list-style-type: disc; +} + +.list-\[square\] { + list-style-type: square; +} + +.list-\[upper-roman\] { + list-style-type: upper-roman; +} + +.list-decimal { + list-style-type: decimal; +} + +.columns-4 { + -moz-columns: 4; + columns: 4; +} + +.columns-1 { + -moz-columns: 1; + columns: 1; +} + +.break-inside-avoid { + -moz-column-break-inside: avoid; + break-inside: avoid; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-cols-\[1fr_2fr_1fr\] { + grid-template-columns: 1fr 2fr 1fr; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-\[1fr_2fr\] { + grid-template-columns: 1fr 2fr; +} + +.grid-cols-\[1fr_1fr\] { + grid-template-columns: 1fr 1fr; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.items-stretch { + align-items: stretch; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-between { + justify-content: space-between; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-8 { + gap: 2rem; +} + +.gap-x-2 { + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} + +.gap-y-3 { + row-gap: 0.75rem; +} + +.gap-y-2 { + row-gap: 0.5rem; +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.overflow-y-auto { + overflow-y: auto; +} + +.text-nowrap { + text-wrap: nowrap; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-tl { + border-top-left-radius: 0.25rem; +} + +.rounded-tr { + border-top-right-radius: 0.25rem; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(228 228 231 / var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(212 212 216 / var(--tw-border-opacity)); +} + +.border-gray-700 { + --tw-border-opacity: 1; + border-color: rgb(63 63 70 / var(--tw-border-opacity)); +} + +.border-yellow-300 { + --tw-border-opacity: 1; + border-color: rgb(253 224 71 / var(--tw-border-opacity)); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + +.bg-blue-300 { + --tw-bg-opacity: 1; + background-color: rgb(147 197 253 / var(--tw-bg-opacity)); +} + +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(228 228 231 / var(--tw-bg-opacity)); +} + +.bg-neutral-800 { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity)); +} + +.bg-orange-300 { + --tw-bg-opacity: 1; + background-color: rgb(253 186 116 / var(--tw-bg-opacity)); +} + +.bg-primary { + --tw-bg-opacity: 1; + background-color: rgb(255 205 0 / var(--tw-bg-opacity)); +} + +.bg-red-300 { + --tw-bg-opacity: 1; + background-color: rgb(252 165 165 / var(--tw-bg-opacity)); +} + +.bg-red-700 { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} + +.bg-transparent { + background-color: transparent; +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); +} + +.bg-green-300 { + --tw-bg-opacity: 1; + background-color: rgb(134 239 172 / var(--tw-bg-opacity)); +} + +.bg-green-400 { + --tw-bg-opacity: 1; + background-color: rgb(74 222 128 / var(--tw-bg-opacity)); +} + +.bg-red-400 { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); +} + +.stroke-2 { + stroke-width: 2; +} + +.p-2 { + padding: 0.5rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-4 { + padding: 1rem; +} + +.p-5 { + padding: 1.25rem; +} + +.p-1 { + padding: 0.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.px-7 { + padding-left: 1.75rem; + padding-right: 1.75rem; +} + +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.py-0\.5 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} + +.pl-7 { + padding-left: 1.75rem; +} + +.pr-4 { + padding-right: 1rem; +} + +.pr-7 { + padding-right: 1.75rem; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pl-5 { + padding-left: 1.25rem; +} + +.pb-0 { + padding-bottom: 0px; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.align-bottom { + vertical-align: bottom; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-bold { + font-weight: 700; +} + +.font-semibold { + font-weight: 600; +} + +.italic { + font-style: italic; +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +.text-blue-300 { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(161 161 170 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(24 24 27 / var(--tw-text-opacity)); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.text-orange-500 { + --tw-text-opacity: 1; + color: rgb(249 115 22 / var(--tw-text-opacity)); +} + +.text-orange-600 { + --tw-text-opacity: 1; + color: rgb(234 88 12 / var(--tw-text-opacity)); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(113 113 122 / var(--tw-text-opacity)); +} + +.text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); +} + +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(212 212 216 / var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(39 39 42 / var(--tw-text-opacity)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(63 63 70 / var(--tw-text-opacity)); +} + +.underline { + text-decoration-line: underline; +} + +.decoration-dotted { + text-decoration-style: dotted; +} + +.placeholder-gray-700::-moz-placeholder { + --tw-placeholder-opacity: 1; + color: rgb(63 63 70 / var(--tw-placeholder-opacity)); +} + +.placeholder-gray-700::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(63 63 70 / var(--tw-placeholder-opacity)); +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-150 { + transition-duration: 150ms; +} + +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +html:not(.dark) .light\:btn-primary { + --tw-bg-opacity: 1; + background-color: rgb(255 205 0 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +html:not(.dark) .light\:btn-primary:hover { + --tw-bg-opacity: 1; + background-color: rgb(234 179 8 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +@media (min-width: 2000px) { + .ultrawide\:container { + width: 100%; + margin-right: auto; + margin-left: auto; + } + + @media (min-width: 640px) { + .ultrawide\:container { + max-width: 640px; + } + } + + @media (min-width: 768px) { + .ultrawide\:container { + max-width: 768px; + } + } + + @media (min-width: 1024px) { + .ultrawide\:container { + max-width: 1024px; + } + } + + @media (min-width: 1280px) { + .ultrawide\:container { + max-width: 1280px; + } + } + + @media (min-width: 1536px) { + .ultrawide\:container { + max-width: 1536px; + } + } + + @media (min-width: 2000px) { + .ultrawide\:container { + max-width: 2000px; + } + } +} + +.dark\:btn-outline:where(.dark, .dark *) { + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(212 212 216 / var(--tw-border-opacity)); + --tw-text-opacity: 1; + color: rgb(63 63 70 / var(--tw-text-opacity)); +} + +.dark\:btn-outline:where(.dark, .dark *):hover { + --tw-bg-opacity: 1; + background-color: rgb(250 250 250 / var(--tw-bg-opacity)); +} + +.dark\:btn-outline:where(.dark, .dark *):where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(63 63 70 / var(--tw-border-opacity)); + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} + +.dark\:btn-outline:where(.dark, .dark *):hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(63 63 70 / var(--tw-bg-opacity)); +} + +.last\:mb-0:last-child { + margin-bottom: 0px; +} + +.hover\:bg-neutral-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(245 245 245 / var(--tw-bg-opacity)); +} + +.hover\:bg-neutral-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity)); +} + +.focus-visible\:outline-none:focus-visible { + outline: 2px solid transparent; + outline-offset: 2px; +} + +@media (min-width: 768px) { + .md\:ml-72 { + margin-left: 18rem; + } + + .md\:ml-auto { + margin-left: auto; + } + + .md\:block { + display: block; + } + + .md\:hidden { + display: none; + } + + .md\:w-64 { + width: 16rem; + } + + .md\:translate-x-0 { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .md\:grid-cols-\[1fr_2fr\] { + grid-template-columns: 1fr 2fr; + } + + .md\:flex-row { + flex-direction: row; + } +} + +@media (min-width: 1024px) { + .lg\:table-cell { + display: table-cell; + } + + .lg\:w-40 { + width: 10rem; + } + + .lg\:columns-2 { + -moz-columns: 2; + columns: 2; + } + + .lg\:flex-row { + flex-direction: row; + } + + .lg\:items-center { + align-items: center; + } +} + +@media (min-width: 1280px) { + .xl\:ml-auto { + margin-left: auto; + } + + .xl\:table-cell { + display: table-cell; + } + + .xl\:w-40 { + width: 10rem; + } + + .xl\:columns-3 { + -moz-columns: 3; + columns: 3; + } + + .xl\:columns-2 { + -moz-columns: 2; + columns: 2; + } + + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .xl\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .xl\:flex-row { + flex-direction: row; + } + + .xl\:items-end { + align-items: flex-end; + } + + .xl\:items-center { + align-items: center; + } +} + +@media (min-width: 1536px) { + .\32xl\:table-cell { + display: table-cell; + } + + .\32xl\:columns-3 { + -moz-columns: 3; + columns: 3; + } + + .\32xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .\32xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (min-width: 2000px) { + .ultrawide\:mb-5 { + margin-bottom: 1.25rem; + } + + .ultrawide\:columns-4 { + -moz-columns: 4; + columns: 4; + } + + .ultrawide\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .ultrawide\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .ultrawide\:p-5 { + padding: 1.25rem; + } +} + +.dark\:block:where(.dark, .dark *) { + display: block; +} + +.dark\:hidden:where(.dark, .dark *) { + display: none; +} + +.dark\:border-gray-700:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(63 63 70 / var(--tw-border-opacity)); +} + +.dark\:bg-blue-300:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(147 197 253 / var(--tw-bg-opacity)); +} + +.dark\:bg-blue-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-700:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(63 63 70 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(39 39 42 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-900:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(24 24 27 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-950:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(9 9 11 / var(--tw-bg-opacity)); +} + +.dark\:bg-orange-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(154 52 18 / var(--tw-bg-opacity)); +} + +.dark\:bg-red-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} + +.dark\:bg-green-700:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(21 128 61 / var(--tw-bg-opacity)); +} + +.dark\:bg-red-700:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} + +.dark\:text-blue-300:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); +} + +.dark\:text-gray-50:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} + +.dark\:text-orange-500:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(249 115 22 / var(--tw-text-opacity)); +} + +.dark\:text-white:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.dark\:text-gray-400:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(161 161 170 / var(--tw-text-opacity)); +} + +.dark\:placeholder-gray-50:where(.dark, .dark *)::-moz-placeholder { + --tw-placeholder-opacity: 1; + color: rgb(250 250 250 / var(--tw-placeholder-opacity)); +} + +.dark\:placeholder-gray-50:where(.dark, .dark *)::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(250 250 250 / var(--tw-placeholder-opacity)); +} + +.dark\:hover\:bg-gray-800:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(39 39 42 / var(--tw-bg-opacity)); +} \ No newline at end of file diff --git a/humitifier-server/src/main/static/main/css/tailwind.input.css b/humitifier-server/src/main/static/main/css/tailwind.input.css new file mode 100644 index 0000000..73cdd63 --- /dev/null +++ b/humitifier-server/src/main/static/main/css/tailwind.input.css @@ -0,0 +1,58 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + .bg { + @apply bg-gray-50 dark:bg-gray-900; + } + + .h-full--header + { + @apply min-h-[calc(100vh-3.5rem)]; + } + + .section { + @apply bg p-7 pt-5 rounded; + } + + .text-green { + @apply text-green-600; + } + + .input { + @apply w-full px-3 py-2 bg-gray-50 dark:bg-gray-900 text-sm text-gray-700 dark:text-gray-50 placeholder-gray-400 border border-gray-200 dark:border-gray-700 rounded focus-visible:outline-none focus-visible:border-gray-400 dark:focus-visible:border-gray-600 transition duration-150 ease-in-out; + } + + .btn { + @apply px-3 py-2 font-bold rounded transition duration-150 ease-in-out border border-primary; + } + + .btn-sm { + @apply px-3 py-2 text-sm h-[36px]; + } + + .btn-outline { + @apply border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-50 hover:bg-gray-50 dark:hover:bg-gray-700; + } + + .btn-secondary { + @apply bg-black text-white hover:bg-gray-800; + } + + .btn-primary { + @apply bg-primary text-black hover:bg-yellow-500 hover:text-black; + } + + .text-blue { + @apply text-blue-600 dark:text-blue-300 + } + + .text-orange { + @apply text-orange-500; + } + + .text-red { + @apply text-red-500; + } +} \ No newline at end of file diff --git a/humitifier-server/src/main/static/main/img/cm_hs_avatar_corporate.png b/humitifier-server/src/main/static/main/img/cm_hs_avatar_corporate.png new file mode 100644 index 0000000000000000000000000000000000000000..d1a6c63b34cd8c1bc3c1630e5174ec345bfd4436 GIT binary patch literal 99366 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

4g9ouVe-@d)8PG?}pr6AB~ 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

0&^{};_Ilu6gNrpL2iu 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 literal 0 HcmV?d00001 diff --git a/static/img/favicon.ico b/humitifier-server/src/main/static/main/img/favicon.ico similarity index 100% rename from static/img/favicon.ico rename to humitifier-server/src/main/static/main/img/favicon.ico diff --git a/static/img/uu-black.png b/humitifier-server/src/main/static/main/img/uu-black.png similarity index 100% rename from static/img/uu-black.png rename to humitifier-server/src/main/static/main/img/uu-black.png diff --git a/static/img/uu-rgb-black.png b/humitifier-server/src/main/static/main/img/uu-rgb-black.png similarity index 100% rename from static/img/uu-rgb-black.png rename to humitifier-server/src/main/static/main/img/uu-rgb-black.png diff --git a/static/img/uu-rgb-white.png b/humitifier-server/src/main/static/main/img/uu-rgb-white.png similarity index 100% rename from static/img/uu-rgb-white.png rename to humitifier-server/src/main/static/main/img/uu-rgb-white.png diff --git a/static/img/uu-white.png b/humitifier-server/src/main/static/main/img/uu-white.png similarity index 100% rename from static/img/uu-white.png rename to humitifier-server/src/main/static/main/img/uu-white.png diff --git a/humitifier-server/src/main/static/main/js/alpine.min.js b/humitifier-server/src/main/static/main/js/alpine.min.js new file mode 100644 index 0000000..cddf462 --- /dev/null +++ b/humitifier-server/src/main/static/main/js/alpine.min.js @@ -0,0 +1,6 @@ +(()=>{var nt=!1,it=!1,W=[],ot=-1;function Ut(e){Rn(e)}function Rn(e){W.includes(e)||W.push(e),Mn()}function Wt(e){let t=W.indexOf(e);t!==-1&&t>ot&&W.splice(t,1)}function Mn(){!it&&!nt&&(nt=!0,queueMicrotask(Nn))}function Nn(){nt=!1,it=!0;for(let e=0;ee.effect(t,{scheduler:r=>{st?Ut(r):r()}}),at=e.raw}function ct(e){N=e}function Yt(e){let t=()=>{};return[n=>{let i=N(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}function Se(e,t){let r=!0,n,i=N(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>$(i)}var Xt=[],Zt=[],Qt=[];function er(e){Qt.push(e)}function te(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Zt.push(t))}function Ae(e){Xt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function lt(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function tr(e){for(e._x_effects?.forEach(Wt);e._x_cleanups?.length;)e._x_cleanups.pop()()}var ut=new MutationObserver(mt),ft=!1;function ue(){ut.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ft=!0}function dt(){Dn(),ut.disconnect(),ft=!1}var le=[];function Dn(){let e=ut.takeRecords();le.push(()=>e.length>0&&mt(e));let t=le.length;queueMicrotask(()=>{if(le.length===t)for(;le.length>0;)le.shift()()})}function m(e){if(!ft)return e();dt();let t=e();return ue(),t}var pt=!1,ve=[];function rr(){pt=!0}function nr(){pt=!1,mt(ve),ve=[]}function mt(e){if(pt){ve=ve.concat(e);return}let t=new Set,r=new Set,n=new Map,i=new Map;for(let o=0;os.nodeType===1&&t.add(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.add(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{lt(s,o)}),n.forEach((o,s)=>{Xt.forEach(a=>a(s,o))});for(let o of r)t.has(o)||Zt.forEach(s=>s(o));t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.has(o)||o.isConnected&&(delete o._x_ignoreSelf,delete o._x_ignore,Qt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function Ce(e){return B(F(e))}function D(e,t,r){return e._x_dataStack=[t,...F(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function F(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?F(e.host):e.parentNode?F(e.parentNode):[]}function B(e){return new Proxy({objects:e},Pn)}var Pn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?In:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(n,r)||!0:Reflect.set(i,t,r)}};function In(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>kn(n,i),s=>ht(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function kn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function ht(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),ht(e[t[0]],t.slice(1),r)}}var ir={};function y(e,t){ir[e]=t}function fe(e,t){let r=Ln(t);return Object.entries(ir).forEach(([n,i])=>{Object.defineProperty(e,`$${n}`,{get(){return i(t,r)},enumerable:!1})}),e}function Ln(e){let[t,r]=_t(e),n={interceptor:Re,...t};return te(e,r),n}function or(e,t,r,...n){try{return r(...n)}catch(i){re(i,e,t)}}function re(e,t,r=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} + +${r?'Expression: "'+r+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function De(e){let t=Me;Me=!1;let r=e();return Me=t,r}function R(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return sr(...e)}var sr=xt;function ar(e){sr=e}function xt(e,t){let r={};fe(r,e);let n=[r,...F(e)],i=typeof t=="function"?$n(n,t):Fn(n,t,e);return or.bind(null,e,t,i)}function $n(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(B([n,...e]),i);Ne(r,o)}}var gt={};function jn(e,t){if(gt[e])return gt[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return re(s,t,e),Promise.resolve()}})();return gt[e]=o,o}function Fn(e,t,r){let n=jn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=B([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>re(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>re(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>re(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var wt="x-";function C(e=""){return wt+e}function cr(e){wt=e}var Pe={};function d(e,t){return Pe[e]=t,{before(r){if(!Pe[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=G.indexOf(r);G.splice(n>=0?n:G.indexOf("DEFAULT"),0,e)}}}function lr(e){return Object.keys(Pe).includes(e)}function pe(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=Et(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(dr((o,s)=>n[o]=s)).filter(mr).map(zn(n,r)).sort(Kn).map(o=>Bn(e,o))}function Et(e){return Array.from(e).map(dr()).filter(t=>!mr(t))}var yt=!1,de=new Map,ur=Symbol();function fr(e){yt=!0;let t=Symbol();ur=t,de.set(t,[]);let r=()=>{for(;de.get(t).length;)de.get(t).shift()();de.delete(t)},n=()=>{yt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Yt(e);return t.push(i),[{Alpine:z,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:R.bind(R,e)},()=>t.forEach(a=>a())]}function Bn(e,t){let r=()=>{},n=Pe[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),yt?de.get(ur).push(n):n())};return s.runCleanups=o,s}var Ie=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),ke=e=>e;function dr(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=pr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var pr=[];function ne(e){pr.push(e)}function mr({name:e}){return hr().test(e)}var hr=()=>new RegExp(`^${wt}([^:^.]+)\\b`);function zn(e,t){return({name:r,value:n})=>{let i=r.match(hr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var bt="DEFAULT",G=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",bt,"teleport"];function Kn(e,t){let r=G.indexOf(e.type)===-1?bt:e.type,n=G.indexOf(t.type)===-1?bt:t.type;return G.indexOf(r)-G.indexOf(n)}function J(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function P(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>P(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)P(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var _r=!1;function gr(){_r&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),_r=!0,document.body||E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + + {% if debug %} + + {% endif %} + {% block head %}{% endblock %} + + + + +{% include 'base/page_parts/sidebar.html' %} + +

+ {% include 'base/page_parts/header.html' %} + + {% block content %} + {% endblock %} + +
+ + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/base/forms/field_template.html b/humitifier-server/src/main/templates/base/forms/field_template.html new file mode 100644 index 0000000..7c27fb9 --- /dev/null +++ b/humitifier-server/src/main/templates/base/forms/field_template.html @@ -0,0 +1,8 @@ +
+ {% if field.label %} + + {% endif %} +
+ {{ field }} +
+
diff --git a/humitifier-server/src/main/templates/base/forms/form_template.html b/humitifier-server/src/main/templates/base/forms/form_template.html new file mode 100644 index 0000000..fb740fc --- /dev/null +++ b/humitifier-server/src/main/templates/base/forms/form_template.html @@ -0,0 +1,14 @@ +{{ errors }} +{% if errors and not fields %} +
{% for field in hidden_fields %}{{ field }}{% endfor %}
+{% endif %} +{% for field, errors in fields %} + {{ field.as_field_group }} + + {% if forloop.last %} + {% for field in hidden_fields %}{{ field }}{% endfor %} + {% endif %} +{% endfor %} +{% if not fields and not errors %} + {% for field in hidden_fields %}{{ field }}{% endfor %} +{% endif %} diff --git a/humitifier-server/src/main/templates/base/page_parts/filters_form_template.html b/humitifier-server/src/main/templates/base/page_parts/filters_form_template.html new file mode 100644 index 0000000..8dcb55b --- /dev/null +++ b/humitifier-server/src/main/templates/base/page_parts/filters_form_template.html @@ -0,0 +1,16 @@ +{{ errors }} +{% if errors and not fields %} +
{% for field in hidden_fields %}{{ field }}{% endfor %}
+{% endif %} +{% for field, errors in fields %} +
+ {{ field }} +
+ + {% if forloop.last %} + {% for field in hidden_fields %}{{ field }}{% endfor %} + {% endif %} +{% endfor %} +{% if not fields and not errors %} + {% for field in hidden_fields %}{{ field }}{% endfor %} +{% endif %} diff --git a/humitifier-server/src/main/templates/base/page_parts/header.html b/humitifier-server/src/main/templates/base/page_parts/header.html new file mode 100644 index 0000000..0741e6c --- /dev/null +++ b/humitifier-server/src/main/templates/base/page_parts/header.html @@ -0,0 +1,36 @@ +
+ +
+ + + +
+ + +
+ +
+ +
+ +
+ + + +
+
+ +
+ Humitifier User +
+
\ No newline at end of file diff --git a/humitifier-server/src/main/templates/base/page_parts/paginator_bottom.html b/humitifier-server/src/main/templates/base/page_parts/paginator_bottom.html new file mode 100644 index 0000000..491d339 --- /dev/null +++ b/humitifier-server/src/main/templates/base/page_parts/paginator_bottom.html @@ -0,0 +1,32 @@ +{% load param_replace %} + +
\ No newline at end of file diff --git a/humitifier-server/src/main/templates/base/page_parts/paginator_top.html b/humitifier-server/src/main/templates/base/page_parts/paginator_top.html new file mode 100644 index 0000000..a8f8030 --- /dev/null +++ b/humitifier-server/src/main/templates/base/page_parts/paginator_top.html @@ -0,0 +1,63 @@ +{% load param_replace %} + +
+
+ {% if paginator_middle is None or paginator_middle is True %} + Total items: {{ paginator.count }} | + Page {{ page_obj.number }} of {{ paginator.num_pages }} + {% endif %} +
+ +
+
+ +
+
+ {{ filterset.form }} + + + +
+ + +
+
+
+
+ + {% if page_sizes %} + + {% endif %} + + {% if ordering_fields %} + + {% endif %} + + {% if is_paginated %} + +
{# spacer #}
+ + {% if page_obj.has_previous %} + Previous + {% else %} +
Previous
+ {% endif %} + {% if page_obj.has_next %} + Next + {% else %} +
Next
+ {% endif %} + {% endif %} +
+
\ No newline at end of file diff --git a/humitifier-server/src/main/templates/base/page_parts/sidebar.html b/humitifier-server/src/main/templates/base/page_parts/sidebar.html new file mode 100644 index 0000000..b22f736 --- /dev/null +++ b/humitifier-server/src/main/templates/base/page_parts/sidebar.html @@ -0,0 +1,80 @@ +{% load static %} +{% load humanize %} +{% load simple_menu %} + + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/django/forms/widgets/input.html b/humitifier-server/src/main/templates/django/forms/widgets/input.html new file mode 100644 index 0000000..8841f60 --- /dev/null +++ b/humitifier-server/src/main/templates/django/forms/widgets/input.html @@ -0,0 +1,9 @@ + diff --git a/humitifier-server/src/main/templates/django/forms/widgets/select.html b/humitifier-server/src/main/templates/django/forms/widgets/select.html new file mode 100644 index 0000000..01ddc15 --- /dev/null +++ b/humitifier-server/src/main/templates/django/forms/widgets/select.html @@ -0,0 +1,21 @@ + diff --git a/humitifier-server/src/main/templates/icons/admin.html b/humitifier-server/src/main/templates/icons/admin.html new file mode 100644 index 0000000..796c81d --- /dev/null +++ b/humitifier-server/src/main/templates/icons/admin.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/api.html b/humitifier-server/src/main/templates/icons/api.html new file mode 100644 index 0000000..0dee1fa --- /dev/null +++ b/humitifier-server/src/main/templates/icons/api.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/critical.html b/humitifier-server/src/main/templates/icons/critical.html new file mode 100644 index 0000000..afba505 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/critical.html @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/icons/dashboard.html b/humitifier-server/src/main/templates/icons/dashboard.html new file mode 100644 index 0000000..56dfe4c --- /dev/null +++ b/humitifier-server/src/main/templates/icons/dashboard.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/databases.html b/humitifier-server/src/main/templates/icons/databases.html new file mode 100644 index 0000000..9e5be9c --- /dev/null +++ b/humitifier-server/src/main/templates/icons/databases.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/host.html b/humitifier-server/src/main/templates/icons/host.html new file mode 100644 index 0000000..ccac9e5 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/host.html @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/icons/info.html b/humitifier-server/src/main/templates/icons/info.html new file mode 100644 index 0000000..cce59f3 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/info.html @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/icons/shield.html b/humitifier-server/src/main/templates/icons/shield.html new file mode 100644 index 0000000..1b86142 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/shield.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/tasks.html b/humitifier-server/src/main/templates/icons/tasks.html new file mode 100644 index 0000000..ace75e2 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/tasks.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/terminal.html b/humitifier-server/src/main/templates/icons/terminal.html new file mode 100644 index 0000000..539d858 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/terminal.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/users.html b/humitifier-server/src/main/templates/icons/users.html new file mode 100644 index 0000000..60f7c91 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/users.html @@ -0,0 +1,3 @@ + + + diff --git a/humitifier-server/src/main/templates/icons/warning.html b/humitifier-server/src/main/templates/icons/warning.html new file mode 100644 index 0000000..60f0032 --- /dev/null +++ b/humitifier-server/src/main/templates/icons/warning.html @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/humitifier-server/src/main/templates/main/dashboard.html b/humitifier-server/src/main/templates/main/dashboard.html new file mode 100644 index 0000000..c48652f --- /dev/null +++ b/humitifier-server/src/main/templates/main/dashboard.html @@ -0,0 +1,181 @@ +{% extends "base/base_page_template.html" %} +{% load strip_quotes %} + +{% load humanize %} +{% load param_replace %} + +{% block page_title %}Hosts | {{ block.super }}{% endblock %} + +{% block head %} + + +{% endblock %} + +{% block content %} +
+
+ +
+
+ +
+
+ +
+
+
+
+

+ Alerts +

+
+ {% include 'base/page_parts/paginator_top.html' %} +
+ + + + + + + + + + {% for alert in object_list %} + + + + + + {% endfor %} + {% if object_list.count == 0 %} + + + + {% endif %} + +
+ Hostname +
+ + {{ alert.host.fqdn }} + +
+ {% if layout.wild_wasteland %} + No hosts found, have you looked under the rug? + {% else %} + No hosts found + {% endif %} +
+ +
+ {% include 'base/page_parts/paginator_bottom.html' %} +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/humitifier-server/src/main/templates/main/not_implemented.html b/humitifier-server/src/main/templates/main/not_implemented.html new file mode 100644 index 0000000..37c1f6b --- /dev/null +++ b/humitifier-server/src/main/templates/main/not_implemented.html @@ -0,0 +1,10 @@ +{% extends 'base/base_page_template.html' %} + +{% block content %} +
+

Not implemented

+

+ This page is not implemented yet. 😔 +

+
+{% endblock %} \ No newline at end of file diff --git a/humitifier-server/src/main/templatetags/__init__.py b/humitifier-server/src/main/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/humitifier-server/src/main/templatetags/param_replace.py b/humitifier-server/src/main/templatetags/param_replace.py new file mode 100644 index 0000000..e768426 --- /dev/null +++ b/humitifier-server/src/main/templatetags/param_replace.py @@ -0,0 +1,32 @@ +from django import template + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def param_replace(context, **kwargs): + """ + Return encoded URL parameters that are the same as the current + request's parameters, only with the specified GET parameters added or changed. + + It also removes any empty parameters to keep things neat, + so you can remove a parm by setting it to ``""``. + + For example, if you're on the page ``/things/?with_frosting=true&page=5``, + then + + Page 3 + + would expand to + + Page 3 + + Based on + https://stackoverflow.com/questions/22734695/next-and-before-links-for-a-django-paginated-query/22735278#22735278 + """ + d = context['request'].GET.copy() + for k, v in kwargs.items(): + d[k] = v + for k in [k for k, v in d.items() if not v]: + del d[k] + return d.urlencode() \ No newline at end of file diff --git a/humitifier-server/src/main/templatetags/strip_quotes.py b/humitifier-server/src/main/templatetags/strip_quotes.py new file mode 100644 index 0000000..9ff66d8 --- /dev/null +++ b/humitifier-server/src/main/templatetags/strip_quotes.py @@ -0,0 +1,15 @@ +from django import template + +register = template.Library() + + +@register.filter() +def strip_quotes(value, **kwargs): + if not value: + return value + if value[0] == '"': + value = value[1:] + if value[-1] == '"': + value = value[:-1] + + return value \ No newline at end of file diff --git a/humitifier-server/src/main/tests.py b/humitifier-server/src/main/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/humitifier-server/src/main/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/humitifier-server/src/main/urls.py b/humitifier-server/src/main/urls.py new file mode 100644 index 0000000..66cb867 --- /dev/null +++ b/humitifier-server/src/main/urls.py @@ -0,0 +1,15 @@ +from django.urls import path + +from .views import AccessProfilesView, DashboardView, HomeRedirectView, \ + OAuthApplicationsView, \ + UsersView + +app_name = 'main' + +urlpatterns = [ + path("", HomeRedirectView.as_view(), name="home"), + path("dashboard/", DashboardView.as_view(), name="dashboard"), + path("users/", UsersView.as_view(), name="users"), + path("access-profiles/", AccessProfilesView.as_view(), name="access_profiles"), + path("oauth-applications/", OAuthApplicationsView.as_view(), name="oauth_applications"), +] diff --git a/humitifier-server/src/main/views.py b/humitifier-server/src/main/views.py new file mode 100644 index 0000000..f7c247b --- /dev/null +++ b/humitifier-server/src/main/views.py @@ -0,0 +1,136 @@ +from django.db.models import Count +from django.urls import reverse +from django.views.generic import ListView, RedirectView, TemplateView + +from hosts.filters import AlertFilters +from hosts.models import Alert, AlertType, Host + + +### +### Generic views +### + +class FilteredListView(ListView): + filterset_class = None + page_sizes = [10, 25, 50, 100, 1000] + ordering_fields = {} + + def get_queryset(self): + queryset = super().get_queryset() + + self.filterset = self.filterset_class(self.request.GET, queryset=queryset) + + return self.filterset.qs.distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context['filterset'] = self.filterset + context['page_sizes'] = self.page_sizes + context['ordering'] = self.get_ordering() + context['ordering_fields'] = self.get_ordering_fields() + + return context + + def get_paginate_by(self, queryset): + # Never paginate if we don't have a default page size + if self.paginate_by is None: + return None + + return self.request.GET.get('page_size', self.paginate_by) + + def get_ordering(self): + # Never order if we don't have ordering fields + if not self.ordering_fields: + return None + + ordering = self.request.GET.get('ordering') + if ordering and ordering.lstrip('-') in self.ordering_fields: + return ordering + + if self.ordering: + return self.ordering + + if self.model._meta.ordering: + return self.model._meta.ordering[0] + + return None + + def get_ordering_fields(self): + ordering_fields = {} + + for field, label in self.ordering_fields.items(): + ordering_fields[field] = f"{label} ↑" + ordering_fields[f"-{field}"] = f"{label} ↓" + + return ordering_fields + + +### +### Page views +### + +class HomeRedirectView(RedirectView): + + def get_redirect_url(self, *args, **kwargs): + # TODO: user preferences + return reverse('hosts:list') + + +class DashboardView(FilteredListView): + model = Alert + filterset_class = AlertFilters + paginate_by = 20 + template_name = 'main/dashboard.html' + ordering_fields = { + 'host': 'Hostname', + 'level': 'Alert level', + 'type': 'Alert type', + } + + def get_queryset(self): + queryset = Alert.objects.get_for_user(self.request.user) + + ordering = self.get_ordering() + if ordering: + if isinstance(ordering, str): + ordering = (ordering,) + queryset = queryset.order_by(*ordering) + + self.filterset = self.filterset_class(self.request.GET, queryset=queryset) + + filtered_qs = self.filterset.qs + # We're going to need the data for the alerts in the template + # So, let's prefetch it here for _performance_ + filtered_qs = filtered_qs.select_related('host') + + return filtered_qs.distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context['os_stats'] = Host.objects.get_for_user( + self.request.user + ).values( + 'os' + ).annotate( + count=Count('os') + ) + context['department_stats'] = Host.objects.get_for_user( + self.request.user + ).values( + 'department' + ).annotate( + count=Count('department') + ) + + return context + +class UsersView(TemplateView): + template_name = 'main/not_implemented.html' + +class AccessProfilesView(TemplateView): + template_name = 'main/not_implemented.html' + +class OAuthApplicationsView(TemplateView): + template_name = 'main/not_implemented.html' \ No newline at end of file diff --git a/humitifier-server/src/manage.py b/humitifier-server/src/manage.py new file mode 100755 index 0000000..5125490 --- /dev/null +++ b/humitifier-server/src/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "humitifier_server.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + 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?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/humitifier-server/tailwind.config.js b/humitifier-server/tailwind.config.js new file mode 100644 index 0000000..872ebca --- /dev/null +++ b/humitifier-server/tailwind.config.js @@ -0,0 +1,41 @@ +const colors = require('tailwindcss/colors') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/*/templates/**/*.html"], + darkMode: ['selector'], + theme: { + container: { + center: true, + }, + colors: { + transparent: 'transparent', + current: 'currentColor', + black: colors.black, + white: colors.white, + gray: colors.zinc, + blue: colors.blue, + red: colors.red, + green: colors.green, + yellow: colors.yellow, + orange: colors.orange, + neutral: colors.neutral, + primary: '#FFCD00' + }, + extend: { + screens: { + 'ultrawide': '2000px', + } + } + // extend: { + // colors: { + // 'primary': '#FFCD00', + // } + // }, + }, + plugins: [ + function ({addVariant}) { + addVariant('light', 'html:not(.dark) &') + }, + ], +} \ No newline at end of file diff --git a/humitifier-server/yarn.lock b/humitifier-server/yarn.lock new file mode 100644 index 0000000..8b0c11c --- /dev/null +++ b/humitifier-server/yarn.lock @@ -0,0 +1,736 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^1.21.0: + version "1.21.6" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== + +lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-nested@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.23: + version "8.4.47" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" + integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.0" + source-map-js "^1.2.1" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +resolve@^1.1.7, resolve@^1.22.2: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +sucrase@^3.32.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tailwindcss@^3.4.14: + version "3.4.14" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.14.tgz#6dd23a7f54ec197b19159e91e3bb1e55e7aa73ac" + integrity sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.0" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +yaml@^2.3.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== diff --git a/static/prism.css b/static/prism.css deleted file mode 100644 index e723289..0000000 --- a/static/prism.css +++ /dev/null @@ -1,3 +0,0 @@ -/* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism&languages=json+json5 */ -code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} diff --git a/static/prism.js b/static/prism.js deleted file mode 100644 index 357cb6a..0000000 --- a/static/prism.js +++ /dev/null @@ -1,5 +0,0 @@ -/* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism&languages=json+json5 */ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); -Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json; -!function(n){var e=/("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/;n.languages.json5=n.languages.extend("json",{property:[{pattern:RegExp(e.source+"(?=\\s*:)"),greedy:!0},{pattern:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/,alias:"unquoted"}],string:{pattern:e,greedy:!0},number:/[+-]?\b(?:NaN|Infinity|0x[a-fA-F\d]+)\b|[+-]?(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+\b)?/})}(Prism); diff --git a/web/core/_inputs.scss b/web/core/_inputs.scss deleted file mode 100644 index 37a52d0..0000000 --- a/web/core/_inputs.scss +++ /dev/null @@ -1,25 +0,0 @@ -@use "../utils/colors"; - -$input-height: 40px; - -@mixin input-core { - height: $input-height; - border: 1px solid colors.$yellow; - border-radius: 0.1rem; - - padding: 0; - &:focus { - outline: colors.$yellow solid 2px; - } -} - -input[type=text] { - @include input-core; - -} - -select { - @include input-core; - background-color: colors.$white; -} - diff --git a/web/core/_page_header.scss b/web/core/_page_header.scss deleted file mode 100644 index a351ad1..0000000 --- a/web/core/_page_header.scss +++ /dev/null @@ -1,15 +0,0 @@ -#page-header { - display: grid; - grid-template-columns: 1fr 1fr 2fr; - img { - height: 150px; - } - - h1 { - font-size: 2em; - em { - font-weight: lighter; - } - } - -} \ No newline at end of file diff --git a/web/core/_table.scss b/web/core/_table.scss deleted file mode 100644 index 9fda3b0..0000000 --- a/web/core/_table.scss +++ /dev/null @@ -1,49 +0,0 @@ -@use "../utils/colors"; - -table { - border-collapse: collapse; - width: 100%; - -} - -td { - border-bottom: 1px solid colors.$black; - margin: 2px; - height: 40px; - - &.center { - text-align: center; - } - - &.vtop { - vertical-align: top; - padding-top: 1em; - - } -} - - -.host-details { - width: 100%; - margin: 1em; - - td { - border: none; - - } - - ul { - padding: 0; - } - - li { - list-style: none; - margin: 0.5em; - - .badge { - padding: 0.2em; - margin-right: 0.5em; - - } - } -} \ No newline at end of file diff --git a/web/core/_typography.scss b/web/core/_typography.scss deleted file mode 100644 index 47a4f67..0000000 --- a/web/core/_typography.scss +++ /dev/null @@ -1,67 +0,0 @@ -@use "../utils/fonts" as fonts; -@use "../utils/colors" as colors; -@use "../utils/animations" as animations; - -$main: fonts.$opensans; -$heading: fonts.$zilla; -$subheading: fonts.$merriweather; - -body { - font-family: $main; -} - -h1, h3, h5 { - font-family: $heading; -} - -h2, h4, h6 { - font-family: $subheading; -} - -// a { -// text-decoration: none; -// @include animations.onhover-left-to-right(colors.$yellow, colors.$black); -// color: colors.$black; -// padding-left: 0.5em; -// padding-right: 0.5em; -// border-bottom: 1px solid colors.$black; -// &:hover { -// color : colors.$yellow; -// } -// } - -.badge { - border: 2px solid; - padding: 0.5em; - border-radius: 0.5em; - font-size: 0.8em; - font-weight: bold; - text-transform: uppercase; - - &.ok { - border-color: colors.$green; - color: colors.$green; - } - - &.info{ - border-color: colors.$blue; - color: colors.$blue; - } - - &.warning { - border-color: colors.$yellow; - color: colors.$yellow; - } - - &.critical { - border-color: colors.$red; - color: colors.$red; - } - - &.small { - display: inline-block; - font-size: 0.6em; - padding: 0.1em; - border: 1px solid; - } -} \ No newline at end of file diff --git a/web/core/main.scss b/web/core/main.scss deleted file mode 100644 index c287209..0000000 --- a/web/core/main.scss +++ /dev/null @@ -1,104 +0,0 @@ -@use "table"; -@use "typography"; -@use "inputs"; -@use "page_header"; -@use "../utils/colors" as colors; - -body { - max-width: 80%; - margin: 0 auto; - display: grid; - grid-template-columns: 1fr 3fr; - // grid-template-rows: 100px auto; - grid-gap: 1rem; - grid-template-areas: "header host-summary" "host-filters host-table"; - - header { - grid-area: header; - } -} - - - // display: grid; - // grid-template-columns: 1fr 3fr; - // grid-template-rows: 200px auto; - // grid-gap: 1rem; - // grid-template-areas: "host-filters host-summary" "host-filters host-table"; - -#hosts-summary { - grid-area: host-summary; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - - article { - width: 200px; - text-align: center; - border: 2px solid; - border-radius: 0.5em; - margin: 2em; - padding: 1em; - - &.ok { - border-color: colors.$green; - color: colors.$green; - } - - &.info{ - border-color: colors.$blue; - color: colors.$blue; - } - - &.warning { - border-color: colors.$yellow; - color: colors.$yellow; - } - - &.critical { - border-color: colors.$red; - color: colors.$red; - } - - strong { - display: block; - font-size: 2em; - font-weight: bold; - } - - a { - color: inherit; - text-decoration: none; - cursor: pointer; - } - } -} - -#host-filters { - grid-area: host-filters; - - form { - padding: 2em; - margin: 2em; - display: flex; - gap: 1em; - flex-direction: column; - justify-content: center; - border: 2px solid; - border-color: colors.$yellow; - border-radius: 0.5em; - - button { - width: { - min: 100px; - max: 200px; - } - } - } - -} - -#hosts-table { - grid-area: host-table; - -} diff --git a/web/style.scss b/web/style.scss deleted file mode 100644 index 20185fc..0000000 --- a/web/style.scss +++ /dev/null @@ -1,2 +0,0 @@ -@use "core/main" as *; -@use "utils/main" as *; \ No newline at end of file diff --git a/web/utils/_animations.scss b/web/utils/_animations.scss deleted file mode 100644 index b5c87af..0000000 --- a/web/utils/_animations.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin onhover-left-to-right($c1, $c2) { - background: linear-gradient(to left, $c1 50%, $c2 50%) right; - transition: .2s ease-out; - background-size: 200% 100%; - &:hover { - background-position: left; - } -} \ No newline at end of file diff --git a/web/utils/_box.scss b/web/utils/_box.scss deleted file mode 100644 index aee39cf..0000000 --- a/web/utils/_box.scss +++ /dev/null @@ -1,8 +0,0 @@ -@use "../utils/colors" as colors; - -@mixin itembox { - background-color: adjust-color(colors.$yellow, $lightness: 30%); - border-radius: 0.2rem; - padding: 1rem; - box-shadow: 0 0 1rem adjust-color(colors.$black, $lightness: 90%); -} \ No newline at end of file diff --git a/web/utils/_button.scss b/web/utils/_button.scss deleted file mode 100644 index 8c70606..0000000 --- a/web/utils/_button.scss +++ /dev/null @@ -1,19 +0,0 @@ -@use "../utils/colors" as colors; -@use "../core/typography" as typography; - -button { - background-color: colors.$white; - color: colors.$black; - border: 2px solid colors.$yellow; - border-radius: 0.5em; - padding: 0.5rem 1.5rem; - font-family: typography.$heading; - font-weight: bolder; - text-transform: uppercase; - cursor: pointer; - - &:hover { - background-color: colors.$yellow; - } -} - diff --git a/web/utils/_colors.scss b/web/utils/_colors.scss deleted file mode 100644 index d5e960a..0000000 --- a/web/utils/_colors.scss +++ /dev/null @@ -1,14 +0,0 @@ -$yellow: #FFCD00; -$red: #C00A35; -$cream: #FFE6AB; -$orange: #F3965E; -$burgundy: #AA1555; -$brown: #6E3B23; -$green: #24A793; -$blue: #5287C6; -$dark-blue: #001240; -$purple: #5B2182; -$white: #FFFFFF; -$black: #000000; - -// $light-black: adjust-color($black, $lightness: 90%); \ No newline at end of file diff --git a/web/utils/_fonts.scss b/web/utils/_fonts.scss deleted file mode 100644 index fab1562..0000000 --- a/web/utils/_fonts.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Merriweather&family=Open+Sans:wght@300;400&family=Zilla+Slab:wght@300;400&display=swap'); - -$merriweather: 'Merriweather', serif; -$opensans: 'Open Sans', sans-serif; -$zilla: 'Zilla Slab', serif; diff --git a/web/utils/_lists.scss b/web/utils/_lists.scss deleted file mode 100644 index 3141e43..0000000 --- a/web/utils/_lists.scss +++ /dev/null @@ -1,35 +0,0 @@ -@use "../core/typography" as typography; -@use "colors"; - -@mixin block-list { - ul { - list-style: none; - padding: 0; - margin: 0; - font-size: 0.8em; - font-style: italic; - font-family: typography.$main; - margin-left: 0.2em; - } -} - -ul.inline-list { - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: flex-start; - align-items: left; - align-content: top; - - li { - margin: 0.2em; - background-color: colors.$yellow; - color: colors.$black; - border: 0.1em solid colors.$black; - padding: 0.2em 0.5em; - } - -} \ No newline at end of file diff --git a/web/utils/_progress.scss b/web/utils/_progress.scss deleted file mode 100644 index 199f81d..0000000 --- a/web/utils/_progress.scss +++ /dev/null @@ -1,32 +0,0 @@ -@use "colors"; -@use "../core/typography"; - -.progress { - height: 1.5em; - background-color: colors.$cream; - position: relative; - border-radius: 0.1rem; - padding: 3px; - max-width: 95%; - max-height: 95%; - } - .progress:before { - content: attr(data-label); - font-size: 0.9em; - position: absolute; - text-align: center; - top: 5px; - left: 0; - right: 0; - font-weight: bolder; - font-family: typography.$heading; - font-style: normal; - text-transform: uppercase; - color: colors.$black; - } - .progress .value { - background-color: colors.$yellow; - display: inline-block; - height: 100%; - } - diff --git a/web/utils/_table.scss b/web/utils/_table.scss deleted file mode 100644 index a81761e..0000000 --- a/web/utils/_table.scss +++ /dev/null @@ -1,23 +0,0 @@ -@use "colors"; -@use "../core/typography" as t; - -tr.kv-table-row { - color: colors.$black; - - border-bottom: 2px solid colors.$black; - - td { - border: none; - } - td:nth-child(odd) { - - font-family: t.$heading; - text-transform: uppercase; - font-weight: bolder; - } - - td:nth-child(even) { - font-family: t.$subheading; - } - -} \ No newline at end of file diff --git a/web/utils/main.scss b/web/utils/main.scss deleted file mode 100644 index 086a983..0000000 --- a/web/utils/main.scss +++ /dev/null @@ -1,4 +0,0 @@ -@use "lists"; -@use "table"; -@use "progress"; -@use "button"; \ No newline at end of file From b9a2c6e52c4ca94484eadd72869e021e15778cb9 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 12:19:13 +0100 Subject: [PATCH 05/20] Ignore node mudules pls --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 97b612e..ec6c1ec 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ docker-compose.yaml .local/ .envrc .idea +node_modules/ # Supabase .branches From 1f88f44637bd96fc1579b75102f049d422dd8180 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:15:31 +0100 Subject: [PATCH 06/20] feat: server docker build --- .github/workflows/build-server.yaml | 47 ++++++++++ humitifier-server/Dockerfile | 27 ++++++ humitifier-server/docker/entrypoint.sh | 10 +++ humitifier-server/docker/gunicorn.conf.py | 7 ++ humitifier-server/poetry.lock | 45 ++++++++-- humitifier-server/pyproject.toml | 6 +- .../src/humitifier_server/env.py | 6 ++ .../src/humitifier_server/settings.py | 86 ++++++++++++++++--- 8 files changed, 216 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/build-server.yaml create mode 100644 humitifier-server/Dockerfile create mode 100644 humitifier-server/docker/entrypoint.sh create mode 100644 humitifier-server/docker/gunicorn.conf.py create mode 100644 humitifier-server/src/humitifier_server/env.py diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml new file mode 100644 index 0000000..5110c13 --- /dev/null +++ b/.github/workflows/build-server.yaml @@ -0,0 +1,47 @@ +name: Build and Push Docker Images + +on: + push: + tags: + - '*' + workflow_dispatch: + +env: + IMAGE_NAME: humitifier-server + DOCKERFILE_PATH: ./humitifier-server/Dockerfile + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push main image + uses: docker/build-push-action@v4 + with: + context: . + file: ${{ env.DOCKERFILE_PATH }} + push: true + tags: | +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest + ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} + + - name: Grype Scan + id: scan + uses: anchore/scan-action@v3 + with: + image: ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} + fail-build: false + + - name: upload Grype SARIF report + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} diff --git a/humitifier-server/Dockerfile b/humitifier-server/Dockerfile new file mode 100644 index 0000000..55b6700 --- /dev/null +++ b/humitifier-server/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.13.0-alpine3.20 AS builder + +ENV PYTHONUNBUFFERED=1 + +RUN pip install poetry && poetry config virtualenvs.in-project true +RUN apk add postgresql-dev gcc musl-dev libffi-dev + +WORKDIR /app + +COPY pyproject.toml poetry.lock ./ +COPY src/ ./src + +RUN poetry install --without dev --with=production -vvv + +FROM python:3.13.0-alpine3.20 + +WORKDIR /app + +RUN apk add postgresql-libs + +COPY --from=builder /app . +COPY docker/gunicorn.conf.py ./ +COPY docker/entrypoint.sh ./ + +RUN .venv/bin/python src/manage.py collectstatic --noinput + +CMD ["sh", "entrypoint.sh"] diff --git a/humitifier-server/docker/entrypoint.sh b/humitifier-server/docker/entrypoint.sh new file mode 100644 index 0000000..2d263bc --- /dev/null +++ b/humitifier-server/docker/entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Activate our venv +source /app/.venv/bin/activate + +# Migrate the database +python src/manage.py migrate + +# Run da server +gunicorn humitifier_server.wsgi:application -c gunicorn.conf.py "$@" \ No newline at end of file diff --git a/humitifier-server/docker/gunicorn.conf.py b/humitifier-server/docker/gunicorn.conf.py new file mode 100644 index 0000000..05c6ebd --- /dev/null +++ b/humitifier-server/docker/gunicorn.conf.py @@ -0,0 +1,7 @@ +import os + +bind = "0.0.0.0:8000" +workers = 3 +capture_output = True +# How verbose the Gunicorn error logs should be +loglevel = os.getenv("LOG_LEVEL", "WARNING") \ No newline at end of file diff --git a/humitifier-server/poetry.lock b/humitifier-server/poetry.lock index 2c465b4..cb94eb4 100644 --- a/humitifier-server/poetry.lock +++ b/humitifier-server/poetry.lock @@ -413,13 +413,13 @@ django = ">=4.2" [[package]] name = "faker" -version = "30.8.0" +version = "30.8.1" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-30.8.0-py3-none-any.whl", hash = "sha256:4cd0c5ea4bc1e4c902967f6e662f5f5da69f1674d9a94f54e516d27f3c2a6a16"}, - {file = "faker-30.8.0.tar.gz", hash = "sha256:3608c7fcac2acde0eaa6da28dae97628f18f14d54eaa2a92b96ae006f1621bd7"}, + {file = "Faker-30.8.1-py3-none-any.whl", hash = "sha256:4f7f133560b9d4d2a915581f4ba86f9a6a83421b89e911f36c4c96cff58135a5"}, + {file = "faker-30.8.1.tar.gz", hash = "sha256:93e8b70813f76d05d98951154681180cb795cfbcff3eced7680d963bcc0da2a9"}, ] [package.dependencies] @@ -512,6 +512,27 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "gunicorn" +version = "23.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "idna" version = "3.10" @@ -608,7 +629,7 @@ type = ["mypy (>=1.11.2)"] name = "psycopg2" version = "2.9.10" description = "psycopg2 - Python-PostgreSQL Database Adapter" -optional = true +optional = false python-versions = ">=3.8" files = [ {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, @@ -834,7 +855,21 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "whitenoise" +version = "6.8.2" +description = "Radically simplified static file serving for WSGI applications" +optional = false +python-versions = ">=3.9" +files = [ + {file = "whitenoise-6.8.2-py3-none-any.whl", hash = "sha256:df12dce147a043d1956d81d288c6f0044147c6d2ab9726e5772ac50fb45d2280"}, + {file = "whitenoise-6.8.2.tar.gz", hash = "sha256:486bd7267a375fa9650b136daaec156ac572971acc8bf99add90817a530dd1d4"}, +] + +[package.extras] +brotli = ["brotli"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "add815003ea81e8541d45420ec291a8e9a4d946acb4013db84acb90c897971e3" +content-hash = "47c8c54a6d5ace32fb3719706a80127eaaa42914338ff460231a051701ae2655" diff --git a/humitifier-server/pyproject.toml b/humitifier-server/pyproject.toml index 922188e..1496223 100644 --- a/humitifier-server/pyproject.toml +++ b/humitifier-server/pyproject.toml @@ -14,6 +14,7 @@ django-filter = "^24.3" markdown = "^3.7" django-debug-toolbar = "^4.4.6" django-simple-menu = "^2.1.3" +whitenoise = "^6.8.2" [tool.poetry.group.dev.dependencies] @@ -21,9 +22,12 @@ black = "^24.10.0" psycopg2-binary = {version = "^2.9.10", optional = true} faker = "^30.8.0" +[tool.poetry.group.production] +optional = true [tool.poetry.group.production.dependencies] -psycopg2 = {version = "^2.9.10", optional = true} +psycopg2 = "^2.9.10" +gunicorn = "^23.0.0" [build-system] requires = ["poetry-core"] diff --git a/humitifier-server/src/humitifier_server/env.py b/humitifier-server/src/humitifier_server/env.py new file mode 100644 index 0000000..7c101be --- /dev/null +++ b/humitifier-server/src/humitifier_server/env.py @@ -0,0 +1,6 @@ +from os import environ + +get = environ.get + +def get_boolean(key: str, default) -> bool: + return get(key, default=str(default)).lower() in ("true", "1", "yes", 't') \ No newline at end of file diff --git a/humitifier-server/src/humitifier_server/settings.py b/humitifier-server/src/humitifier_server/settings.py index df1a40a..f0c2dac 100644 --- a/humitifier-server/src/humitifier_server/settings.py +++ b/humitifier-server/src/humitifier_server/settings.py @@ -11,6 +11,7 @@ """ from pathlib import Path +from . import env # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -20,17 +21,20 @@ # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-ffdjavjsp%s%b069$aai#h7odtbd#!q8uu7=hn1tv&y$gdq17_" +SECRET_KEY = env.get( + "DJANGO_SECRET_KEY", + "django-insecure-ffdjavjsp%s%b069$aai#h7odtbd#!q8uu7=hn1tv&y$gdq17_" +) # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = env.get_boolean("DJANGO_DEBUG", False) ALLOWED_HOSTS = [] -INTERNAL_IPS = [ - # ... - "127.0.0.1", - # ... -] +INTERNAL_IPS = ["127.0.0.1",] + +_env_hosts = env.get("DJANGO_ALLOWED_HOSTS", default=None) +if _env_hosts: + ALLOWED_HOSTS += _env_hosts.split(",") # Application definition @@ -58,6 +62,7 @@ MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", @@ -103,15 +108,62 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "NAME": "postgres", - "USER": "postgres", - "PASSWORD": "postgres", - "HOST": "127.0.0.1", - "PORT": "6432", + "NAME": env.get("POSTGRES_DB", default="postgres"), + "USER": env.get("POSTGRES_USER", default="postgres"), + "PASSWORD": env.get("POSTGRES_PASSWORD", default="postgres"), + "HOST": env.get("POSTGRES_HOST", default="127.0.0.1"), + "PORT": env.get("POSTGRES_PORT", default="5432"), } } +# Security + +_https_enabled = env.get_boolean("DJANGO_HTTPS", default=False) + +X_FRAME_OPTIONS = "DENY" +SECURE_SSL_REDIRECT = _https_enabled + +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + +SESSION_COOKIE_SECURE = _https_enabled +CSRF_COOKIE_SECURE = _https_enabled +# Needed to work in kubernetes, as the app may be behind a proxy/may not know it's +# own domain +SESSION_COOKIE_DOMAIN = env.get("SESSION_COOKIE_DOMAIN", default=None) +CSRF_COOKIE_DOMAIN = env.get("CSRF_COOKIE_DOMAIN", default=None) +SESSION_COOKIE_NAME = env.get("SESSION_COOKIE_NAME", + default="humitifier_sessionid") +SESSION_EXPIRE_AT_BROWSER_CLOSE = True +SESSION_COOKIE_AGE = 60 * 60 * 12 # 12 hours + +CSRF_TRUSTED_ORIGINS = [ + f"http{'s' if _https_enabled else ''}://{host}" for host in ALLOWED_HOSTS +] + +# Logging + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + }, + "root": { + "handlers": ["console"], + "level": env.get("LOG_LEVEL", default="INFO"), + }, + "loggers": { + "django": { + "handlers": ["console"], + "level": env.get("DJANGO_LOG_LEVEL", default="INFO"), + "propagate": False, + }, + }, +} + # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators @@ -152,3 +204,13 @@ # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +# Whitenoise + +STATIC_ROOT = BASE_DIR / "static" + +STORAGES = { + "staticfiles": { + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", + }, +} \ No newline at end of file From 0e9206df6559bc2f3d581e4ebc51e7e8fe162454 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:18:19 +0100 Subject: [PATCH 07/20] feat: upload scan to new server --- agent/humitifier/config.py | 1 + agent/humitifier/tasks.py | 25 +++++++++ agent/poetry.lock | 101 +++++++++++++------------------------ agent/pyproject.toml | 1 + 4 files changed, 63 insertions(+), 65 deletions(-) diff --git a/agent/humitifier/config.py b/agent/humitifier/config.py index a87347a..fc0fda7 100644 --- a/agent/humitifier/config.py +++ b/agent/humitifier/config.py @@ -15,6 +15,7 @@ class Config: inventory: list[str] pssh: dict tasks: dict[str, str] + upload_endpoint = str @classmethod def load(cls) -> "Config": diff --git a/agent/humitifier/tasks.py b/agent/humitifier/tasks.py index e3fd8c7..d2213dd 100644 --- a/agent/humitifier/tasks.py +++ b/agent/humitifier/tasks.py @@ -1,7 +1,9 @@ +import asyncio from sys import stdout import asyncpg import json +import requests import time from datetime import datetime from rocketry import Rocketry @@ -111,6 +113,29 @@ async def parse_facts(): conn = await asyncpg.connect(CONFIG.db) rows = await conn.fetch("""SELECT name, host, scan, stdout, stderr, exception, exit_code FROM host_outputs""") parsed_rows = [(row["name"], row["host"], row["scan"], parse_row_data(row)) for row in rows] + + hosts = {row["host"] for row in rows} + host_data = [] + + for host in hosts: + host_data.append( + { + "host": host, + "data": { + name: parsed.to_sql() + for name, this_host, scan, parsed in parsed_rows + if this_host == host + }, + } + ) + + await asyncio.to_thread( + requests.post, + CONFIG.upload_endpoint, + data=json.dumps(host_data), + headers={'Content-Type': 'application/json'} + ) + await conn.executemany( """INSERT INTO facts(name, host, scan, data) VALUES($1, $2, $3, $4)""", [(name, host, scan, json.dumps(parsed.to_sql())) for name, host, scan, parsed in parsed_rows], diff --git a/agent/poetry.lock b/agent/poetry.lock index a3edf90..d090285 100644 --- a/agent/poetry.lock +++ b/agent/poetry.lock @@ -135,8 +135,6 @@ 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)"] @@ -474,20 +472,6 @@ files = [ {file = "cwcwidth-0.1.9.tar.gz", hash = "sha256:f19d11a0148d4a8cacd064c96e93bca8ce3415a186ae8204038f45e108db76b8"}, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "faker" version = "17.6.0" @@ -753,52 +737,52 @@ ansicon = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "mypy" -version = "1.12.1" +version = "1.13.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, - {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, - {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, - {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, - {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, - {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, - {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, - {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, - {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, - {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, - {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, - {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, - {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, - {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, - {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, - {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, - {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, - {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, - {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, - {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, - {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, - {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, - {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, - {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] @@ -1020,11 +1004,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<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"] @@ -1255,17 +1237,6 @@ files = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -[[package]] -name = "tomli" -version = "2.0.2" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -1396,5 +1367,5 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "e9339bf4b2411c31c765b828121c7579bfb56d950cce3adcbbe00e1e8b5a05ee" +python-versions = "~3.11" +content-hash = "20dcaee8d0b2542cf48e6d799321d9251276199e13f6ea2ab522c32f25d91bf7" diff --git a/agent/pyproject.toml b/agent/pyproject.toml index 9c405b0..25ef6ba 100644 --- a/agent/pyproject.toml +++ b/agent/pyproject.toml @@ -12,6 +12,7 @@ parallel-ssh = "^2.12.0" rocketry = "^2.5.1" pydantic = "<2.0.0" # Needed to keep compatibility with rocketry asyncpg = "^0.29.0" +requests = "^2.32.3" [tool.poetry.group.dev.dependencies] From e30dfe210da106f81a7002604a8715f45c71bcb6 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:18:32 +0100 Subject: [PATCH 08/20] fix: update agent build action --- .github/workflows/{build.yaml => build-agent.yaml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{build.yaml => build-agent.yaml} (87%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build-agent.yaml similarity index 87% rename from .github/workflows/build.yaml rename to .github/workflows/build-agent.yaml index 304cf93..6bc7cff 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build-agent.yaml @@ -15,10 +15,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -31,7 +31,7 @@ jobs: file: ${{ env.DOCKERFILE_PATH }} push: true tags: | - ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} - name: Grype Scan From 0a74a007602c9ecffbfc9b9ed4c798d420f81d76 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:21:15 +0100 Subject: [PATCH 09/20] fix: possible syntax error --- .github/workflows/build-agent.yaml | 5 +++-- .github/workflows/build-server.yaml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-agent.yaml b/.github/workflows/build-agent.yaml index 6bc7cff..ad45fcb 100644 --- a/.github/workflows/build-agent.yaml +++ b/.github/workflows/build-agent.yaml @@ -25,14 +25,15 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push main image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: . file: ${{ env.DOCKERFILE_PATH }} push: true tags: | -# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} + +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest - name: Grype Scan id: scan diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index 5110c13..37cde58 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -31,9 +31,9 @@ jobs: file: ${{ env.DOCKERFILE_PATH }} push: true tags: | -# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} - +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest + - name: Grype Scan id: scan uses: anchore/scan-action@v3 From 9743910dbc053f2a812bb81b099283bf685e3246 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:22:08 +0100 Subject: [PATCH 10/20] fix: distinct build workflow names --- .github/workflows/build-agent.yaml | 2 +- .github/workflows/build-server.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-agent.yaml b/.github/workflows/build-agent.yaml index ad45fcb..f888136 100644 --- a/.github/workflows/build-agent.yaml +++ b/.github/workflows/build-agent.yaml @@ -1,4 +1,4 @@ -name: Build and Push Docker Images +name: Build and Push Docker Humitifier-Agent Image on: push: diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index 37cde58..5edad5f 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -1,4 +1,4 @@ -name: Build and Push Docker Images +name: Build and Push Docker Humitifier-Server Image on: push: From dd523de5da464957a91872469286488c3f0e0e33 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:26:03 +0100 Subject: [PATCH 11/20] fix: build context --- .github/workflows/build-agent.yaml | 3 ++- .github/workflows/build-server.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-agent.yaml b/.github/workflows/build-agent.yaml index f888136..e47bba7 100644 --- a/.github/workflows/build-agent.yaml +++ b/.github/workflows/build-agent.yaml @@ -9,6 +9,7 @@ on: env: IMAGE_NAME: humitifier DOCKERFILE_PATH: ./agent/Dockerfile + PATH: ./agent jobs: build-and-push: @@ -27,7 +28,7 @@ jobs: - name: Build and push main image uses: docker/build-push-action@v6 with: - context: . + context: ${{ env.PATH }} file: ${{ env.DOCKERFILE_PATH }} push: true tags: | diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index 5edad5f..ce193b5 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -9,6 +9,7 @@ on: env: IMAGE_NAME: humitifier-server DOCKERFILE_PATH: ./humitifier-server/Dockerfile + PATH: ./humitifier-server jobs: build-and-push: @@ -27,7 +28,7 @@ jobs: - name: Build and push main image uses: docker/build-push-action@v4 with: - context: . + context: ${{ env.PATH }} file: ${{ env.DOCKERFILE_PATH }} push: true tags: | From f149aaf750ac6f0dc8cf5eb1d13cac98a2c3c49c Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:28:01 +0100 Subject: [PATCH 12/20] fix: don't use protected env var name -.- --- .github/workflows/build-agent.yaml | 4 ++-- .github/workflows/build-server.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-agent.yaml b/.github/workflows/build-agent.yaml index e47bba7..c426bd3 100644 --- a/.github/workflows/build-agent.yaml +++ b/.github/workflows/build-agent.yaml @@ -9,7 +9,7 @@ on: env: IMAGE_NAME: humitifier DOCKERFILE_PATH: ./agent/Dockerfile - PATH: ./agent + CONTEXT_PATH: ./agent jobs: build-and-push: @@ -28,7 +28,7 @@ jobs: - name: Build and push main image uses: docker/build-push-action@v6 with: - context: ${{ env.PATH }} + context: ${{ env.CONTEXT_PATH }} file: ${{ env.DOCKERFILE_PATH }} push: true tags: | diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml index ce193b5..79b8568 100644 --- a/.github/workflows/build-server.yaml +++ b/.github/workflows/build-server.yaml @@ -9,7 +9,7 @@ on: env: IMAGE_NAME: humitifier-server DOCKERFILE_PATH: ./humitifier-server/Dockerfile - PATH: ./humitifier-server + CONTEXT_PATH: ./humitifier-server jobs: build-and-push: @@ -28,7 +28,7 @@ jobs: - name: Build and push main image uses: docker/build-push-action@v4 with: - context: ${{ env.PATH }} + context: ${{ env.CONTEXT_PATH }} file: ${{ env.DOCKERFILE_PATH }} push: true tags: | From 97c0ac82320a78982aa6d932f4dfd4b6ce343789 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:32:15 +0100 Subject: [PATCH 13/20] fix: remove unused dir from dockerfile --- agent/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/Dockerfile b/agent/Dockerfile index 0807c2a..81b7af2 100644 --- a/agent/Dockerfile +++ b/agent/Dockerfile @@ -17,7 +17,6 @@ FROM python:3.11.9-slim WORKDIR /app COPY --from=builder /app . -COPY static/ static COPY supabase/migrations /migrations COPY entrypoint/main.py entrypoint.py From de680a88a474abeafc859b193cf73ddc4c31a052 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:32:25 +0100 Subject: [PATCH 14/20] feat: consolidate build steps --- .github/workflows/build-agent.yaml | 49 ---------------- .github/workflows/build-images.yaml | 87 +++++++++++++++++++++++++++++ .github/workflows/build-server.yaml | 48 ---------------- 3 files changed, 87 insertions(+), 97 deletions(-) delete mode 100644 .github/workflows/build-agent.yaml create mode 100644 .github/workflows/build-images.yaml delete mode 100644 .github/workflows/build-server.yaml diff --git a/.github/workflows/build-agent.yaml b/.github/workflows/build-agent.yaml deleted file mode 100644 index c426bd3..0000000 --- a/.github/workflows/build-agent.yaml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build and Push Docker Humitifier-Agent Image - -on: - push: - tags: - - '*' - workflow_dispatch: - -env: - IMAGE_NAME: humitifier - DOCKERFILE_PATH: ./agent/Dockerfile - CONTEXT_PATH: ./agent - -jobs: - build-and-push: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push main image - uses: docker/build-push-action@v6 - with: - context: ${{ env.CONTEXT_PATH }} - file: ${{ env.DOCKERFILE_PATH }} - push: true - tags: | - ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} - -# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest - - - name: Grype Scan - id: scan - uses: anchore/scan-action@v3 - with: - image: ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} - fail-build: false - - - name: upload Grype SARIF report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ${{ steps.scan.outputs.sarif }} diff --git a/.github/workflows/build-images.yaml b/.github/workflows/build-images.yaml new file mode 100644 index 0000000..8fd0679 --- /dev/null +++ b/.github/workflows/build-images.yaml @@ -0,0 +1,87 @@ +name: Build and Push Docker Images + +on: + push: + tags: + - '*' + workflow_dispatch: + +env: + AGENT_IMAGE_NAME: humitifier + AGENT_DOCKERFILE_PATH: ./agent/Dockerfile + AGENT_CONTEXT_PATH: ./agent + SERVER_IMAGE_NAME: humitifier-server + SERVER_DOCKERFILE_PATH: ./humitifier-server/Dockerfile + SERVER_CONTEXT_PATH: ./humitifier-server + +jobs: + build-and-push-agent: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push main image + uses: docker/build-push-action@v6 + with: + context: ${{ env.AGENT_CONTEXT_PATH }} + file: ${{ env.AGENT_DOCKERFILE_PATH }} + push: true + tags: | + ghcr.io/centrefordigitalhumanities/humitifier/${{ env.AGENT_IMAGE_NAME }}:${{ github.ref_name }} + +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.AGENT_IMAGE_NAME }}:latest + + - name: Grype Scan + id: scan + uses: anchore/scan-action@v3 + with: + image: ghcr.io/centrefordigitalhumanities/humitifier/${{ env.AGENT_IMAGE_NAME }}:${{ github.ref_name }} + fail-build: false + + - name: upload Grype SARIF report + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} + + build-and-push-server: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push main image + uses: docker/build-push-action@v4 + with: + context: ${{ env.SERVER_CONTEXT_PATH }} + file: ${{ env.SERVER_DOCKERFILE_PATH }} + push: true + tags: | + ghcr.io/centrefordigitalhumanities/humitifier/${{ env.SERVER_IMAGE_NAME }}:${{ github.ref_name }} +# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.SERVER_IMAGE_NAME }}:latest + + - name: Grype Scan + id: scan + uses: anchore/scan-action@v3 + with: + image: ghcr.io/centrefordigitalhumanities/humitifier/${{ env.SERVER_IMAGE_NAME }}:${{ github.ref_name }} + fail-build: false + + - name: upload Grype SARIF report + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} diff --git a/.github/workflows/build-server.yaml b/.github/workflows/build-server.yaml deleted file mode 100644 index 79b8568..0000000 --- a/.github/workflows/build-server.yaml +++ /dev/null @@ -1,48 +0,0 @@ -name: Build and Push Docker Humitifier-Server Image - -on: - push: - tags: - - '*' - workflow_dispatch: - -env: - IMAGE_NAME: humitifier-server - DOCKERFILE_PATH: ./humitifier-server/Dockerfile - CONTEXT_PATH: ./humitifier-server - -jobs: - build-and-push: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push main image - uses: docker/build-push-action@v4 - with: - context: ${{ env.CONTEXT_PATH }} - file: ${{ env.DOCKERFILE_PATH }} - push: true - tags: | - ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} -# ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:latest - - - name: Grype Scan - id: scan - uses: anchore/scan-action@v3 - with: - image: ghcr.io/centrefordigitalhumanities/humitifier/${{ env.IMAGE_NAME }}:${{ github.ref_name }} - fail-build: false - - - name: upload Grype SARIF report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ${{ steps.scan.outputs.sarif }} From ca1942f107ab26c672c70189895fd54ef9ae1034 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 13:36:17 +0100 Subject: [PATCH 15/20] fix: use newest build action for server as well --- .github/workflows/build-images.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-images.yaml b/.github/workflows/build-images.yaml index 8fd0679..4a36a22 100644 --- a/.github/workflows/build-images.yaml +++ b/.github/workflows/build-images.yaml @@ -65,7 +65,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push main image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: context: ${{ env.SERVER_CONTEXT_PATH }} file: ${{ env.SERVER_DOCKERFILE_PATH }} From 3b4ea10b6984fe03d92ef370c39af30547a24800 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 14:00:58 +0100 Subject: [PATCH 16/20] feat: integrate sentry --- humitifier-server/poetry.lock | 55 ++++++++++++++++++- humitifier-server/pyproject.toml | 1 + .../src/humitifier_server/settings.py | 16 +++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/humitifier-server/poetry.lock b/humitifier-server/poetry.lock index cb94eb4..13d0d4c 100644 --- a/humitifier-server/poetry.lock +++ b/humitifier-server/poetry.lock @@ -779,6 +779,59 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "sentry-sdk" +version = "2.17.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sentry_sdk-2.17.0-py2.py3-none-any.whl", hash = "sha256:625955884b862cc58748920f9e21efdfb8e0d4f98cca4ab0d3918576d5b606ad"}, + {file = "sentry_sdk-2.17.0.tar.gz", hash = "sha256:dd0a05352b78ffeacced73a94e86f38b32e2eae15fff5f30ca5abb568a72eacf"}, +] + +[package.dependencies] +certifi = "*" +django = {version = ">=1.8", optional = true, markers = "extra == \"django\""} +urllib3 = ">=1.26.11" + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +anthropic = ["anthropic (>=0.16)"] +arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +celery-redbeat = ["celery-redbeat (>=2)"] +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)", "protobuf (>=3.8.0)"] +http2 = ["httpcore[http2] (==1.*)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +huggingface-hub = ["huggingface-hub (>=0.22)"] +langchain = ["langchain (>=0.0.210)"] +litestar = ["litestar (>=2.0.0)"] +loguru = ["loguru (>=0.5)"] +openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +opentelemetry-experimental = ["opentelemetry-distro"] +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 (>=6)"] + [[package]] name = "six" version = "1.16.0" @@ -872,4 +925,4 @@ brotli = ["brotli"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "47c8c54a6d5ace32fb3719706a80127eaaa42914338ff460231a051701ae2655" +content-hash = "9b90c3ab84c9ce2596c31f902fd6f3f9c7d99f57ce6d8c7bab0344ee85a5de88" diff --git a/humitifier-server/pyproject.toml b/humitifier-server/pyproject.toml index 1496223..0eefb52 100644 --- a/humitifier-server/pyproject.toml +++ b/humitifier-server/pyproject.toml @@ -15,6 +15,7 @@ markdown = "^3.7" django-debug-toolbar = "^4.4.6" django-simple-menu = "^2.1.3" whitenoise = "^6.8.2" +sentry-sdk = {extras = ["django"], version = "^2.17.0"} [tool.poetry.group.dev.dependencies] diff --git a/humitifier-server/src/humitifier_server/settings.py b/humitifier-server/src/humitifier_server/settings.py index f0c2dac..ce7f695 100644 --- a/humitifier-server/src/humitifier_server/settings.py +++ b/humitifier-server/src/humitifier_server/settings.py @@ -213,4 +213,18 @@ "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", }, -} \ No newline at end of file +} + +# Sentry + +import sentry_sdk + +DSN = env.get("SENTRY_DSN", default=None) +if DSN: + sentry_sdk.init( + dsn=DSN, + traces_sample_rate=1.0, + _experiments={ + "continuous_profiling_auto_start": True, + }, + ) From 3011f5cdf3b5cf39a48e16e48247a1c3d2fdec1b Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 14:32:25 +0100 Subject: [PATCH 17/20] fix: fixed very stupid typo --- agent/humitifier/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/humitifier/config.py b/agent/humitifier/config.py index fc0fda7..0193ad9 100644 --- a/agent/humitifier/config.py +++ b/agent/humitifier/config.py @@ -15,7 +15,7 @@ class Config: inventory: list[str] pssh: dict tasks: dict[str, str] - upload_endpoint = str + upload_endpoint: str @classmethod def load(cls) -> "Config": From d75bdf9db5c1023c52725f56596402ad77c02a7d Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 14:50:11 +0100 Subject: [PATCH 18/20] fix: made upload-scans endpoint ssl-exempt --- humitifier-server/src/humitifier_server/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/humitifier-server/src/humitifier_server/settings.py b/humitifier-server/src/humitifier_server/settings.py index ce7f695..845c1e0 100644 --- a/humitifier-server/src/humitifier_server/settings.py +++ b/humitifier-server/src/humitifier_server/settings.py @@ -123,6 +123,9 @@ X_FRAME_OPTIONS = "DENY" SECURE_SSL_REDIRECT = _https_enabled +SECURE_REDIRECT_EXEMPT = [ + r'^api/upload-scans/$' +] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From 57691cc9c643f7d42309901c0fd1bf8802e36a4b Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 14:50:50 +0100 Subject: [PATCH 19/20] fix: added missing migration --- .../hosts/migrations/0007_alter_alert_type.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 humitifier-server/src/hosts/migrations/0007_alter_alert_type.py diff --git a/humitifier-server/src/hosts/migrations/0007_alter_alert_type.py b/humitifier-server/src/hosts/migrations/0007_alter_alert_type.py new file mode 100644 index 0000000..101bec8 --- /dev/null +++ b/humitifier-server/src/hosts/migrations/0007_alter_alert_type.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.2 on 2024-10-30 13:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hosts", "0006_alter_host_options_host_os"), + ] + + operations = [ + migrations.AlterField( + model_name="alert", + name="type", + field=models.CharField( + choices=[ + ("Outdated OS", "Outdated Os"), + ("Has fact error", "Fact Error"), + ("Puppet agent disabled", "Disabled Puppet"), + ], + max_length=255, + ), + ), + ] From 351fe328a4c1846c3c3c70e9e539ea28fdbfb8a6 Mon Sep 17 00:00:00 2001 From: "Mees, T.D. (Ty)" Date: Wed, 30 Oct 2024 15:01:51 +0100 Subject: [PATCH 20/20] fix: fixed another stupid typo --- humitifier-server/src/humitifier_server/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/humitifier-server/src/humitifier_server/settings.py b/humitifier-server/src/humitifier_server/settings.py index 845c1e0..1817d34 100644 --- a/humitifier-server/src/humitifier_server/settings.py +++ b/humitifier-server/src/humitifier_server/settings.py @@ -124,7 +124,7 @@ X_FRAME_OPTIONS = "DENY" SECURE_SSL_REDIRECT = _https_enabled SECURE_REDIRECT_EXEMPT = [ - r'^api/upload-scans/$' + r'^api/upload_scans/$' ] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
+
+ {% if is_paginated %} + {% if page_obj.has_previous %} + First + {% if page_obj.number != 1 %} + Previous + {% endif %} + {% endif %} + {% endif %} +
+ +
+ Total items: {{ paginator.count }} | + Page {{ page_obj.number }} of {{ paginator.num_pages }} +
+ +
+ {% if is_paginated %} + {% if page_obj.has_next %} + {% if page_obj.number != paginator.num_pages %} + Next + {% endif %} + Last + {% endif %} + {% endif %} +
+ +