From 47a84236a3bdf240c857f33b482f6b44e5029e47 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 13:28:05 +0200 Subject: [PATCH 01/31] update python, django and minor packages --- .gitignore | 153 +--- AHC_app/.python-version | 1 + AHC_app/AHC_app/settings.py | 4 - AHC_app/Pipfile | 3 - AHC_app/__init__.py | 0 AHC_app/pyproject.toml | 50 ++ AHC_app/uv.lock | 1470 +++++++++++++++++++++++++++++++++++ 7 files changed, 1546 insertions(+), 135 deletions(-) create mode 100644 AHC_app/.python-version delete mode 100644 AHC_app/__init__.py create mode 100644 AHC_app/pyproject.toml create mode 100644 AHC_app/uv.lock diff --git a/.gitignore b/.gitignore index f3464e5..e6dfafc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,165 +1,62 @@ -# Byte-compiled / optimized / DLL files +# Python __pycache__/ *.py[cod] -*$py.class - -# C extensions *.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg *.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt +*.egg-info/ +.Python -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ +# Testing .coverage .coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ +htmlcov/ .pytest_cache/ -cover/ - -# Translations -*.mo -*.pot +.hypothesis/ -# Django stuff: +# Django *.log local_settings.py db.sqlite3 db.sqlite3-journal -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - # Environments .env -.venv +.venv/ env/ venv/ -ENV/ -env.bak/ -venv.bak/ -# Spyder project settings -.spyderproject -.spyproject +# uv — commit uv.lock, do NOT add it here +# .python-version is intentionally tracked -# Rope project settings -.ropeproject +# Build / packaging +build/ +dist/ +wheels/ -# mkdocs documentation -/site +# Celery +celerybeat-schedule +celerybeat.pid -# mypy +# Type checkers .mypy_cache/ .dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ +.ty_cache/ -# Cython debug symbols -cython_debug/ +# Claude Code — local settings are machine-specific +.claude/settings.local.json -# PyCharm -# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# IDE .idea/ -# Collected static files - temporary +# Project-specific /AHC_app/static_collected/ /AHC_app/static/media/profile_pics/animals/ /AHC_app/static/media/attachments/ -# Kubernetes configuration +# Kubernetes /AHC_app/tars/ /kubernetes/**/*.tar secret.yaml -# volumens +# Volumes /db/ diff --git a/AHC_app/.python-version b/AHC_app/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/AHC_app/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index f933f48..c1e968c 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -49,7 +49,6 @@ "compressor", "taggit", "django_crontab", - "django_cron", "homepage.apps.HomepageConfig", "users.apps.UsersConfig", "animals.apps.AnimalsConfig", @@ -210,9 +209,6 @@ ("6 * * * *", "AHC_app.celery_notifications.cron.send_discord_notes"), ] -CRON_CLASSES = [ - # 'AHC_app.celery_notifications.cron.SynchNotificationsCron', -] CELERY_BROKER_URL = config("CELERY_BROKER_URL") CELERY_BACKEND = config("CELERY_BACKEND") diff --git a/AHC_app/Pipfile b/AHC_app/Pipfile index 8fa3083..e76d50b 100644 --- a/AHC_app/Pipfile +++ b/AHC_app/Pipfile @@ -16,7 +16,6 @@ click-plugins = "==1.1.1" click-repl = "==0.3.0" colorama = "==0.4.6" contourpy = "==1.0.7" -couchdb = "==1.2" crispy-bootstrap4 = "==2022.1" cryptography = "==41.0.5" cycler = "==0.11.0" @@ -40,7 +39,6 @@ oauthlib = "==3.2.2" packaging = "==23.1" pillow = "==9.5.0" prompt-toolkit = "==3.0.39" -psycopg2 = "==2.9.6" pyasn1 = "==0.5.0" pyasn1-modules = "==0.3.0" pycparser = "==2.21" @@ -67,7 +65,6 @@ django-cron = "*" python-decouple = "*" pycouchdb = "*" discord = "*" -django-ajax = "*" psycopg2-binary = "*" django = "==4.2.11" celery = "5.3.4" diff --git a/AHC_app/__init__.py b/AHC_app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml new file mode 100644 index 0000000..cb462e0 --- /dev/null +++ b/AHC_app/pyproject.toml @@ -0,0 +1,50 @@ +[project] +name = "animals-healthcare-application" +version = "0.1.0" +description = "Animals Healthcare Application — pet health data management" +requires-python = ">=3.14" + +dependencies = [ + "django>=5.2,<5.3", + "djangorestframework>=3.14", + "psycopg[binary]>=3.2", + "celery>=5.4", + "redis>=5.0", + "flower", + "pycouchdb", + "discord", + "python-decouple", + "pillow>=11.0", + "cryptography>=43", + "cffi>=1.17", + "django-crispy-forms", + "crispy-bootstrap4", + "django-bootstrap-modal-forms", + "django-compressor", + "django-libsass", + "libsass", + "django-appconf", + "django-taggit", + "django-timezone-field>=6.1", + "django-crontab", + "pytz", + "tzdata", + "python3-openid", + "requests", + "requests-oauthlib", + "httplib2", + "python-dateutil", + "pyjwt", + "defusedxml", + "icecream", +] + +[dependency-groups] +dev = [ + "pre-commit", + "black", + "isort", +] + +[tool.uv] +package = false diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock new file mode 100644 index 0000000..390af96 --- /dev/null +++ b/AHC_app/uv.lock @@ -0,0 +1,1470 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "amqp" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432", size = 129013, upload-time = "2024-11-12T19:55:44.051Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944, upload-time = "2024-11-12T19:55:41.782Z" }, +] + +[[package]] +name = "animals-healthcare-application" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "celery" }, + { name = "cffi" }, + { name = "crispy-bootstrap4" }, + { name = "cryptography" }, + { name = "defusedxml" }, + { name = "discord" }, + { name = "django" }, + { name = "django-appconf" }, + { name = "django-bootstrap-modal-forms" }, + { name = "django-compressor" }, + { name = "django-crispy-forms" }, + { name = "django-crontab" }, + { name = "django-libsass" }, + { name = "django-taggit" }, + { name = "django-timezone-field" }, + { name = "djangorestframework" }, + { name = "flower" }, + { name = "httplib2" }, + { name = "icecream" }, + { name = "libsass" }, + { name = "pillow" }, + { name = "psycopg", extra = ["binary"] }, + { name = "pycouchdb" }, + { name = "pyjwt" }, + { name = "python-dateutil" }, + { name = "python-decouple" }, + { name = "python3-openid" }, + { name = "pytz" }, + { name = "redis" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "tzdata" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "isort" }, + { name = "pre-commit" }, +] + +[package.metadata] +requires-dist = [ + { name = "celery", specifier = ">=5.4" }, + { name = "cffi", specifier = ">=1.17" }, + { name = "crispy-bootstrap4" }, + { name = "cryptography", specifier = ">=43" }, + { name = "defusedxml" }, + { name = "discord" }, + { name = "django", specifier = ">=5.2,<5.3" }, + { name = "django-appconf" }, + { name = "django-bootstrap-modal-forms" }, + { name = "django-compressor" }, + { name = "django-crispy-forms" }, + { name = "django-crontab" }, + { name = "django-libsass" }, + { name = "django-taggit" }, + { name = "django-timezone-field", specifier = ">=6.1" }, + { name = "djangorestframework", specifier = ">=3.14" }, + { name = "flower" }, + { name = "httplib2" }, + { name = "icecream" }, + { name = "libsass" }, + { name = "pillow", specifier = ">=11.0" }, + { name = "psycopg", extras = ["binary"], specifier = ">=3.2" }, + { name = "pycouchdb" }, + { name = "pyjwt" }, + { name = "python-dateutil" }, + { name = "python-decouple" }, + { name = "python3-openid" }, + { name = "pytz" }, + { name = "redis", specifier = ">=5.0" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "tzdata" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "black" }, + { name = "isort" }, + { name = "pre-commit" }, +] + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "audioop-lts" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, + { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, + { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, +] + +[[package]] +name = "billiard" +version = "4.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/23/b12ac0bcdfb7360d664f40a00b1bda139cbbbced012c34e375506dbd0143/billiard-4.2.4.tar.gz", hash = "sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f", size = 156537, upload-time = "2025-11-30T13:28:48.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/87/8bab77b323f16d67be364031220069f79159117dd5e43eeb4be2fef1ac9b/billiard-4.2.4-py3-none-any.whl", hash = "sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5", size = 87070, upload-time = "2025-11-30T13:28:47.016Z" }, +] + +[[package]] +name = "black" +version = "26.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, +] + +[[package]] +name = "celery" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "billiard" }, + { name = "click" }, + { name = "click-didyoumean" }, + { name = "click-plugins" }, + { name = "click-repl" }, + { name = "kombu" }, + { name = "python-dateutil" }, + { name = "tzlocal" }, + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/b4/a1233943ab5c8ea05fb877a88a0a0622bf47444b99e4991a8045ac37ea1d/celery-5.6.3.tar.gz", hash = "sha256:177006bd2054b882e9f01be59abd8529e88879ef50d7918a7050c5a9f4e12912", size = 1742243, upload-time = "2026-03-26T12:14:51.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/c9/6eccdda96e098f7ae843162db2d3c149c6931a24fda69fe4ab84d0027eb5/celery-5.6.3-py3-none-any.whl", hash = "sha256:0808f42f80909c4d5833202360ffafb2a4f83f4d8e23e1285d926610e9a7afa6", size = 451235, upload-time = "2026-03-26T12:14:49.491Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "click-didyoumean" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463", size = 3089, upload-time = "2024-03-24T08:22:07.499Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c", size = 3631, upload-time = "2024-03-24T08:22:06.356Z" }, +] + +[[package]] +name = "click-plugins" +version = "1.1.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/34847b59150da33690a36da3681d6bbc2ec14ee9a846bc30a6746e5984e4/click_plugins-1.1.1.2.tar.gz", hash = "sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261", size = 8343, upload-time = "2025-06-25T00:47:37.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/9a/2abecb28ae875e39c8cad711eb1186d8d14eab564705325e77e4e6ab9ae5/click_plugins-1.1.1.2-py2.py3-none-any.whl", hash = "sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6", size = 11051, upload-time = "2025-06-25T00:47:36.731Z" }, +] + +[[package]] +name = "click-repl" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "prompt-toolkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", size = 10449, upload-time = "2023-06-15T12:43:51.141Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289, upload-time = "2023-06-15T12:43:48.626Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "crispy-bootstrap4" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-crispy-forms" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/cc/638d36595da9fbb2c9d0be98bf6007442a65a521b614cf4645d04311b061/crispy_bootstrap4-2026.2.tar.gz", hash = "sha256:66f8f14bf9c2c16ed94243236ed253a94e5a625afa1ee64022ce29db98c6cd85", size = 34645, upload-time = "2026-02-11T22:45:05.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/6d/b90d601ea2449cc6b35b4b08be90fb1f6ca1baf2be383ed195f7bfa91a32/crispy_bootstrap4-2026.2-py3-none-any.whl", hash = "sha256:4b2b99dfe3e3cacb548702159462110901bd38792b650b770e50c62284ac2227", size = 23178, upload-time = "2026-02-11T22:45:04.108Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +] + +[[package]] +name = "discord" +version = "2.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "discord-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/9f/0763429949416aeff9c6f33051270831b72e5582273acd28053b9b949462/discord-2.3.2.tar.gz", hash = "sha256:cc1ee2dbe6df218ca51519af355b97e87309f8230f58c7f34885feb8e8a76145", size = 1137, upload-time = "2023-08-10T21:45:07.93Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/38/d91ac49e8169b6c0f724f7aad26704eec07c4ecf31e067ca3d46a87e33d6/discord-2.3.2-py3-none-any.whl", hash = "sha256:d7959418799dd3b1e896685812d880169c193468b061b3431fa2a4664febd3da", size = 1132, upload-time = "2023-08-10T21:45:06.334Z" }, +] + +[[package]] +name = "discord-py" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "audioop-lts" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/57/9a2d9abdabdc9db8ef28ce0cf4129669e1c8717ba28d607b5ba357c4de3b/discord_py-2.7.1.tar.gz", hash = "sha256:24d5e6a45535152e4b98148a9dd6b550d25dc2c9fb41b6d670319411641249da", size = 1106326, upload-time = "2026-03-03T18:40:46.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/a7/17208c3b3f92319e7fad259f1c6d5a5baf8fd0654c54846ced329f83c3eb/discord_py-2.7.1-py3-none-any.whl", hash = "sha256:849dca2c63b171146f3a7f3f8acc04248098e9e6203412ce3cf2745f284f7439", size = 1227550, upload-time = "2026-03-03T18:40:44.492Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "django" +version = "5.2.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" }, +] + +[[package]] +name = "django-appconf" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/a2/e58bec8d7941b914af52a67c35b5709eceed2caa2848f28437f1666ed668/django_appconf-1.2.0.tar.gz", hash = "sha256:15a88d60dd942d6059f467412fe4581db632ef03018a3c183fb415d6fc9e5cec", size = 16127, upload-time = "2025-11-08T15:46:27.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/e6/4c34d94dfb74bbcbc489606e61f1924933de30d22c593dd1f429f35fbd7f/django_appconf-1.2.0-py3-none-any.whl", hash = "sha256:b81bce5ef0ceb9d84df48dfb623a32235d941c78cc5e45dbb6947f154ea277f4", size = 6500, upload-time = "2025-11-08T15:46:25.957Z" }, +] + +[[package]] +name = "django-bootstrap-modal-forms" +version = "3.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/92/d1d37315c897e2ad311242d617d87e26f507e0446bdfd6a0ef265501d138/django_bootstrap_modal_forms-3.0.5.tar.gz", hash = "sha256:322930953c68e1dcd4c5dc073612c77ff4d68c46074a55d98db2de6e7860050b", size = 38292, upload-time = "2024-09-28T13:39:55.656Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/e7/84a437e7d413e14627b82d6f3478960d01e88b5e927f673cee536e6907ba/django_bootstrap_modal_forms-3.0.5-py3-none-any.whl", hash = "sha256:e56bbe05fb29c5aa9e0f3c0277b0d8363b81cc6c4e4aaf152cedea883edae58a", size = 29560, upload-time = "2024-09-28T13:39:53.645Z" }, +] + +[[package]] +name = "django-compressor" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-appconf" }, + { name = "rcssmin" }, + { name = "rjsmin" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/e4/c6d87b1341d744ceafa85eeceb2adabb1c62b795b8207cbc580fb70df8f4/django_compressor-4.6.0.tar.gz", hash = "sha256:c7478feab98f3368780591f9ee28a433350f5277dd28811f7f710f5bc6dff3c0", size = 99735, upload-time = "2025-11-10T13:12:11.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/9d/9a0ba39f33574994e5b33aea55a68e8fad72b8dd923a82300e4e91774f59/django_compressor-4.6.0-py3-none-any.whl", hash = "sha256:6e7b21020a0d86272c5e37000c33accc4ebeb77394a3dd86d775a09aae7aade4", size = 96828, upload-time = "2025-11-10T13:12:10.001Z" }, +] + +[[package]] +name = "django-crispy-forms" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/42/c2cfb672493730b963ef377b103e29871c56348a215d0ae8cf362fe8ab1e/django_crispy_forms-2.6.tar.gz", hash = "sha256:4921a1087c6cd4f9fa3c139654c1de1c1c385f8bd6729aaee530bc0121ab4b93", size = 1097838, upload-time = "2026-03-01T09:03:37.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/e3/4c5915a732d6ab54da8871400852b67529518eedfb6b78ecf10bbccfcabb/django_crispy_forms-2.6-py3-none-any.whl", hash = "sha256:8ee0ae28b6b0ac41ff48a65944480c049fe8d1b0047086874fd7efabf4ec1374", size = 31479, upload-time = "2026-03-01T09:03:36.048Z" }, +] + +[[package]] +name = "django-crontab" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/bd/a122ba96167f5dfab70a58ca22fa046b7ef1ebad9ff026f7831bd6c2a49c/django-crontab-0.7.1.tar.gz", hash = "sha256:1201810a212460aaaa48eb6a766738740daf42c1a4f6aafecfb1525036929236", size = 7089, upload-time = "2016-03-07T19:35:54.714Z" } + +[[package]] +name = "django-libsass" +version = "0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django-compressor" }, + { name = "libsass" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/6c/fe7c95536eed569960daf139726c8f83eaf8c4ae01d908c22d94d60f31c2/django-libsass-0.9.tar.gz", hash = "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5", size = 6754, upload-time = "2021-07-08T14:16:55.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/ee/65935acc5a36c418fa17d5190a4aeb339cfdf98b6a93ca1c59134cf1e6aa/django_libsass-0.9-py3-none-any.whl", hash = "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97", size = 6572, upload-time = "2021-07-08T14:16:53.501Z" }, +] + +[[package]] +name = "django-taggit" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/a6/f1beaf8f552fe90c153cc039316ebab942c23dfbc88588dde081fefca816/django_taggit-6.1.0.tar.gz", hash = "sha256:c4d1199e6df34125dd36db5eb0efe545b254dec3980ce5dd80e6bab3e78757c3", size = 38151, upload-time = "2024-09-29T08:07:39.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/34/4185c345530b91d05cb82e05d07148f481a5eb5dc2ac44e092b3daa6f206/django_taggit-6.1.0-py3-none-any.whl", hash = "sha256:ab776264bbc76cb3d7e49e1bf9054962457831bd21c3a42db9138b41956e4cf0", size = 75749, upload-time = "2024-09-29T08:07:14.612Z" }, +] + +[[package]] +name = "django-timezone-field" +version = "7.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/05/9b93a66452cdb8a08ab26f08d5766d2332673e659a8b2aeb73f2a904d421/django_timezone_field-7.2.1.tar.gz", hash = "sha256:def846f9e7200b7b8f2a28fcce2b78fb2d470f6a9f272b07c4e014f6ba4c6d2e", size = 13096, upload-time = "2025-12-06T23:50:44.591Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/7f/d885667401515b467f84569c56075bc9add72c9fd425fca51a25f4c997e1/django_timezone_field-7.2.1-py3-none-any.whl", hash = "sha256:276915b72c5816f57c3baf9e43f816c695ef940d1b21f91ebf6203c09bf4ad44", size = 13284, upload-time = "2025-12-06T23:50:43.302Z" }, +] + +[[package]] +name = "djangorestframework" +version = "3.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/d7/c016e69fac19ff8afdc89db9d31d9ae43ae031e4d1993b20aca179b8301a/djangorestframework-3.17.1.tar.gz", hash = "sha256:a6def5f447fe78ff853bff1d47a3c59bf38f5434b031780b351b0c73a62db1a5", size = 905742, upload-time = "2026-03-24T16:58:33.705Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/e1/2c516bdc83652b1a60c6119366ac2c0607b479ed05cd6093f916ca8928f8/djangorestframework-3.17.1-py3-none-any.whl", hash = "sha256:c3c74dd3e83a5a3efc37b3c18d92bd6f86a6791c7b7d4dff62bb068500e76457", size = 898844, upload-time = "2026-03-24T16:58:31.845Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "flower" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "celery" }, + { name = "humanize" }, + { name = "prometheus-client" }, + { name = "pytz" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a1/357f1b5d8946deafdcfdd604f51baae9de10aafa2908d0b7322597155f92/flower-2.0.1.tar.gz", hash = "sha256:5ab717b979530770c16afb48b50d2a98d23c3e9fe39851dcf6bc4d01845a02a0", size = 3220408, upload-time = "2023-08-13T14:37:46.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ff/ee2f67c0ff146ec98b5df1df637b2bc2d17beeb05df9f427a67bd7a7d79c/flower-2.0.1-py2.py3-none-any.whl", hash = "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2", size = 383553, upload-time = "2023-08-13T14:37:41.552Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "httplib2" +version = "0.31.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" }, +] + +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + +[[package]] +name = "icecream" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "colorama" }, + { name = "executing" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/84/6ebc95844feae8a6a29c7fd57e9e3a7ac4817ffab384dc4f0ed53b8e3c46/icecream-2.2.0.tar.gz", hash = "sha256:9d7f244187f00a13f4ac77d176990e187e9c279d6cac4f7548e338291ad97343", size = 14267, upload-time = "2026-04-03T17:42:51.387Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/82/9707c7b0336bca53b75f52fc350956a93da66eb6be632b370bc933216fb4/icecream-2.2.0-py3-none-any.whl", hash = "sha256:f8df7343b3e787023eec22f42fbe4722df2f93099d394fd820b91e16b2e6cb56", size = 16707, upload-time = "2026-04-03T17:42:50.001Z" }, +] + +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + +[[package]] +name = "idna" +version = "3.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, +] + +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + +[[package]] +name = "kombu" +version = "5.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "amqp" }, + { name = "packaging" }, + { name = "tzdata" }, + { name = "vine" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/a5/607e533ed6c83ae1a696969b8e1c137dfebd5759a2e9682e26ff1b97740b/kombu-5.6.2.tar.gz", hash = "sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55", size = 472594, upload-time = "2025-12-29T20:30:07.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/0f/834427d8c03ff1d7e867d3db3d176470c64871753252b21b4f4897d1fa45/kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93", size = 214219, upload-time = "2025-12-29T20:30:05.74Z" }, +] + +[[package]] +name = "libsass" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b4/ab091585eaa77299558e3289ca206846aefc123fb320b5656ab2542c20ad/libsass-0.23.0.tar.gz", hash = "sha256:6f209955ede26684e76912caf329f4ccb57e4a043fd77fe0e7348dd9574f1880", size = 316068, upload-time = "2024-01-06T18:53:05.404Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/13/fc1bea1de880ca935137183727c7d4dd921c4128fc08b8ddc3698ba5a8a3/libsass-0.23.0-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:34cae047cbbfc4ffa832a61cbb110f3c95f5471c6170c842d3fed161e40814dc", size = 1086783, upload-time = "2024-01-06T19:02:38.903Z" }, + { url = "https://files.pythonhosted.org/packages/55/2f/6af938651ff3aec0a0b00742209df1172bc297fa73531f292801693b7315/libsass-0.23.0-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:ea97d1b45cdc2fc3590cb9d7b60f1d8915d3ce17a98c1f2d4dd47ee0d9c68ce6", size = 982759, upload-time = "2024-01-06T19:02:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/fd/5a/eb5b62641df0459a3291fc206cf5bd669c0feed7814dded8edef4ade8512/libsass-0.23.0-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4a218406d605f325d234e4678bd57126a66a88841cb95bee2caeafdc6f138306", size = 9444543, upload-time = "2024-01-06T19:02:43.191Z" }, + { url = "https://files.pythonhosted.org/packages/e5/fc/275783f5120970d859ae37d04b6a60c13bdec2aa4294b9dfa8a37b5c2513/libsass-0.23.0-cp38-abi3-win32.whl", hash = "sha256:31e86d92a5c7a551df844b72d83fc2b5e50abc6fbbb31e296f7bebd6489ed1b4", size = 775481, upload-time = "2024-01-06T19:02:46.05Z" }, + { url = "https://files.pythonhosted.org/packages/ef/20/caf3c7cf2432d85263119798c45221ddf67bdd7dae8f626d14ff8db04040/libsass-0.23.0-cp38-abi3-win_amd64.whl", hash = "sha256:a2ec85d819f353cbe807432d7275d653710d12b08ec7ef61c124a580a8352f3c", size = 872914, upload-time = "2024-01-06T19:02:47.61Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, +] + +[[package]] +name = "prometheus-client" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "psycopg" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" }, +] + +[package.optional-dependencies] +binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, +] + +[[package]] +name = "psycopg-binary" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/a6/828c9185701dab71b234c2a76c38a08b098ebfec5020716b4e93807492b5/psycopg_binary-3.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:28b7398fdd19db3232c884fb24550bdfe951221f510e195e233299e4c9b78f97", size = 4607292, upload-time = "2026-05-01T23:30:38.962Z" }, + { url = "https://files.pythonhosted.org/packages/92/58/5b40dbc9d839045c9dae956960e4fb6d20bcabe6c59a2aa34fc3a371913f/psycopg_binary-3.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1fbaa292a3c8bb61b45df1ad3da1908ccee7cb889db9425e3557d9e34e2a4829", size = 4687023, upload-time = "2026-05-01T23:30:47.227Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/793f0ac107a9003b48441d0d1f9f616d96e0f37458dd8dc12528ceff55fb/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94596f9e7633ee3f6440711d43bb70aa31cc0a46a900ab8b4201a366ace5c9e7", size = 5486985, upload-time = "2026-05-01T23:30:55.517Z" }, + { url = "https://files.pythonhosted.org/packages/8f/26/42e8533497e2592334f68ec529cf5f840f7fa4e99575a4bb61aa184dbfbf/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c0056529e68dbe9184cd4019a1f3d8f3a4ead2f6fc7a5afcf27d3314edd1277", size = 5168745, upload-time = "2026-05-01T23:31:01.904Z" }, + { url = "https://files.pythonhosted.org/packages/15/af/b7151776cc08d5935d45c833ec818a9beb417cf7c08239af1aafbdae78ee/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c09aad7051326e7603c14e50636db9c01f78272dc54b3accff03d46370461e6", size = 6761486, upload-time = "2026-05-01T23:31:14.511Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ed/c92533b9124712d592cbf1cd6c76da933a2e0acea81dfe1fbe7e735f0cff/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:514404ed543efd620c85602b747df2a23cf1241b4067199e1a66f2d2757aaa41", size = 4997427, upload-time = "2026-05-01T23:31:20.901Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/ccadfd0de416aa188356daa199453af24087b042e296088706d190ae0295/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:46893c26858be12cc49ca4226ed6a60b4bfccadd946b3bebb783a60b38788228", size = 4533549, upload-time = "2026-05-01T23:31:26.204Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a0/c8f43cee36386f7bc891ab41a9d31ea07cf9826038e732da79f26b1e5f34/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:df1d567fc430f6df15c9fcf67d87685fc49bdb325adc0db5af1adfb2f44eb5c9", size = 4210256, upload-time = "2026-05-01T23:31:33.884Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2c/c1547871be3790676e8868b38655496422f94f0978dfb66b74bdba2f1676/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6b9016b1714da4dd5ecaaa75b82098aa5a0b87854ce9b092e21c27c4ae23e014", size = 3946204, upload-time = "2026-05-01T23:31:39.626Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b1/f6670f00fa7ea601584623f6c11602ab92117d83eaff885e0210f6de7418/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:47c656a8a7ba6eb0cff1801a4caaa9c8bdc12d03080e273aff1c8ac39971a77e", size = 4255811, upload-time = "2026-05-01T23:31:44.986Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, +] + +[[package]] +name = "pycouchdb" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "chardet" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/b4/4f699a686a2ce14ab31cb17902693f2cf201ba51c3a6fb7aba210725c154/pycouchdb-1.16.0.tar.gz", hash = "sha256:309d71c3ce3f98bbee5731db00f514753438b81e6e7adefbb8c134312200a4f9", size = 11351, upload-time = "2024-05-29T10:00:11.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/63/b4397a7215c089c7951afb258069cc58a06788224f1bb6a0d4f976f2d476/pycouchdb-1.16.0-py3-none-any.whl", hash = "sha256:e26ce58f626fcabbe2f5b15b3ad2b89cdd3f6d666da673632037476d1191ab67", size = 12560, upload-time = "2024-05-29T10:00:09.31Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-decouple" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/97/373dcd5844ec0ea5893e13c39a2c67e7537987ad8de3842fe078db4582fa/python-decouple-3.8.tar.gz", hash = "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f", size = 9612, upload-time = "2023-03-01T19:38:38.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d4/9193206c4563ec771faf2ccf54815ca7918529fe81f6adb22ee6d0e06622/python_decouple-3.8-py3-none-any.whl", hash = "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66", size = 9947, upload-time = "2023-03-01T19:38:36.015Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/60/e88788207d81e46362cfbef0d4aaf4c0f49efc3c12d4c3fa3f542c34ebec/python_discovery-1.3.1.tar.gz", hash = "sha256:62f6db28064c9613e7ca76cb3f00c38c839a07c31c00dfe7ed0986493d2150a6", size = 68011, upload-time = "2026-05-12T20:53:36.336Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/6f/a05a317a66fee0aad270011461f1a63a453ed12471249f172f7d2e2bc7b4/python_discovery-1.3.1-py3-none-any.whl", hash = "sha256:ed188687ebb3b82c01a17cd5ac62fc94d9f6487a7f1a0f9dfe89753fec91039c", size = 33185, upload-time = "2026-05-12T20:53:34.969Z" }, +] + +[[package]] +name = "python3-openid" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "defusedxml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/4a/29feb8da6c44f77007dcd29518fea73a3d5653ee02a587ae1f17f1f5ddb5/python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf", size = 305600, upload-time = "2020-06-29T12:15:49.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a5/c6ba13860bdf5525f1ab01e01cc667578d6f1efc8a1dba355700fb04c29b/python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b", size = 133681, upload-time = "2020-06-29T12:15:47.502Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "rcssmin" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/af/c9654b4f9b054ec163ed7cb20d8db0e5ae05e2e9ce99a4c11d91a2180b3f/rcssmin-1.2.2.tar.gz", hash = "sha256:806986eaf7414545edc28a1d29523e9560e49e151ff4a337d9d1f0271d6e1cc4", size = 587012, upload-time = "2025-10-12T10:48:08.932Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/40/9c4cb3133f6d4ddfbeada76988a10ff2a974706fd6fcbb97edd8c0f4cc76/rcssmin-1.2.2-cp314-cp314-manylinux1_i686.whl", hash = "sha256:540dd3aa586b5f8f4c4b90db37e6a31c04718cdf90dbe9bec43c3b4dd50519e7", size = 49032, upload-time = "2025-10-12T10:48:53.014Z" }, + { url = "https://files.pythonhosted.org/packages/07/84/a411a48fd4179a88c68a2ad3649b408fa7887a421d3435c10ae6f5724e3a/rcssmin-1.2.2-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:6ea38a38eec263858b70bed6715478dcfed7fbc5d63333a8c512631ee22baad9", size = 49497, upload-time = "2025-10-12T10:48:54.009Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/5663a71a9304e0c9f33b765264508229d026359cfff746e1d0a593d809ea/rcssmin-1.2.2-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:07dc7d352e8eb08de82fc4c545dd04f9f487466c8370051e0bee4eb1e4dc85d0", size = 50382, upload-time = "2025-10-12T10:48:55.079Z" }, + { url = "https://files.pythonhosted.org/packages/d7/28/e411eb191ffff7bd712f2eb0f691cb7ca514b1876d6bff2f5ae61359b8db/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cdccb0e08281f0dd5d463c16ec61a06bd1534de50206dc72918be3c10dcb82e5", size = 50962, upload-time = "2025-10-12T10:48:56.494Z" }, + { url = "https://files.pythonhosted.org/packages/fb/3f/cdb99526d294c5dd4b919dc4ef492b7bd11e08b585d15ec641dfb9423493/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2b6d5e2e2fd65738d57ef65aaaed2cff2288eccff7f704bf3d579e6f451cb60a", size = 52504, upload-time = "2025-10-12T10:48:57.886Z" }, + { url = "https://files.pythonhosted.org/packages/e8/60/a8183401fa64e93e1d52b2cdf275a2c11e0993f5f3162c573a67872b535d/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7018d4197713c7797d1a67ed47ab53d4706c2e9ed134123c30a47d389dda5386", size = 50561, upload-time = "2025-10-12T10:48:58.935Z" }, + { url = "https://files.pythonhosted.org/packages/47/5e/496d6c9c309e2fe79e6a69f25f7a6d18f545edb4ea3584f461b9f84b0d60/rcssmin-1.2.2-cp314-cp314t-manylinux1_i686.whl", hash = "sha256:0162c32ce946978edc834d4fba705ac5f9422d7f556f3264cc4fc67c7ee39171", size = 51214, upload-time = "2025-10-12T10:49:00.021Z" }, + { url = "https://files.pythonhosted.org/packages/5e/78/87da6706d5856ceee71421ba831d2f5d93c3e6865acfbb56ace8d54587cc/rcssmin-1.2.2-cp314-cp314t-manylinux1_x86_64.whl", hash = "sha256:f17dc92553a46412c49f972f0ab31088032b9482a9c421bc2d39691a5d8842aa", size = 51608, upload-time = "2025-10-12T10:49:01.422Z" }, + { url = "https://files.pythonhosted.org/packages/cd/6c/204b0262c11ac2da2b8df2d8fed76f1959273fbc8376450d0ac022d754b7/rcssmin-1.2.2-cp314-cp314t-manylinux2014_aarch64.whl", hash = "sha256:40c7dfba098bbd129d8c35dd8b604275585f9dc0496e5d17dbe7fd6b873b0233", size = 53349, upload-time = "2025-10-12T10:49:02.512Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7b/9aae16756d3f33cbc512760ba3e69c3856a51aa293e463f2ca97760d1b1b/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d0197fab78ebbe33f5df9caf2572ef2d44bbe243a9130881a0c5c53ba03641fa", size = 53066, upload-time = "2025-10-12T10:49:03.589Z" }, + { url = "https://files.pythonhosted.org/packages/4e/18/b06fadfa9b85e486bb1571050217cb539c062d1ae4cd32b1a31c36f67fd4/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:19e53c58768369366fdaef00da59f275f724f229994ea885309df6ca368ff3c8", size = 54271, upload-time = "2025-10-12T10:49:04.735Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/f29ce21f8e5a1f3c19d43b67b907268d227b7edcda2ca200ca0028734a5e/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8d3de1a870e00d157f3a7b1797498fdc09a3774629079572350f75783bb94b9a", size = 52423, upload-time = "2025-10-12T10:49:06.04Z" }, +] + +[[package]] +name = "redis" +version = "7.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "rjsmin" +version = "1.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/16/14288d309d0f42c6586440c47bf6ec1a880218f698f30293fa3782db4008/rjsmin-1.2.5.tar.gz", hash = "sha256:a3f8040b0273dec773e0e807e86a4d0a9535516c0a0a35aa1bb6de6e15bb1f09", size = 427399, upload-time = "2025-10-12T10:50:27.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/ed/b472d5a3fd7d63c016893f7d438e677901fea28089b5d30cd1a115bcc887/rjsmin-1.2.5-cp314-cp314-manylinux1_i686.whl", hash = "sha256:7096357ed596fdfe0acb750f8cbfca338f3c845cc12def3861e23ed811589d15", size = 31983, upload-time = "2025-10-12T10:51:11.361Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e8/e76fa527fde17fd08288e4efef25c0aba7979ed5740eeab7bdff507bdeba/rjsmin-1.2.5-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:4e80b05803749502995fe33b6f5fd589b51dc46e50d873baf0b515c8f6e7b668", size = 32002, upload-time = "2025-10-12T10:51:12.257Z" }, + { url = "https://files.pythonhosted.org/packages/87/6c/ee395ef8ee117ba2d158a23a9502bc4a706e02f63bfdf6d01b802ae6ee9a/rjsmin-1.2.5-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:b6d0bc092acc3f54ea63ec1dcb808edaac5e956141d89fd0d038e80de5322052", size = 32435, upload-time = "2025-10-12T10:51:13.147Z" }, + { url = "https://files.pythonhosted.org/packages/1a/78/c157d33aa6148f0e8c57bb91a41969e1a4aab929f3bb0a8d9ff3b5e21556/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1e2943259be7beafdcb0847c2a901f223bf9044bdfa8105e1be1ad67d6c47795", size = 32877, upload-time = "2025-10-12T10:51:14.545Z" }, + { url = "https://files.pythonhosted.org/packages/e9/49/6252145bf85d87c815aaf441c5efdf1ce918db5ab6e915cf6d0d99ca3969/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e0387568c27fb49e55c1d0dfc27b54fc63d04b7756b1fed9743078130262907f", size = 32957, upload-time = "2025-10-12T10:51:15.964Z" }, + { url = "https://files.pythonhosted.org/packages/15/7e/c321c047b1a2fb7fa5ac818c37c1a15d348e1c12a1148de8ca5192a83b8f/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8196f1ecb0dff6c8647d4622e496869e94f1be92567ea2e941aa18d49a1a4347", size = 32456, upload-time = "2025-10-12T10:51:16.885Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d7/2d190ce5ad10832df62edd4d9b1ae7092fd259ca58b39a1e202337f511a9/rjsmin-1.2.5-cp314-cp314t-manylinux1_i686.whl", hash = "sha256:9dd9f66568be9c8676278f140aa54102fab9af7feb59adf0c7a85bef49fe70df", size = 34115, upload-time = "2025-10-12T10:51:17.911Z" }, + { url = "https://files.pythonhosted.org/packages/76/ab/e7bcf261ede4cef7a0693927d7dcd1612bb59ba6c05191f58a92deec9f01/rjsmin-1.2.5-cp314-cp314t-manylinux1_x86_64.whl", hash = "sha256:5b8f72f7d96e5e1d30a33182cb39d4eb4516ddcd9b2f984813a9eefe66f8e180", size = 33977, upload-time = "2025-10-12T10:51:18.996Z" }, + { url = "https://files.pythonhosted.org/packages/a7/75/f1ff5f2199437b534204b40aa46c55c703489063cf7806c948a1a665575e/rjsmin-1.2.5-cp314-cp314t-manylinux2014_aarch64.whl", hash = "sha256:8c5906bd8830f616e992ad5e7277d0ea12c530110da188b2b9da23e9524a7cbc", size = 34604, upload-time = "2025-10-12T10:51:20.031Z" }, + { url = "https://files.pythonhosted.org/packages/d2/dc/acd463d88c56476cc683f1c6cce893c590007dccd390747e824b8e923d63/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8207bac0d3bab7791fd667f0863b5f32e51047845179b94b28c716e6514a9234", size = 34775, upload-time = "2025-10-12T10:51:21.364Z" }, + { url = "https://files.pythonhosted.org/packages/ce/56/e6f61718d1c36e646aabe552ad1f8f77744a4c57524eaa782b5b44eba220/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1e3ab93a51d7581ba0a3b6a383df2929b86d9d55f9516764678f9b4e409826e8", size = 34682, upload-time = "2025-10-12T10:51:22.755Z" }, + { url = "https://files.pythonhosted.org/packages/00/f3/37a4672ddb1307eb57d9b54ba89a48f483a04a63cac4e1471fdb4cba76e6/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47dad1732a2c4779bdc76d5b3183fdf2ec27838f31071fa9dfcc79483d3480e2", size = 34161, upload-time = "2025-10-12T10:51:23.761Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, + { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, + { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, + { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "tzlocal" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "vine" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980, upload-time = "2023-11-05T08:46:53.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636, upload-time = "2023-11-05T08:46:51.205Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/ba/1f6e8c957e4932be060dcdc482d339c12e0216351478add3645cdaa53c05/virtualenv-21.3.3.tar.gz", hash = "sha256:f5bda277e553b1c2b3c1a8debfc30496e1288cc93ce6b7b71b3280047e317328", size = 7613784, upload-time = "2026-05-13T18:01:30.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/34/a9dbe051de88a63eb7408ea66630bac38e72f7f6077d4be58737106860d9/virtualenv-21.3.3-py3-none-any.whl", hash = "sha256:7d5987d8369e098e41406efb780a3d4ca79280097293899e351a6407ee153ab3", size = 7594554, upload-time = "2026-05-13T18:01:27.815Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132, upload-time = "2026-05-02T16:04:12.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] From cc84a7c7d2a459c6c95ca8592dc82956f75b88b8 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 13:58:10 +0200 Subject: [PATCH 02/31] dockerized --- .github/workflows/django.yml | 54 +- AHC_app/.dockerignore | 7 + AHC_app/Dockerfile-queue | 20 +- AHC_app/Dockerfile-web | 26 +- AHC_app/Pipfile | 78 -- AHC_app/Pipfile.lock | 1786 ---------------------------------- 6 files changed, 50 insertions(+), 1921 deletions(-) delete mode 100644 AHC_app/Pipfile delete mode 100644 AHC_app/Pipfile.lock diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 37ee701..fbbc571 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -8,7 +8,7 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: POSTGRES_DB: ${{ secrets.POSTGRES_DB }} POSTGRES_USER: ${{ secrets.POSTGRES_USER }} @@ -34,50 +34,24 @@ jobs: EMAIL_HOST_PASSWORD: ${{ secrets.EMAIL_HOST_PASSWORD }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} PYTHONUNBUFFERED: "1" - PYTHONPATH: "/home/runner/work/Animals_Healthcare_Application/Animals_Healthcare_Application/AHC_app" -# strategy: -# max-parallel: 4 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 + - name: Setup uv + uses: astral-sh/setup-uv@v3 with: - python-version: 3.12 - - - name: Install PostgreSQL dependencies - run: sudo apt-get install -y libpq-dev - - - name: Install pipenv - run: pip install pipenv - - - name: Prepare environment - run: | - cd ./AHC_app - pipenv install --dev - pipenv graph - - - name: Show Python version - run: python --version - working-directory: ./AHC_app + enable-cache: true + cache-dependency-glob: "AHC_app/uv.lock" + python-version: "3.14" - - name: Show pip version - run: pip --version + - name: Install dependencies + run: uv sync --no-group dev working-directory: ./AHC_app - - name: Show pipenv version - run: pipenv --version - working-directory: ./AHC_app - - - name: List installed packages - run: pipenv run pip list - working-directory: ./AHC_app - - - name: Prepare environment and run tests - run: | - PYTHONPATH=$(pwd) pipenv run python manage.py test + - name: Run tests + run: PYTHONPATH=$(pwd) uv run python manage.py test working-directory: ./AHC_app build-and-push: @@ -87,7 +61,7 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 @@ -106,8 +80,8 @@ jobs: ECR_URI: ${{ secrets.ECR_URI_APPENDIXES_DB }} IMAGE_TAG: latest run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker build -t $ECR_URI:$IMAGE_TAG . + docker push $ECR_URI:$IMAGE_TAG - name: Build, tag and push the celery-flower image to ECR id: build-image-celery-flower diff --git a/AHC_app/.dockerignore b/AHC_app/.dockerignore index 1b1aaee..1f45f28 100644 --- a/AHC_app/.dockerignore +++ b/AHC_app/.dockerignore @@ -1,2 +1,9 @@ ./tars/* *.tar +.venv/ +__pycache__/ +*.py[cod] +db.sqlite3 +static_collected/ +static/media/profile_pics/ +static/media/attachments/ diff --git a/AHC_app/Dockerfile-queue b/AHC_app/Dockerfile-queue index 0cc351b..c4ca1d3 100644 --- a/AHC_app/Dockerfile-queue +++ b/AHC_app/Dockerfile-queue @@ -1,10 +1,16 @@ -FROM python:3.12 -LABEL authors="AM" +# syntax=docker/dockerfile:1.7 +FROM python:3.14-slim AS builder WORKDIR /app -COPY Pipfile Pipfile.lock ./ - -RUN python -m pip install --upgrade pip && \ - pip install --no-cache-dir pipenv && \ - pipenv install --dev --system --deploy +RUN pip install uv --break-system-packages +COPY pyproject.toml uv.lock ./ +ENV UV_PYTHON_PREFERENCE=only-system \ + UV_PROJECT_ENVIRONMENT=/opt/venv +RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ + uv sync --no-group dev +FROM python:3.14-slim AS runtime +LABEL authors="AM" +WORKDIR /app +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" COPY . . diff --git a/AHC_app/Dockerfile-web b/AHC_app/Dockerfile-web index a664b69..cea64ab 100644 --- a/AHC_app/Dockerfile-web +++ b/AHC_app/Dockerfile-web @@ -1,13 +1,19 @@ -FROM python:3.12 -LABEL authors="AM" +# syntax=docker/dockerfile:1.7 +FROM python:3.14-slim AS builder WORKDIR /app -COPY Pipfile Pipfile.lock ./ - -RUN python -m pip install --upgrade pip && \ - pip install --no-cache-dir pipenv && \ - pipenv install --dev --system --deploy - -RUN apt-get update && \ - apt-get install -y cron +RUN pip install uv --break-system-packages +COPY pyproject.toml uv.lock ./ +ENV UV_PYTHON_PREFERENCE=only-system \ + UV_PROJECT_ENVIRONMENT=/opt/venv +RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ + uv sync --no-group dev +FROM python:3.14-slim AS runtime +LABEL authors="AM" +RUN apt-get update && \ + apt-get install -y --no-install-recommends cron && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" COPY . . diff --git a/AHC_app/Pipfile b/AHC_app/Pipfile deleted file mode 100644 index e76d50b..0000000 --- a/AHC_app/Pipfile +++ /dev/null @@ -1,78 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -amqp = "==5.1.1" -billiard = "==4.1.0" -cachetools = "==5.3.2" -certifi = "==2023.7.22" -cffi = "==1.16.0" -charset-normalizer = "==3.3.1" -click = "==8.1.7" -click-didyoumean = "==0.3.0" -click-plugins = "==1.1.1" -click-repl = "==0.3.0" -colorama = "==0.4.6" -contourpy = "==1.0.7" -crispy-bootstrap4 = "==2022.1" -cryptography = "==41.0.5" -cycler = "==0.11.0" -defusedxml = "==0.7.1" -django-appconf = "==1.0.5" -django-bootstrap-modal-forms = "==3.0.4" -django-compressor = "==4.4" -django-crispy-forms = "==2.0" -django-crontab = "==0.7.1" -django-libsass = "==0.9" -django-taggit = "==4.0.0" -django-timezone-field = "==6.0.1" -djangorestframework = "==3.14.0" -fonttools = "==4.39.4" -httplib2 = "==0.22.0" -idna = "==3.4" -kiwisolver = "==1.4.4" -kombu = "==5.3.2" -libsass = "==0.22.0" -oauthlib = "==3.2.2" -packaging = "==23.1" -pillow = "==9.5.0" -prompt-toolkit = "==3.0.39" -pyasn1 = "==0.5.0" -pyasn1-modules = "==0.3.0" -pycparser = "==2.21" -pyjwt = "==2.8.0" -pyparsing = "==3.0.9" -python-dateutil = "==2.8.2" -python3-openid = "==3.2.0" -pytz = "==2023.3" -rcssmin = "==1.1.1" -redis = "==5.0.1" -requests = "==2.31.0" -requests-oauthlib = "==1.3.1" -rjsmin = "==1.2.1" -rsa = "==4.9" -six = "==1.16.0" -sqlparse = "==0.4.4" -tzdata = "==2023.3" -urllib3 = "==2.0.7" -vine = "==5.0.0" -wcwidth = "==0.2.8" -icecream = "*" -flower = "*" -django-cron = "*" -python-decouple = "*" -pycouchdb = "*" -discord = "*" -psycopg2-binary = "*" -django = "==4.2.11" -celery = "5.3.4" - -[dev-packages] -pre-commit = "*" -isort = "*" -black = "*" - -[requires] -python_version = "3.12" diff --git a/AHC_app/Pipfile.lock b/AHC_app/Pipfile.lock deleted file mode 100644 index ddf2690..0000000 --- a/AHC_app/Pipfile.lock +++ /dev/null @@ -1,1786 +0,0 @@ -{ - "_meta": { - "hash": { - - "sha256": "adc7b4e1a58ad54950df7b0133e5a7ddaa4bd8dbcb727885188ac9528a5c3c24" - - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.12" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "aiohttp": { - "hashes": [ - - "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8", - "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c", - "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475", - "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed", - "sha256:18f634d540dd099c262e9f887c8bbacc959827cfe5da7a0e2e1cf3f14dbf2daf", - "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372", - "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81", - "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f", - "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1", - "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd", - "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a", - "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb", - "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46", - "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de", - "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78", - "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c", - "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771", - "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb", - "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430", - "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233", - "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156", - "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9", - "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59", - "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888", - "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c", - "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c", - "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da", - "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424", - "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2", - "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb", - "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8", - "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a", - "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10", - "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0", - "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09", - "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031", - "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4", - "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3", - "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa", - "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a", - "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe", - "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a", - "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2", - "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1", - "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323", - "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b", - "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b", - "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106", - "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac", - "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6", - "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832", - "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75", - "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6", - "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d", - "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72", - "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db", - "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a", - "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da", - "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678", - "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b", - "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24", - "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed", - "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f", - "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e", - "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58", - "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a", - "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342", - "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558", - "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2", - "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551", - "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595", - "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee", - "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11", - "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d", - "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7", - "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f" - ], - "markers": "python_version >= '3.8'", - "version": "==3.9.5" - - }, - "aiosignal": { - "hashes": [ - "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", - "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, - "amqp": { - "hashes": [ - "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2", - "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==5.1.1" - }, - "asgiref": { - "hashes": [ - "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", - "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590" - ], - - "markers": "python_version >= '3.8'", - "version": "==3.8.1" - - }, - "asttokens": { - "hashes": [ - "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", - "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" - ], - "version": "==2.4.1" - }, - "attrs": { - "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2.0" - }, - "billiard": { - "hashes": [ - "sha256:0f50d6be051c6b2b75bfbc8bfd85af195c5739c281d3f5b86a5640c65563614a", - "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2", - "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.2" - }, - "celery": { - "hashes": [ - "sha256:1e6ed40af72695464ce98ca2c201ad0ef8fd192246f6c9eac8bba343b980ad34", - "sha256:9023df6a8962da79eb30c0c84d5f4863d9793a466354cc931d7f72423996de28" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.3.4" - }, - "certifi": { - "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5", - "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93", - "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a", - "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d", - "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c", - "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1", - "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58", - "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2", - "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557", - "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147", - "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041", - "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2", - "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2", - "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7", - "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296", - "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690", - "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67", - "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57", - "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597", - "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846", - "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b", - "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97", - "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c", - "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62", - "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa", - "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f", - "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e", - "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821", - "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3", - "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4", - "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb", - "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727", - "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514", - "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d", - "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761", - "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55", - "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f", - "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c", - "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034", - "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6", - "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae", - "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1", - "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14", - "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1", - "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228", - "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708", - "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48", - "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f", - "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5", - "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f", - "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4", - "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8", - "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff", - "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61", - "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b", - "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97", - "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b", - "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605", - "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728", - "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d", - "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c", - "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf", - "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673", - "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1", - "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b", - "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41", - "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8", - "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f", - "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4", - "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008", - "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9", - "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5", - "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f", - "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e", - "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273", - "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45", - "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e", - "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656", - "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e", - "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c", - "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2", - "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72", - "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056", - "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397", - "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42", - "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd", - "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3", - "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213", - "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf", - "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67" - ], - "index": "pypi", - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.1" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "click-didyoumean": { - "hashes": [ - "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667", - "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035" - ], - "index": "pypi", - "markers": "python_full_version >= '3.6.2' and python_full_version < '4.0.0'", - "version": "==0.3.0" - }, - "click-plugins": { - "hashes": [ - "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", - "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8" - ], - "index": "pypi", - "version": "==1.1.1" - }, - "click-repl": { - "hashes": [ - "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", - "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.3.0" - }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, - "contourpy": { - "hashes": [ - "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98", - "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772", - "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2", - "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc", - "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803", - "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051", - "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc", - "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4", - "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436", - "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5", - "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5", - "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3", - "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80", - "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1", - "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0", - "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae", - "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556", - "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02", - "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566", - "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350", - "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967", - "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4", - "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66", - "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69", - "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd", - "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2", - "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810", - "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50", - "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc", - "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2", - "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0", - "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3", - "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6", - "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac", - "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d", - "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6", - "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f", - "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd", - "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566", - "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa", - "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414", - "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a", - "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c", - "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693", - "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d", - "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161", - "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e", - "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2", - "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f", - "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71", - "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd", - "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9", - "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8", - "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab", - "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.0.7" - }, - "couchdb": { - "hashes": [ - "sha256:1386a1a43f25bed3667e3b805222054940d674fa1967fa48e9d2012a18630ab7", - "sha256:13a28a1159c49f8346732e8724b9a4d65cba54bec017c4a7eeb1499fe88151d1" - ], - "index": "pypi", - "version": "==1.2" - }, - "crispy-bootstrap4": { - "hashes": [ - "sha256:5241ab1dc2188c95560aa786439bcbedec7416e6b0f5a52dded82380810367ec", - "sha256:f18386c4d96180c1bb1212487d6e8f5a3dd1a9bb58d3032183973d4d130b55f9" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2022.1" - }, - "cryptography": { - "hashes": [ - "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", - "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84", - "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", - "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", - "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", - "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", - "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", - "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", - "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", - "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81", - "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", - "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", - "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d", - "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", - "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", - "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", - "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", - "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936", - "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", - "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", - "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", - "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", - "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==41.0.5" - }, - "cycler": { - "hashes": [ - "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3", - "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.11.0" - }, - "defusedxml": { - "hashes": [ - "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", - "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.7.1" - }, - "discord": { - "hashes": [ - "sha256:cc1ee2dbe6df218ca51519af355b97e87309f8230f58c7f34885feb8e8a76145", - "sha256:d7959418799dd3b1e896685812d880169c193468b061b3431fa2a4664febd3da" - ], - "index": "pypi", - "version": "==2.3.2" - }, - "discord.py": { - "hashes": [ - "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c", - "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==2.3.2" - }, - "django": { - "hashes": [ - "sha256:6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4", - "sha256:ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.2.11" - }, - "django-ajax": { - "hashes": [ - "sha256:f45a4c5b0240a0c5e0e088e056e71ec3b381bbd8fa2e6b2bc307c9ff6b64bf33" - ], - "index": "pypi", - "version": "==0.3.0" - }, - "django-appconf": { - "hashes": [ - "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d", - "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==1.0.5" - }, - "django-bootstrap-modal-forms": { - "hashes": [ - "sha256:47a4f303724182a5e4958da94772f5a6a7359fac6d6ae204442aadfd56c03133", - "sha256:ff33b9b89608c8bcb9eba811524e9fb7ec1c843a73bd78a7d38fbb94cc9bdd4a" - ], - "index": "pypi", - "version": "==3.0.4" - }, - "django-compressor": { - "hashes": [ - "sha256:1b0acc9cfba9f69bc38e7c41da9b0d70a20bc95587b643ffef9609cf46064f67", - "sha256:6e2b0c0becb9607f5099c2546a824c5b84a6918a34bc37a8a622ffa250313596" - ], - "index": "pypi", - "version": "==4.4" - }, - "django-crispy-forms": { - "hashes": [ - "sha256:90193b068bf948d9c68449bc8260afed1a8e2afe11ee0bac8c4ebfaeb175b322", - "sha256:d1d4e585929058a9ab3b797666ea5b69320b9ba7937f9d146d32173246a6fd13" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0" - }, - "django-cron": { - "hashes": [ - "sha256:016203554748512b7f19d7363b4fde8741c6ff63fe8a15051f3031f4a0506a41", - "sha256:dc3c0d3433a2e4e7012f77f6d8415ad90367ba068649db2674325bc36f935841" - ], - "index": "pypi", - "version": "==0.6.0" - }, - "django-crontab": { - "hashes": [ - "sha256:1201810a212460aaaa48eb6a766738740daf42c1a4f6aafecfb1525036929236" - ], - "index": "pypi", - "version": "==0.7.1" - }, - "django-libsass": { - "hashes": [ - "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97", - "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5" - ], - "index": "pypi", - "version": "==0.9" - }, - "django-taggit": { - "hashes": [ - "sha256:4d52de9d37245a9b9f98c0ec71fdccf1d2283e38e8866d40a7ae6a3b6787a161", - "sha256:eb800dabef5f0a4e047ab0751f82cf805bc4a9e972037ef12bf519f52cd92480" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==4.0.0" - }, - "django-timezone-field": { - "hashes": [ - "sha256:916d0fd924443462f099f02122cc38d6a6e901ea17f1206c343836199df8bc49", - "sha256:ed28d3ff8e3500f2bc173cdf1aab7a3244ef607d06ad890611512de1bae6074d" - ], - "index": "pypi", - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==6.0.1" - }, - "djangorestframework": { - "hashes": [ - "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8", - "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==3.14.0" - }, - "executing": { - "hashes": [ - "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147", - "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc" - ], - "markers": "python_version >= '3.5'", - "version": "==2.0.1" - }, - "flower": { - "hashes": [ - "sha256:5ab717b979530770c16afb48b50d2a98d23c3e9fe39851dcf6bc4d01845a02a0", - "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.1" - }, - "fonttools": { - "hashes": [ - "sha256:106caf6167c4597556b31a8d9175a3fdc0356fdcd70ab19973c3b0d4c893c461", - "sha256:dba8d7cdb8e2bac1b3da28c5ed5960de09e59a2fe7e63bb73f5a59e57b0430d2" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.39.4" - }, - "frozenlist": { - "hashes": [ - "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", - "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", - "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", - "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5", - "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", - "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", - "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", - "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701", - "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d", - "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6", - "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", - "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", - "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", - "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", - "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a", - "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0", - "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", - "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826", - "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", - "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6", - "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", - "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", - "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0", - "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", - "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", - "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09", - "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", - "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", - "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", - "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b", - "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b", - "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d", - "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", - "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea", - "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", - "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", - "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897", - "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7", - "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", - "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9", - "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", - "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", - "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742", - "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09", - "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", - "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932", - "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", - "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", - "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", - "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d", - "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7", - "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", - "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", - "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e", - "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", - "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", - "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb", - "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", - "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8", - "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", - "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", - "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", - "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11", - "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", - "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", - "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0", - "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497", - "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", - "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0", - "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", - "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", - "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", - "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", - "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", - "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887", - "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", - "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74" - ], - "markers": "python_version >= '3.8'", - "version": "==1.4.1" - }, - "httplib2": { - "hashes": [ - "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", - "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.22.0" - }, - "humanize": { - "hashes": [ - "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", - "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" - ], - "markers": "python_version >= '3.8'", - "version": "==4.9.0" - }, - "icecream": { - "hashes": [ - "sha256:0aa4a7c3374ec36153a1d08f81e3080e83d8ac1eefd97d2f4fe9544e8f9b49de", - "sha256:757aec31ad4488b949bc4f499d18e6e5973c40cc4d4fc607229e78cfaec94c34" - ], - "index": "pypi", - "version": "==2.1.3" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "kiwisolver": { - "hashes": [ - "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b", - "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166", - "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c", - "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c", - "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0", - "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4", - "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9", - "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286", - "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767", - "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c", - "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6", - "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b", - "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004", - "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf", - "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494", - "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac", - "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626", - "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766", - "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514", - "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6", - "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f", - "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d", - "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191", - "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d", - "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51", - "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f", - "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8", - "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454", - "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb", - "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da", - "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8", - "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de", - "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a", - "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9", - "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008", - "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3", - "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32", - "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938", - "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1", - "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9", - "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d", - "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824", - "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b", - "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd", - "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2", - "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5", - "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69", - "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3", - "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae", - "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597", - "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e", - "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955", - "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca", - "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a", - "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea", - "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede", - "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4", - "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6", - "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686", - "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408", - "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871", - "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29", - "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750", - "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897", - "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0", - "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2", - "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09", - "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.4.4" - }, - "kombu": { - "hashes": [ - "sha256:0ba213f630a2cb2772728aef56ac6883dc3a2f13435e10048f6e97d48506dbbd", - "sha256:b753c9cfc9b1e976e637a7cbc1a65d446a22e45546cd996ea28f932082b7dc9e" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.3.2" - }, - "libsass": { - "hashes": [ - "sha256:081e256ab3c5f3f09c7b8dea3bf3bf5e64a97c6995fd9eea880639b3f93a9f9a", - "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425", - "sha256:5fb2297a4754a6c8e25cfe5c015a3b51a2b6b9021b333f989bb8ce9d60eb5828", - "sha256:65455a2728b696b62100eb5932604aa13a29f4ac9a305d95773c14aaa7200aaf", - "sha256:89c5ce497fcf3aba1dd1b19aae93b99f68257e5f2026b731b00a872f13324c7f", - "sha256:f1efc1b612299c88aec9e39d6ca0c266d360daa5b19d9430bdeaffffa86993f9" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.22.0" - }, - "multidict": { - "hashes": [ - "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", - "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c", - "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", - "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b", - "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8", - "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", - "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd", - "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40", - "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", - "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3", - "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c", - "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9", - "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5", - "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae", - "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442", - "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9", - "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", - "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", - "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", - "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", - "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50", - "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182", - "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453", - "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e", - "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600", - "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", - "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", - "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241", - "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461", - "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", - "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", - "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b", - "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e", - "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7", - "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386", - "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", - "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9", - "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf", - "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee", - "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5", - "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a", - "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", - "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54", - "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", - "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", - "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", - "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319", - "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", - "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f", - "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527", - "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", - "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604", - "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", - "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8", - "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", - "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5", - "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626", - "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c", - "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d", - "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c", - "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc", - "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc", - "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", - "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38", - "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", - "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1", - "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", - "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3", - "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755", - "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", - "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a", - "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046", - "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", - "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479", - "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", - "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", - "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", - "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83", - "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929", - "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93", - "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a", - "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c", - "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44", - "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89", - "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", - "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e", - "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", - "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", - "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423", - "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef" - ], - "markers": "python_version >= '3.7'", - "version": "==6.0.5" - }, - "numpy": { - "hashes": [ - "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", - "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", - "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", - "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", - "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", - "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", - "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", - "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", - "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", - "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", - "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", - "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", - "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", - "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", - "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", - "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", - "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", - "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", - "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", - "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", - "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", - "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", - "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", - "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", - "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", - "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", - "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", - "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", - "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", - "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", - "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", - "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", - "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", - "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", - "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", - "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" - ], - "markers": "python_version >= '3.9'", - "version": "==1.26.4" - }, - "oauthlib": { - "hashes": [ - "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", - "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==3.2.2" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pillow": { - "hashes": [ - "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1", - "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba", - "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a", - "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799", - "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51", - "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb", - "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5", - "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270", - "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6", - "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47", - "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf", - "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e", - "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b", - "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66", - "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865", - "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec", - "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c", - "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1", - "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38", - "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906", - "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705", - "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef", - "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc", - "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f", - "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf", - "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392", - "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d", - "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe", - "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32", - "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5", - "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7", - "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44", - "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d", - "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3", - "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625", - "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e", - "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829", - "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089", - "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3", - "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78", - "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96", - "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964", - "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597", - "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99", - "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a", - "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140", - "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7", - "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16", - "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903", - "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1", - "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296", - "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572", - "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115", - "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a", - "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd", - "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4", - "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1", - "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb", - "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa", - "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a", - "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569", - "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c", - "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf", - "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082", - "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062", - "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==9.5.0" - }, - "prometheus-client": { - "hashes": [ - "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89", - "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" - }, - "prompt-toolkit": { - "hashes": [ - "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac", - "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88" - ], - "index": "pypi", - "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.39" - }, - "psycopg2": { - "hashes": [ - "sha256:11aca705ec888e4f4cea97289a0bf0f22a067a32614f6ef64fcf7b8bfbc53744", - "sha256:1861a53a6a0fd248e42ea37c957d36950da00266378746588eab4f4b5649e95f", - "sha256:2362ee4d07ac85ff0ad93e22c693d0f37ff63e28f0615a16b6635a645f4b9214", - "sha256:36c941a767341d11549c0fbdbb2bf5be2eda4caf87f65dfcd7d146828bd27f39", - "sha256:53f4ad0a3988f983e9b49a5d9765d663bbe84f508ed655affdb810af9d0972ad", - "sha256:869776630c04f335d4124f120b7fb377fe44b0a7645ab3c34b4ba42516951889", - "sha256:a8ad4a47f42aa6aec8d061fdae21eaed8d864d4bb0f0cade5ad32ca16fcd6258", - "sha256:b81fcb9ecfc584f661b71c889edeae70bae30d3ef74fa0ca388ecda50b1222b7", - "sha256:d24ead3716a7d093b90b27b3d73459fe8cd90fd7065cf43b3c40966221d8c394", - "sha256:ded2faa2e6dfb430af7713d87ab4abbfc764d8d7fb73eafe96a24155f906ebf5", - "sha256:f15158418fd826831b28585e2ab48ed8df2d0d98f502a2b4fe619e7d5ca29011", - "sha256:f75001a1cbbe523e00b0ef896a5a1ada2da93ccd752b7636db5a99bc57c44494", - "sha256:f7a7a5ee78ba7dc74265ba69e010ae89dae635eea0e97b055fb641a01a31d2b1" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==2.9.6" - }, - "psycopg2-binary": { - "hashes": [ - "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", - "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77", - "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", - "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", - "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3", - "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", - "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67", - "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876", - "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152", - "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", - "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a", - "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6", - "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503", - "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f", - "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", - "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", - "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f", - "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e", - "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59", - "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94", - "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7", - "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", - "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420", - "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae", - "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291", - "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", - "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980", - "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", - "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692", - "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", - "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716", - "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472", - "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", - "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", - "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", - "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", - "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5", - "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", - "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984", - "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", - "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", - "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", - "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", - "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", - "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", - "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", - "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90", - "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", - "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", - "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", - "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", - "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", - "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", - "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", - "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", - "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", - "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", - "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", - "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", - "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1", - "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd", - "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", - "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", - "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55", - "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", - "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972", - "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f", - "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", - "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", - "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957", - "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", - "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.9.9" - }, - "pyasn1": { - "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pycouchdb": { - "hashes": [ - "sha256:00bc6c7dd4744895920b9153cb9537bc5ecaff72002ea10209c05580e186e0eb", - "sha256:434758aa6b49c6a67fcf89e71a11c36e2ec2df51e2998c38bcd570eab94cc288" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.14.2" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "index": "pypi", - "version": "==2.21" - }, - "pygments": { - "hashes": [ - "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", - "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" - ], - "markers": "python_version >= '3.8'", - "version": "==2.18.0" - }, - "pyjwt": { - "hashes": [ - "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", - "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.8.0" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "index": "pypi", - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-decouple": { - "hashes": [ - "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f", - "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66" - ], - "index": "pypi", - "version": "==3.8" - }, - "python3-openid": { - "hashes": [ - "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf", - "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b" - ], - "index": "pypi", - "version": "==3.2.0" - }, - "pytz": { - "hashes": [ - "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588", - "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb" - ], - "index": "pypi", - "version": "==2023.3" - }, - "rcssmin": { - "hashes": [ - "sha256:271e3d2f8614a6d4637ed8fff3d90007f03e2a654cd9444f37d888797662ba72", - "sha256:35da6a6999e9e2c5b0e691b42ed56cc479373e0ecab33ef5277dfecce625e44a", - "sha256:42576d95dfad53d77df2e68dfdec95b89b10fad320f241f1af3ca1438578254a", - "sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64", - "sha256:705c9112d0ed54ea40aecf97e7fd29bdf0f1c46d278a32d8f957f31dde90778a", - "sha256:79421230dd67c37ec61ed9892813d2b839b68f2f48ef55c75f976e81701d60b4", - "sha256:868215e1fd0e92a6122e0ed5973dfc7bb8330fe1e92274d05b2585253b38c0ca", - "sha256:8a26fec3c1e6b7a3765ccbaccc20fbb5c0ed3422cc381e01a2607f08d7621c44", - "sha256:8fcfd10ae2a1c4ce231a33013f2539e07c3836bf17cc945cc25cc30bf8e68e45", - "sha256:908fe072efd2432fb0975a61124609a8e05021367f6a3463d45f5e3e74c4fdda", - "sha256:914e589f40573035006913861ed2adc28fbe70082a8b6bff5be7ee430b7b5c2e", - "sha256:a04d58a2a21e9a089306d3f99c4b12bf5b656a79c198ef2321e80f8fd9afab06", - "sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019", - "sha256:c30f8bc839747b6da59274e0c6e4361915d66532e26448d589cb2b1846d7bf11", - "sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877", - "sha256:c7728e3b546b1b6ea08cab721e8e21409dbcc11b881d0b87d10b0be8930af2a2", - "sha256:cf74d7ea5e191f0f344b354eed8b7c83eeafbd9a97bec3a579c3d26edf11b005", - "sha256:d0afc6e7b64ef30d6dcde88830ec1a237b9f16a39f920a8fd159928684ccf8db", - "sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1", - "sha256:e923c105100ab70abde1c01d3196ddd6b07255e32073685542be4e3a60870c8e", - "sha256:ee386bec6d62f8c814d65c011d604a7c82d24aa3f718facd66e850eea8d6a5a1", - "sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f", - "sha256:f7a1fcdbafaacac0530da04edca4a44303baab430ea42e7d59aece4b3f3e9a51" - ], - "index": "pypi", - "version": "==1.1.1" - }, - "redis": { - "hashes": [ - "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", - "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.1" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5", - "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.3.1" - }, - "rjsmin": { - "hashes": [ - "sha256:113132a40ce7d03b2ced4fac215f0297338ed1c207394b739266efab7831988b", - "sha256:122aa52bcf7ad9f12728d309012d1308c6ecfe4d6b09ea867a110dcad7b7728c", - "sha256:145c6af8df42d8af102d0d39a6de2e5fa66aef9e38947cfb9d65377d1b9940b2", - "sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411", - "sha256:3453ee6d5e7a2723ec45c2909e2382371783400e8d51952b692884c6d850a3d0", - "sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1", - "sha256:35f21046504544e2941e04190ce24161255479133751550e36ddb3f4af0ecdca", - "sha256:5d67ec09da46a492186e35cabca02a0d092eda5ef5b408a419b99ee4acf28d5c", - "sha256:747bc9d3bc8a220f40858e6aad50b2ae2eb7f69c924d4fa3803b81be1c1ddd02", - "sha256:7dd58b5ed88233bc61dc80b0ed87b93a1786031d9977c70d335221ef1ac5581a", - "sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064", - "sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39", - "sha256:8c340e251619c97571a5ade20f147f1f7e8664f66a2d6d7319e05e3ef6a4423c", - "sha256:99c074cd6a8302ff47118a9c3d086f89328dc8e5c4b105aa1f348fb85c765a30", - "sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346", - "sha256:bbd7a0abaa394afd951f5d4e05249d306fec1c9674bfee179787674dddd0bdb7", - "sha256:bc5bc2f94e59bc81562c572b7f1bdd6bcec4f61168dc68a2993bad2d355b6e19", - "sha256:bd1faedc425006d9e86b23837d164f01d105b7a8b66b767a9766d0014773db2a", - "sha256:ca90630b84fe94bb07739c3e3793e87d30c6ee450dde08653121f0d9153c8d0d", - "sha256:d332e44a1b21ad63401cc7eebc81157e3d982d5fb503bb4faaea5028068d71e9", - "sha256:eb770aaf637919b0011c4eb87b9ac6317079fb9800eb17c90dda05fc9de4ebc3", - "sha256:f0895b360dccf7e2d6af8762a52985e3fbaa56778de1bf6b20dbc96134253807", - "sha256:f7cd33602ec0f393a0058e883284496bb4dbbdd34e0bbe23b594c8933ddf9b65" - ], - "index": "pypi", - "version": "==1.2.1" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "index": "pypi", - "markers": "python_version >= '3.6' and python_version < '4'", - "version": "==4.9" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sqlparse": { - "hashes": [ - "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3", - "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==0.4.4" - }, - "tornado": { - "hashes": [ - "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0", - "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63", - "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263", - "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052", - "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f", - "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee", - "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78", - "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579", - "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212", - "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e", - "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2" - ], - "markers": "python_version >= '3.8'", - "version": "==6.4" - }, - "tzdata": { - "hashes": [ - "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a", - "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda" - ], - "index": "pypi", - "markers": "python_version >= '2'", - "version": "==2023.3" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "vine": { - "hashes": [ - "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", - "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==5.0.0" - }, - "wcwidth": { - "hashes": [ - "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704", - "sha256:8705c569999ffbb4f6a87c6d1b80f324bd6db952f5eb0b95bc07517f4c1813d4" - ], - "index": "pypi", - "version": "==0.2.8" - }, - "yarl": { - "hashes": [ - "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", - "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", - "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559", - "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", - "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", - "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", - "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4", - "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c", - "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130", - "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136", - "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e", - "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec", - "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7", - "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1", - "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455", - "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", - "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", - "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", - "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", - "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", - "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa", - "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", - "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", - "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", - "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", - "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c", - "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", - "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b", - "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", - "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23", - "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd", - "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27", - "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f", - "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece", - "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434", - "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec", - "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", - "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78", - "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d", - "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863", - "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53", - "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", - "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15", - "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5", - "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b", - "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57", - "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3", - "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1", - "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f", - "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", - "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c", - "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", - "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", - "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b", - "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2", - "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b", - "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", - "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be", - "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e", - "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", - "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", - "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", - "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2", - "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392", - "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91", - "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541", - "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf", - "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", - "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66", - "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575", - "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14", - "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5", - "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", - "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e", - "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551", - "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17", - "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead", - "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", - "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", - "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234", - "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0", - "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7", - "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34", - "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", - "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385", - "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", - "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be", - "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", - "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749", - "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.4" - } - }, - "develop": { - "black": { - "hashes": [ - - "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", - "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1", - "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0", - "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8", - "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", - "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1", - "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04", - "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", - "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94", - "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d", - "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c", - "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7", - "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", - "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc", - "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7", - "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", - "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", - "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741", - "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", - "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", - "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", - "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==24.4.2" - - }, - "cfgv": { - "hashes": [ - "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", - "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560" - ], - "markers": "python_version >= '3.8'", - "version": "==3.4.0" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, - "distlib": { - "hashes": [ - "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", - "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64" - ], - "version": "==0.3.8" - }, - "filelock": { - "hashes": [ - "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f", - "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a" - ], - "markers": "python_version >= '3.8'", - "version": "==3.14.0" - }, - "identify": { - "hashes": [ - - "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa", - "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d" - ], - "markers": "python_version >= '3.8'", - "version": "==2.5.36" - - }, - "isort": { - "hashes": [ - "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", - "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6" - ], - "index": "pypi", - "markers": "python_full_version >= '3.8.0'", - "version": "==5.13.2" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "nodeenv": { - "hashes": [ - "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", - "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.8.0" - }, - "packaging": { - "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==23.1" - }, - "pathspec": { - "hashes": [ - "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", - "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" - ], - "markers": "python_version >= '3.8'", - "version": "==0.12.1" - }, - "platformdirs": { - "hashes": [ - - "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf", - "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1" - ], - "markers": "python_version >= '3.8'", - "version": "==4.2.1" - }, - "pre-commit": { - "hashes": [ - "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab", - "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==3.7.0" - - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "setuptools": { - "hashes": [ - - "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", - "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" - ], - "markers": "python_version >= '3.8'", - "version": "==69.5.1" - }, - "virtualenv": { - "hashes": [ - "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b", - "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75" - ], - "markers": "python_version >= '3.7'", - "version": "==20.26.1" - - } - } -} From 5dceb8602ee2012ce094ae121a42d430565124bb Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 19:53:44 +0200 Subject: [PATCH 03/31] refactor(toolchain): swap lint stack to ruff, codespell and bandit --- .gitattributes | 4 + .gitignore | 5 +- .pre-commit-config.yaml | 41 ++++ AHC_app/.pre-commit-config.yaml | 29 --- AHC_app/AHC_app/celery_notifications/cron.py | 9 +- .../utils/discord_utils.py | 3 +- .../utils/sending_utils.py | 7 +- AHC_app/AHC_app/settings.py | 6 +- AHC_app/AHC_app/urls.py | 4 +- AHC_app/animals/forms.py | 3 +- AHC_app/animals/migrations/0001_initial.py | 16 +- .../mixins/animal_owner_permissions.py | 3 +- AHC_app/animals/models.py | 1 + AHC_app/animals/signals.py | 3 +- .../templates/animals/all_animals_stable.html | 2 +- .../animals/animal_confirm_delete.html | 2 +- .../templates/animals/change_birthday.html | 2 +- .../animals/change_first_contact.html | 2 +- AHC_app/animals/templates/animals/image.html | 2 +- .../animals/templatetags/custom_timesince.py | 17 +- AHC_app/animals/urls.py | 7 +- AHC_app/animals/utils_owner/forms.py | 8 +- AHC_app/animals/utils_owner/views.py | 13 +- AHC_app/animals/views.py | 5 +- AHC_app/homepage/admin.py | 1 + AHC_app/homepage/management/__init__.py | 2 +- .../management/commands/sync_cronjobs.py | 1 + AHC_app/homepage/migrations/0001_initial.py | 3 +- .../0002_alter_profilebackground_content.py | 3 +- AHC_app/homepage/models.py | 3 +- AHC_app/homepage/tests/test_homepage.py | 1 + AHC_app/homepage/urls.py | 1 + AHC_app/homepage/views.py | 4 +- AHC_app/manage.py | 1 + AHC_app/medical_notes/admin.py | 2 - .../medical_notes/forms/type_basic_note.py | 6 +- .../medical_notes/forms/type_feeding_notes.py | 5 +- .../forms/type_measurement_notes.py | 14 +- .../medical_notes/migrations/0001_initial.py | 16 +- .../0005_medicalrecordattachment.py | 5 +- ...notification_last_modification_and_more.py | 1 - ...notification_last_modification_and_more.py | 1 - ...011_medicalrecordattachment_description.py | 1 - ...icalrecordattachment_file_name_and_more.py | 1 - ...ve_medicalrecordattachment_url_and_more.py | 1 - ...notification_last_modification_and_more.py | 1 - .../medical_notes/models/type_basic_note.py | 3 +- .../models/type_feeding_notes.py | 3 +- .../models/type_measurement_notes.py | 19 +- .../signals/type_feeding_notes.py | 1 + .../signals/type_measurement_notes.py | 1 + .../templatetags/custom_file_name.py | 3 +- AHC_app/medical_notes/tests.py | 2 - AHC_app/medical_notes/urls.py | 3 +- .../medical_notes/views/type_basic_note.py | 13 +- .../medical_notes/views/type_feeding_notes.py | 5 +- .../views/type_measurement_notes.py | 9 +- AHC_app/pyproject.toml | 31 ++- AHC_app/users/admin.py | 1 + AHC_app/users/forms.py | 1 + ...0002_profile_allow_recennt_animals_list.py | 1 - .../migrations/0003_profile_pinned_animals.py | 1 - AHC_app/users/models.py | 3 +- AHC_app/users/signals.py | 3 +- AHC_app/users/templates/users/login.html | 2 +- .../users/templates/users/login_success.html | 2 +- .../users/templates/users/password_reset.html | 6 +- .../users/password_reset_complete.html | 4 +- .../users/password_reset_confirm.html | 10 +- .../templates/users/password_reset_done.html | 2 +- .../templates/users/password_reset_email.html | 2 +- AHC_app/users/tests.py | 2 - AHC_app/users/urls.py | 5 +- AHC_app/users/views.py | 1 + AHC_app/uv.lock | 195 +++++++++++------- README.md | 4 +- doc/01_adr_functionality.md | 14 +- doc/02_adr_django.md | 14 +- doc/03_adr_monolit.md | 12 +- doc/04_adr_monorepo.md | 14 +- doc/05_adr_matlibplot.md | 12 +- doc/06_adr_html_template.md | 8 +- doc/07_adr_drf.md | 10 +- doc/08_adr_databases.md | 18 +- doc/09_adr_user_data.md | 24 +-- doc/10_adr_notification_trigger.md | 10 +- 86 files changed, 407 insertions(+), 335 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 100644 AHC_app/.pre-commit-config.yaml diff --git a/.gitattributes b/.gitattributes index dfe0770..287d346 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ # Auto detect text files and perform LF normalization * text=auto + +# Vendor static files — preserve LF line endings as published +AHC_app/static/**/* text eol=lf +AHC_app/static_collected/**/* text eol=lf diff --git a/.gitignore b/.gitignore index e6dfafc..60345ef 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,9 @@ celerybeat.pid .dmypy.json .ty_cache/ -# Claude Code — local settings are machine-specific -.claude/settings.local.json +# Claude Code — not tracked in this repository +.claude/ +CLAUDE.md # IDE .idea/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1a9f553 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,41 @@ +exclude: 'AHC_app/static/|AHC_app/static_collected/' + +repos: +- repo: local + hooks: + - id: uv-lock-check + name: uv lock --check + entry: uv --directory AHC_app lock --check + language: system + pass_filenames: false + files: ^AHC_app/pyproject\.toml$ + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: end-of-file-fixer + exclude: '\.sh$' + - id: trailing-whitespace + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.14 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + +- repo: https://github.com/codespell-project/codespell + rev: v2.4.2 + hooks: + - id: codespell + types_or: [python, markdown, yaml] + +- repo: local + hooks: + - id: bandit + name: bandit + entry: bandit -r AHC_app -c AHC_app/pyproject.toml + language: python + additional_dependencies: ["bandit[toml]==1.9.4"] + pass_filenames: false + always_run: true diff --git a/AHC_app/.pre-commit-config.yaml b/AHC_app/.pre-commit-config.yaml deleted file mode 100644 index 7f61623..0000000 --- a/AHC_app/.pre-commit-config.yaml +++ /dev/null @@ -1,29 +0,0 @@ -repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: end-of-file-fixer - exclude: '\.sh$' - - id: trailing-whitespace -- repo: https://github.com/PyCQA/autoflake - rev: v2.1.1 - hooks: - - id: autoflake - name: autoflake - entry: autoflake - language: python - types: [python] - require_serial: true - args: ['--in-place', '--remove-all-unused-imports'] - exclude: '^requirements\.txt$|^setup_couchdb\.sh$|^.yaml$' -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - args: ['--profile', 'black', '--lines-between-types', '1', '--filter-files'] -- repo: https://github.com/psf/black - rev: 22.3.0 - hooks: - - id: black - language_version: python3.12 - args: ['--line-length', '120'] diff --git a/AHC_app/AHC_app/celery_notifications/cron.py b/AHC_app/AHC_app/celery_notifications/cron.py index a46ed4c..4a197fa 100644 --- a/AHC_app/AHC_app/celery_notifications/cron.py +++ b/AHC_app/AHC_app/celery_notifications/cron.py @@ -3,26 +3,25 @@ import logging.config import logging.handlers import pathlib - from datetime import date, datetime, time, timedelta from functools import wraps from django.db.models import Q, QuerySet from django.utils import timezone -from medical_notes.models.type_feeding_notes import EmailNotification from AHC_app.celery_notifications.config import ( send_discord_notifications, send_email_notifications, ) from AHC_app.celery_notifications.utils.example_task import send_mail_fnc +from medical_notes.models.type_feeding_notes import EmailNotification logger = logging.getLogger("crons_logger") def setup_logging(): config_file = pathlib.Path("AHC_app/celery_notifications/logger_config.json") - with open(config_file, "r") as file: + with open(config_file) as file: config = json.load(file) logging.config.dictConfig(config) @@ -85,7 +84,7 @@ def send_emails() -> None: return None for notification in notifications_to_send: - user_set_zone: str = notification.timezone + _user_set_zone: str = notification.timezone # user_weekday_number: int = datetime.now( # tz=pytz.timezone(user_set_zone) # ).weekday() @@ -103,7 +102,7 @@ def send_emails() -> None: # "note_edit", kwargs={"pk": notification.related_note.id} # ) note_url: str = "" - center: str = f"{message} \n\n " f"For further information:\n{note_url}" + center: str = f"{message} \n\n For further information:\n{note_url}" sender: str = notification.related_note.related_note.author footer: str = f"Best regards \n{sender}" diff --git a/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py b/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py index 87da4e7..c7585f0 100644 --- a/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py +++ b/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py @@ -2,10 +2,9 @@ from discord import Client, Intents from discord.ext import commands -from django.conf import settings # TOKEN = settings.DISCORD_TOKEN -TOKEN = "none" +TOKEN = "none" # nosec B105 def send_via_discord(user_id: int, user_message: str) -> None: diff --git a/AHC_app/AHC_app/celery_notifications/utils/sending_utils.py b/AHC_app/AHC_app/celery_notifications/utils/sending_utils.py index 3d8caea..fe3de81 100644 --- a/AHC_app/AHC_app/celery_notifications/utils/sending_utils.py +++ b/AHC_app/AHC_app/celery_notifications/utils/sending_utils.py @@ -9,8 +9,8 @@ def standardize_message_size(message: str, max_length: int = 2500) -> str: def send_via_email(**kwargs): - recipient_list = kwargs.get("email") - subject = kwargs.get("subject") + _recipient_list = kwargs.get("email") + _subject = kwargs.get("subject") message = kwargs.get("message") message = standardize_message_size(message, max_length=2500) sender_email = settings.EMAIL_HOST_USER @@ -33,7 +33,8 @@ def send_via_email(**kwargs): server.login("0c425676241dc7", "3ca81a9102980f") server.sendmail(sender, receiver, message) - # send_mail(subject=subject, message=message, from_email=sender_email, recipient_list=recipient_list, fail_silently=False) + # send_mail(subject=subject, message=message, from_email=sender_email, + # recipient_list=recipient_list, fail_silently=False) def send_via_sms(**kwargs): diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index c1e968c..45bcaf3 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -9,13 +9,12 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.2/ref/settings/ """ + import os import sys - from pathlib import Path import pycouchdb - from decouple import config # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -26,7 +25,7 @@ # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-x#q0@altnjw2yrhh)edi)co2)n3p8q&0qmz7m8oxu-*jhd8d9-" +SECRET_KEY = "django-insecure-x#q0@altnjw2yrhh)edi)co2)n3p8q&0qmz7m8oxu-*jhd8d9-" # nosec B105 # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -117,7 +116,6 @@ # COUCH_CONNECTOR = (config("COUCH_CONNECTOR"),) if "test" not in sys.argv: - COUCHDB_USER = config("COUCHDB_USER") COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") COUCHDB_PORT = config("COUCHDB_PORT") diff --git a/AHC_app/AHC_app/urls.py b/AHC_app/AHC_app/urls.py index 4ef5aef..1f92f89 100644 --- a/AHC_app/AHC_app/urls.py +++ b/AHC_app/AHC_app/urls.py @@ -14,6 +14,7 @@ 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.conf.urls.static import static from django.contrib import admin @@ -30,4 +31,5 @@ "favicon.ico", RedirectView.as_view(url=static("media/icons/chinchilla.png")), ), -] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), +] diff --git a/AHC_app/animals/forms.py b/AHC_app/animals/forms.py index fa3d045..41503d0 100644 --- a/AHC_app/animals/forms.py +++ b/AHC_app/animals/forms.py @@ -1,8 +1,9 @@ -from animals.models import Animal from django import forms from django.core.validators import MaxLengthValidator, MinLengthValidator from django.db.models import Q +from animals.models import Animal + class AnimalRegisterForm(forms.ModelForm): class Meta: diff --git a/AHC_app/animals/migrations/0001_initial.py b/AHC_app/animals/migrations/0001_initial.py index 898346b..64da9bb 100644 --- a/AHC_app/animals/migrations/0001_initial.py +++ b/AHC_app/animals/migrations/0001_initial.py @@ -29,15 +29,11 @@ class Migration(migrations.Migration): ("full_name", models.CharField(max_length=50)), ( "short_description", - models.CharField( - blank=True, default=None, max_length=250, null=True - ), + models.CharField(blank=True, default=None, max_length=250, null=True), ), ( "long_description", - models.CharField( - blank=True, default=None, max_length=2500, null=True - ), + models.CharField(blank=True, default=None, max_length=2500, null=True), ), ("birthdate", models.DateField(default=None, null=True)), ( @@ -50,15 +46,11 @@ class Migration(migrations.Migration): ("creation_date", models.DateTimeField(auto_now_add=True)), ( "first_contact_vet", - models.CharField( - blank=True, default=None, max_length=250, null=True - ), + models.CharField(blank=True, default=None, max_length=250, null=True), ), ( "first_contact_medical_place", - models.CharField( - blank=True, default=None, max_length=250, null=True - ), + models.CharField(blank=True, default=None, max_length=250, null=True), ), ("last_control_visit", models.DateTimeField(default=None, null=True)), ( diff --git a/AHC_app/animals/mixins/animal_owner_permissions.py b/AHC_app/animals/mixins/animal_owner_permissions.py index 378f8c0..4ebf125 100644 --- a/AHC_app/animals/mixins/animal_owner_permissions.py +++ b/AHC_app/animals/mixins/animal_owner_permissions.py @@ -1,6 +1,7 @@ -from animals.models import Animal from django.contrib.auth.mixins import UserPassesTestMixin +from animals.models import Animal + class UserPassesOwnershipTestMixin(UserPassesTestMixin): def test_func(self): diff --git a/AHC_app/animals/models.py b/AHC_app/animals/models.py index d64a8d1..14cab2f 100644 --- a/AHC_app/animals/models.py +++ b/AHC_app/animals/models.py @@ -1,6 +1,7 @@ import uuid from django.db import models + from users.models import Profile as UserProfile diff --git a/AHC_app/animals/signals.py b/AHC_app/animals/signals.py index 05ebd4f..f0d6a88 100644 --- a/AHC_app/animals/signals.py +++ b/AHC_app/animals/signals.py @@ -1,8 +1,9 @@ import os -from animals.models import Animal from django.db.models.signals import post_delete, post_save, pre_delete from django.dispatch import receiver + +from animals.models import Animal from users.models import Profile diff --git a/AHC_app/animals/templates/animals/all_animals_stable.html b/AHC_app/animals/templates/animals/all_animals_stable.html index b0a72e1..7221dd3 100644 --- a/AHC_app/animals/templates/animals/all_animals_stable.html +++ b/AHC_app/animals/templates/animals/all_animals_stable.html @@ -33,4 +33,4 @@

Operations:

{% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/animals/templates/animals/animal_confirm_delete.html b/AHC_app/animals/templates/animals/animal_confirm_delete.html index c1d4e3c..2145247 100644 --- a/AHC_app/animals/templates/animals/animal_confirm_delete.html +++ b/AHC_app/animals/templates/animals/animal_confirm_delete.html @@ -10,4 +10,4 @@

Delete Animal


Cancel -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/animals/templates/animals/change_birthday.html b/AHC_app/animals/templates/animals/change_birthday.html index 67c180d..5109645 100644 --- a/AHC_app/animals/templates/animals/change_birthday.html +++ b/AHC_app/animals/templates/animals/change_birthday.html @@ -9,4 +9,4 @@

Return to profile

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/animals/templates/animals/change_first_contact.html b/AHC_app/animals/templates/animals/change_first_contact.html index 562871b..4ef1549 100644 --- a/AHC_app/animals/templates/animals/change_first_contact.html +++ b/AHC_app/animals/templates/animals/change_first_contact.html @@ -19,4 +19,4 @@

Current first contact medical place:

Return to profile

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/animals/templates/animals/image.html b/AHC_app/animals/templates/animals/image.html index 6382fa1..ef53601 100644 --- a/AHC_app/animals/templates/animals/image.html +++ b/AHC_app/animals/templates/animals/image.html @@ -9,4 +9,4 @@

Return to profile

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/animals/templatetags/custom_timesince.py b/AHC_app/animals/templatetags/custom_timesince.py index a9465d5..46267dc 100644 --- a/AHC_app/animals/templatetags/custom_timesince.py +++ b/AHC_app/animals/templatetags/custom_timesince.py @@ -16,19 +16,8 @@ def years_and_months_since(value, arg): years -= 1 months += 12 - if years == 1: - years_str = "1 year" - else: - years_str = f"{years} years" - - if months == 1: - months_str = "1 month" - else: - months_str = f"{months} months" - - if years > 0: - response = f"{years_str}, {months_str}" - else: - response = f"{months_str}" + years_str = "1 year" if years == 1 else f"{years} years" + months_str = "1 month" if months == 1 else f"{months} months" + response = f"{years_str}, {months_str}" if years > 0 else f"{months_str}" return response diff --git a/AHC_app/animals/urls.py b/AHC_app/animals/urls.py index f741254..0ab0ade 100644 --- a/AHC_app/animals/urls.py +++ b/AHC_app/animals/urls.py @@ -1,14 +1,13 @@ +from django.urls import path + from animals import views as animal_views from animals.utils_owner import views as animal_owner_views -from django.urls import path urlpatterns = [ path("create/", animal_views.CreateAnimalView.as_view(), name="animal_create"), path("/delete/", animal_owner_views.AnimalDeleteView.as_view(), name="animal_delete"), path("/owner/", animal_owner_views.ChangeOwnerView.as_view(), name="animal_ownership"), - path( - "/cnt/", animal_owner_views.ChangeFirstContactView.as_view(), name="animal_first_contact" - ), # TO change + path("/cnt/", animal_owner_views.ChangeFirstContactView.as_view(), name="animal_first_contact"), # TO change path("/btd/", animal_owner_views.ChangeBirthdayView.as_view(), name="animal_birthday"), path("/", animal_views.AnimalProfileDetailView.as_view(), name="animal_profile"), path("/upload-image/", animal_owner_views.ImageUploadView.as_view(), name="upload_image"), diff --git a/AHC_app/animals/utils_owner/forms.py b/AHC_app/animals/utils_owner/forms.py index 868e015..14fa925 100644 --- a/AHC_app/animals/utils_owner/forms.py +++ b/AHC_app/animals/utils_owner/forms.py @@ -1,8 +1,9 @@ from datetime import date -from animals.models import Animal from django import forms from PIL import Image + +from animals.models import Animal from users.models import Profile @@ -23,9 +24,8 @@ def clean_profile_image(self): if extension not in self.ALLOWED_EXTENSIONS: raise forms.ValidationError("Invalid file extension.") - if image: - if image.size > self.MAX_IMAGE_SIZE_MB * 1024 * 1024: - raise forms.ValidationError("Image size is too large.") + if image and image.size > self.MAX_IMAGE_SIZE_MB * 1024 * 1024: + raise forms.ValidationError("Image size is too large.") if image: img = Image.open(image) diff --git a/AHC_app/animals/utils_owner/views.py b/AHC_app/animals/utils_owner/views.py index 963d817..4e6f706 100644 --- a/AHC_app/animals/utils_owner/views.py +++ b/AHC_app/animals/utils_owner/views.py @@ -1,3 +1,10 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import get_object_or_404, redirect +from django.urls import reverse, reverse_lazy +from django.views.generic import DeleteView +from django.views.generic.edit import FormView +from PIL import Image + from animals.mixins.animal_owner_permissions import UserPassesOwnershipTestMixin from animals.models import Animal from animals.utils_owner.forms import ( @@ -7,12 +14,6 @@ ImageUploadForm, ManageKeepersForm, ) -from django.contrib.auth.mixins import LoginRequiredMixin -from django.shortcuts import get_object_or_404, redirect -from django.urls import reverse, reverse_lazy -from django.views.generic import DeleteView -from django.views.generic.edit import FormView -from PIL import Image class AnimalDeleteView(LoginRequiredMixin, UserPassesOwnershipTestMixin, DeleteView): diff --git a/AHC_app/animals/views.py b/AHC_app/animals/views.py index 84f1c0b..d4d362d 100644 --- a/AHC_app/animals/views.py +++ b/AHC_app/animals/views.py @@ -1,5 +1,3 @@ -from animals.forms import AnimalRegisterForm, PinAnimalForm -from animals.models import Animal from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db.models import Q from django.http import JsonResponse @@ -8,6 +6,9 @@ from django.views.generic import TemplateView, View from django.views.generic.detail import DetailView from django.views.generic.edit import FormView + +from animals.forms import AnimalRegisterForm, PinAnimalForm +from animals.models import Animal from medical_notes.models.type_basic_note import MedicalRecord # from users.models import Profile as UserProfile diff --git a/AHC_app/homepage/admin.py b/AHC_app/homepage/admin.py index 23fc0d8..bc300d6 100644 --- a/AHC_app/homepage/admin.py +++ b/AHC_app/homepage/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin + from homepage.models import AnimalTitle, CronJob admin.site.register(AnimalTitle) diff --git a/AHC_app/homepage/management/__init__.py b/AHC_app/homepage/management/__init__.py index cb65698..bd9f50c 100644 --- a/AHC_app/homepage/management/__init__.py +++ b/AHC_app/homepage/management/__init__.py @@ -1 +1 @@ -from .commands.sync_cronjobs import Command +from .commands.sync_cronjobs import Command # noqa: F401 diff --git a/AHC_app/homepage/management/commands/sync_cronjobs.py b/AHC_app/homepage/management/commands/sync_cronjobs.py index 80ccaa6..fc2ec7e 100644 --- a/AHC_app/homepage/management/commands/sync_cronjobs.py +++ b/AHC_app/homepage/management/commands/sync_cronjobs.py @@ -1,6 +1,7 @@ import subprocess from django.core.management.base import BaseCommand + from homepage.models import CronJob diff --git a/AHC_app/homepage/migrations/0001_initial.py b/AHC_app/homepage/migrations/0001_initial.py index e5a9f45..4c846ca 100644 --- a/AHC_app/homepage/migrations/0001_initial.py +++ b/AHC_app/homepage/migrations/0001_initial.py @@ -1,10 +1,11 @@ # Generated by Django 4.2.1 on 2023-10-12 07:43 import django.db.models.deletion -import homepage.utils from django.conf import settings from django.db import migrations, models +import homepage.utils + class Migration(migrations.Migration): initial = True diff --git a/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py b/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py index d75b47b..273b3bb 100644 --- a/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py +++ b/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py @@ -1,8 +1,9 @@ # Generated by Django 4.2.1 on 2023-10-06 16:41 -import homepage.utils from django.db import migrations, models +import homepage.utils + class Migration(migrations.Migration): dependencies = [ diff --git a/AHC_app/homepage/models.py b/AHC_app/homepage/models.py index fab7fc2..8091afb 100644 --- a/AHC_app/homepage/models.py +++ b/AHC_app/homepage/models.py @@ -1,6 +1,7 @@ from django.contrib.auth.models import User from django.db import models from django.urls import reverse + from homepage.utils import ImageGenerator @@ -8,7 +9,7 @@ class Privilege(models.Model): title = models.CharField(max_length=30) privilege_to_delete_animal = models.BooleanField(default=False) - # TODO: reconsider usage to simplyfy priveliges test mixins + # TODO: reconsider usage to simplify privileges test mixins def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) raise NotImplementedError diff --git a/AHC_app/homepage/tests/test_homepage.py b/AHC_app/homepage/tests/test_homepage.py index fe3ad54..fa3783f 100644 --- a/AHC_app/homepage/tests/test_homepage.py +++ b/AHC_app/homepage/tests/test_homepage.py @@ -2,6 +2,7 @@ from django.contrib.auth.models import User from django.test import Client, TestCase + from homepage.models import AnimalTitle client = Client() diff --git a/AHC_app/homepage/urls.py b/AHC_app/homepage/urls.py index 6f65fb3..30af557 100644 --- a/AHC_app/homepage/urls.py +++ b/AHC_app/homepage/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from homepage.views import HomepageView urlpatterns = [ diff --git a/AHC_app/homepage/views.py b/AHC_app/homepage/views.py index 1a51f30..611875f 100644 --- a/AHC_app/homepage/views.py +++ b/AHC_app/homepage/views.py @@ -1,6 +1,7 @@ -from animals.models import Animal from django.db.models import Q from django.views.generic import TemplateView + +from animals.models import Animal from users.models import Profile as UserProfile @@ -23,7 +24,6 @@ def get_context_data(self, **kwargs): context["pinned_animals"] = pinned_animals_query if user_query.allow_recennt_animals_list: - recent_created_animals_query = Animal.objects.filter( Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile) ).order_by("-creation_date")[:3] diff --git a/AHC_app/manage.py b/AHC_app/manage.py index db3b9dc..c262845 100644 --- a/AHC_app/manage.py +++ b/AHC_app/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys diff --git a/AHC_app/medical_notes/admin.py b/AHC_app/medical_notes/admin.py index 8c38f3f..846f6b4 100644 --- a/AHC_app/medical_notes/admin.py +++ b/AHC_app/medical_notes/admin.py @@ -1,3 +1 @@ -from django.contrib import admin - # Register your models here. diff --git a/AHC_app/medical_notes/forms/type_basic_note.py b/AHC_app/medical_notes/forms/type_basic_note.py index 8263457..a03bc1b 100644 --- a/AHC_app/medical_notes/forms/type_basic_note.py +++ b/AHC_app/medical_notes/forms/type_basic_note.py @@ -47,7 +47,7 @@ class Meta: def __init__(self, *args, **kwargs): animal_choices = kwargs.pop("animal_choices", None) type_of_event_param = kwargs.pop("type_of_event_param", None) - super(MedicalRecordForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # self.Meta.fields.append('TYPES_OF_EVENTS') if animal_choices: @@ -64,7 +64,7 @@ def __init__(self, *args, **kwargs): class MedicalRecordEditForm(MedicalRecordForm): def __init__(self, *args, **kwargs): animal = kwargs.pop("animal", None) - super(MedicalRecordEditForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.animal = animal tag_names = list(self.instance.note_tags.values_list("name", flat=True)) self.initial["note_tags"] = ", ".join(tag_names) @@ -91,7 +91,7 @@ def __init__(self, *args, **kwargs): kwargs.pop("animal") animal_choices = kwargs.pop("animal_choices", None) is_author = kwargs.pop("is_author", None) - super(MedicalRecordEditRelatedAnimalsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if animal_choices: self.fields["animal"].widget.choices = animal_choices diff --git a/AHC_app/medical_notes/forms/type_feeding_notes.py b/AHC_app/medical_notes/forms/type_feeding_notes.py index 3e4fb4e..2ded162 100644 --- a/AHC_app/medical_notes/forms/type_feeding_notes.py +++ b/AHC_app/medical_notes/forms/type_feeding_notes.py @@ -1,8 +1,9 @@ from django import forms from django.conf import settings -from medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote from timezone_field import TimeZoneFormField +from medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote + class DietRecordForm(forms.ModelForm): class Meta: @@ -77,6 +78,6 @@ class Meta: days_of_week = forms.MultipleChoiceField(choices=days_of_week_choices, widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): - super(NotificationRecordForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields["timezone"].initial = str(settings.TIME_ZONE) diff --git a/AHC_app/medical_notes/forms/type_measurement_notes.py b/AHC_app/medical_notes/forms/type_measurement_notes.py index ae4fe25..7fa41f3 100644 --- a/AHC_app/medical_notes/forms/type_measurement_notes.py +++ b/AHC_app/medical_notes/forms/type_measurement_notes.py @@ -1,5 +1,7 @@ from django import forms +from medical_notes.models.type_measurement_notes import BiometricHeightRecords, BiometricWeightRecords + class BiometricRecordForm(forms.Form): RECORD_CHOICES = [ @@ -21,14 +23,10 @@ class BiometricRecordForm(forms.Form): custom_unit = forms.CharField(max_length=12, required=False) def __init__(self, *args, **kwargs): - super(BiometricRecordForm, self).__init__(*args, **kwargs) - - default_height_unit_to_present = BiometricHeightRecords._meta.get_field( - "height_unit_to_present" - ).get_default() - default_weight_unit_to_present = BiometricWeightRecords._meta.get_field( - "weight_unit_to_present" - ).get_default() + super().__init__(*args, **kwargs) + + default_height_unit_to_present = BiometricHeightRecords._meta.get_field("height_unit_to_present").get_default() + default_weight_unit_to_present = BiometricWeightRecords._meta.get_field("weight_unit_to_present").get_default() self.fields["height_unit_to_present"].initial = default_height_unit_to_present self.fields["weight_unit_to_present"].initial = default_weight_unit_to_present diff --git a/AHC_app/medical_notes/migrations/0001_initial.py b/AHC_app/medical_notes/migrations/0001_initial.py index 7f28f34..8eed680 100644 --- a/AHC_app/medical_notes/migrations/0001_initial.py +++ b/AHC_app/medical_notes/migrations/0001_initial.py @@ -146,9 +146,7 @@ class Migration(migrations.Migration): ), ( "animal", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="animals.animal" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="animals.animal"), ), ( "author", @@ -199,9 +197,7 @@ class Migration(migrations.Migration): ), ( "animal", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="animals.animal" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="animals.animal"), ), ], ), @@ -225,9 +221,7 @@ class Migration(migrations.Migration): ("description", models.CharField(max_length=250)), ( "animal", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="animals.animal" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="animals.animal"), ), ], ), @@ -246,9 +240,7 @@ class Migration(migrations.Migration): ("date_updated", models.DateTimeField(auto_now_add=True)), ( "animal", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="animals.animal" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="animals.animal"), ), ( "custom_biometric_record", diff --git a/AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py b/AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py index 921f03e..a93c2ec 100644 --- a/AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py +++ b/AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py @@ -1,9 +1,10 @@ # Generated by Django 4.2.1 on 2023-12-28 16:40 -from django.db import migrations, models -import django.db.models.deletion import uuid +import django.db.models.deletion +from django.db import migrations, models + class Migration(migrations.Migration): dependencies = [ diff --git a/AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py b/AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py index 4e9e757..29c4e8d 100644 --- a/AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py +++ b/AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py @@ -6,7 +6,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0008_alter_medicalrecordattachment_file"), ] diff --git a/AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py b/AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py index c0dc312..70e6d6d 100644 --- a/AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py +++ b/AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0009_discordnotification_last_modification_and_more"), ] diff --git a/AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py b/AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py index 7690074..2e74989 100644 --- a/AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py +++ b/AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0010_alter_discordnotification_last_modification_and_more"), ] diff --git a/AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py b/AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py index 0639d1d..075e3aa 100644 --- a/AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py +++ b/AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0011_medicalrecordattachment_description"), ] diff --git a/AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py b/AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py index 57ce211..b87311c 100644 --- a/AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py +++ b/AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0012_medicalrecordattachment_file_name_and_more"), ] diff --git a/AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py b/AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py index 4e0b8bb..9d99dea 100644 --- a/AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py +++ b/AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("medical_notes", "0013_remove_medicalrecordattachment_url_and_more"), ] diff --git a/AHC_app/medical_notes/models/type_basic_note.py b/AHC_app/medical_notes/models/type_basic_note.py index b5ce3d4..1edb49c 100644 --- a/AHC_app/medical_notes/models/type_basic_note.py +++ b/AHC_app/medical_notes/models/type_basic_note.py @@ -1,9 +1,10 @@ import uuid -from animals.models import Animal from django.db import models from taggit.managers import TaggableManager from taggit.models import GenericUUIDTaggedItemBase, TaggedItemBase + +from animals.models import Animal from users.models import Profile as UserProfile diff --git a/AHC_app/medical_notes/models/type_feeding_notes.py b/AHC_app/medical_notes/models/type_feeding_notes.py index e48c449..87725b8 100644 --- a/AHC_app/medical_notes/models/type_feeding_notes.py +++ b/AHC_app/medical_notes/models/type_feeding_notes.py @@ -3,9 +3,10 @@ from django.contrib.postgres.fields import ArrayField from django.db import models -from medical_notes.models.type_basic_note import MedicalRecord from timezone_field import TimeZoneField +from medical_notes.models.type_basic_note import MedicalRecord + class FeedingNote(models.Model): related_note = models.ForeignKey(MedicalRecord, on_delete=models.CASCADE, null=False, blank=False) diff --git a/AHC_app/medical_notes/models/type_measurement_notes.py b/AHC_app/medical_notes/models/type_measurement_notes.py index 1c36dda..84eb0ec 100644 --- a/AHC_app/medical_notes/models/type_measurement_notes.py +++ b/AHC_app/medical_notes/models/type_measurement_notes.py @@ -1,5 +1,6 @@ -from animals.models import Animal from django.db import models + +from animals.models import Animal from medical_notes.models.type_basic_note import MedicalRecord @@ -23,17 +24,9 @@ class BiometricCustomRecords(models.Model): class BiometricRecord(models.Model): animal = models.ForeignKey(Animal, on_delete=models.CASCADE) - related_note = models.ForeignKey( - MedicalRecord, on_delete=models.SET_NULL, blank=True, null=True - ) + related_note = models.ForeignKey(MedicalRecord, on_delete=models.SET_NULL, blank=True, null=True) date_updated = models.DateTimeField(auto_now_add=True, editable=True) - weight_biometric_record = models.OneToOneField( - BiometricWeightRecords, on_delete=models.CASCADE, blank=True, null=True - ) - height_biometric_record = models.OneToOneField( - BiometricHeightRecords, on_delete=models.CASCADE, blank=True, null=True - ) - custom_biometric_record = models.OneToOneField( - BiometricCustomRecords, on_delete=models.CASCADE, blank=True, null=True - ) + weight_biometric_record = models.OneToOneField(BiometricWeightRecords, on_delete=models.CASCADE, blank=True, null=True) + height_biometric_record = models.OneToOneField(BiometricHeightRecords, on_delete=models.CASCADE, blank=True, null=True) + custom_biometric_record = models.OneToOneField(BiometricCustomRecords, on_delete=models.CASCADE, blank=True, null=True) diff --git a/AHC_app/medical_notes/signals/type_feeding_notes.py b/AHC_app/medical_notes/signals/type_feeding_notes.py index ce07d43..9fa7dc5 100644 --- a/AHC_app/medical_notes/signals/type_feeding_notes.py +++ b/AHC_app/medical_notes/signals/type_feeding_notes.py @@ -1,6 +1,7 @@ from django.db import transaction from django.db.models.signals import post_save from django.dispatch import receiver + from medical_notes.models.type_feeding_notes import FeedingNote from users.models import Profile as UserProfile diff --git a/AHC_app/medical_notes/signals/type_measurement_notes.py b/AHC_app/medical_notes/signals/type_measurement_notes.py index 6e7cb9e..63783b1 100644 --- a/AHC_app/medical_notes/signals/type_measurement_notes.py +++ b/AHC_app/medical_notes/signals/type_measurement_notes.py @@ -2,6 +2,7 @@ from django.db.models import Q from django.db.models.signals import post_save, pre_save from django.dispatch import receiver + from medical_notes.models.type_basic_note import MedicalRecord from medical_notes.models.type_measurement_notes import BiometricRecord from users.models import Profile as UserProfile diff --git a/AHC_app/medical_notes/templatetags/custom_file_name.py b/AHC_app/medical_notes/templatetags/custom_file_name.py index 82d6968..9932126 100644 --- a/AHC_app/medical_notes/templatetags/custom_file_name.py +++ b/AHC_app/medical_notes/templatetags/custom_file_name.py @@ -1,4 +1,5 @@ from django import template + from medical_notes.models.type_feeding_notes import FeedingNotification register = template.Library() @@ -10,6 +11,6 @@ def to_class_name(value): raise template.TemplateSyntaxError("Value cannot be None") if FeedingNotification not in value.__class__.__bases__: - raise template.TemplateSyntaxError(f"Not allowed to use on the model") + raise template.TemplateSyntaxError("Not allowed to use on the model") return value.__class__.__name__ diff --git a/AHC_app/medical_notes/tests.py b/AHC_app/medical_notes/tests.py index 7ce503c..a39b155 100644 --- a/AHC_app/medical_notes/tests.py +++ b/AHC_app/medical_notes/tests.py @@ -1,3 +1 @@ -from django.test import TestCase - # Create your tests here. diff --git a/AHC_app/medical_notes/urls.py b/AHC_app/medical_notes/urls.py index dea5590..e9fbfab 100644 --- a/AHC_app/medical_notes/urls.py +++ b/AHC_app/medical_notes/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from medical_notes.views import type_basic_note as notes_views from medical_notes.views import type_feeding_notes as feeding_views from medical_notes.views import type_measurement_notes as measurement_views @@ -7,7 +8,7 @@ path("/create/", notes_views.CreateNoteFormView.as_view(), name="note_create"), path("/edit/", notes_views.EditNoteView.as_view(), name="note_edit"), path("/delete/", notes_views.DeleteNoteView.as_view(), name="note_delete"), - path("/realted/", notes_views.EditRelatedAnimalsView.as_view(), name="note_animals_edit"), + path("/related/", notes_views.EditRelatedAnimalsView.as_view(), name="note_animals_edit"), path("/notes/", notes_views.FullTimelineOfNotes.as_view(), name="full_timeline_of_notes"), path("/feeding_create/", feeding_views.DietRecordCreateView.as_view(), name="feeding_create"), path("/feeding_edit/", feeding_views.EditDietRecordView.as_view(), name="feeding_edit"), diff --git a/AHC_app/medical_notes/views/type_basic_note.py b/AHC_app/medical_notes/views/type_basic_note.py index 7748361..b7271ba 100644 --- a/AHC_app/medical_notes/views/type_basic_note.py +++ b/AHC_app/medical_notes/views/type_basic_note.py @@ -1,4 +1,3 @@ -from animals.models import Animal as AnimalProfile from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin @@ -12,6 +11,8 @@ from django.views.generic import View from django.views.generic.edit import DeleteView, FormView, UpdateView from django.views.generic.list import ListView + +from animals.models import Animal as AnimalProfile from medical_notes.forms.type_basic_note import ( MedicalRecordEditForm, MedicalRecordEditRelatedAnimalsForm, @@ -32,9 +33,7 @@ def get_form_kwargs(self): kwargs = super().get_form_kwargs() query = ( - AnimalProfile.objects.filter( - Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile) - ) + AnimalProfile.objects.filter(Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile)) .exclude(id=self.kwargs.get("pk")) .order_by("-creation_date") ) @@ -133,7 +132,7 @@ def get_context_data(self, **kwargs): form.fields["medical_record_id"].initial = str(note.id) upload_forms.append(form) - notes_with_forms = zip(context["notes"], upload_forms) + notes_with_forms = zip(context["notes"], upload_forms, strict=False) context["notes"] = notes_with_forms @@ -182,7 +181,7 @@ def post(self, request, *args, **kwargs): else: print(form.errors) - for field, errors in form.errors.items(): + for _field, errors in form.errors.items(): messages.error(request, f"Failed to upload: {', '.join(errors)}") return redirect(request.path) @@ -371,7 +370,7 @@ def test_func(self): def get(self, request, *args, **kwargs): couch_connector = settings.COUCH_DB reference_id = self.kwargs.get("id") - filename = self.kwargs.get("name") + _filename = self.kwargs.get("name") attachment = couch_connector.get(reference_id) if not attachment: diff --git a/AHC_app/medical_notes/views/type_feeding_notes.py b/AHC_app/medical_notes/views/type_feeding_notes.py index 7fe7c76..8c23a6b 100644 --- a/AHC_app/medical_notes/views/type_feeding_notes.py +++ b/AHC_app/medical_notes/views/type_feeding_notes.py @@ -1,4 +1,3 @@ -from animals.models import Animal as AnimalProfile from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.http import HttpResponseRedirect @@ -6,6 +5,8 @@ from django.urls import reverse_lazy from django.views.generic.edit import FormView, UpdateView from django.views.generic.list import ListView + +from animals.models import Animal as AnimalProfile from medical_notes.forms.type_feeding_notes import ( DietRecordForm, NotificationRecordForm, @@ -27,7 +28,7 @@ def form_valid(self, form): note_id = self.kwargs.get("pk") related_note = get_object_or_404(MedicalRecord, id=note_id) - animal = related_note.animal + _animal = related_note.animal feeding_note = form.save(commit=False) feeding_note.related_note = related_note diff --git a/AHC_app/medical_notes/views/type_measurement_notes.py b/AHC_app/medical_notes/views/type_measurement_notes.py index e4c0a3b..7d89125 100644 --- a/AHC_app/medical_notes/views/type_measurement_notes.py +++ b/AHC_app/medical_notes/views/type_measurement_notes.py @@ -1,6 +1,7 @@ -from animals.models import Animal as AnimalProfile from django.shortcuts import get_object_or_404, redirect, reverse from django.views.generic.edit import FormView + +from animals.models import Animal as AnimalProfile from medical_notes.forms.type_measurement_notes import BiometricRecordForm from medical_notes.models.type_basic_note import MedicalRecord from medical_notes.models.type_measurement_notes import ( @@ -34,7 +35,7 @@ def form_valid(self, form): weight = form.cleaned_data["weight"] unit = form.cleaned_data["weight_unit_to_present"] weight_record = BiometricWeightRecords.objects.create(weight=weight, weight_unit_to_present=unit) - biometric_record = BiometricRecord.objects.create( + _biometric_record = BiometricRecord.objects.create( animal=animal, related_note=related_note, weight_biometric_record=weight_record, @@ -43,7 +44,7 @@ def form_valid(self, form): height = form.cleaned_data["height"] unit = form.cleaned_data["height_unit_to_present"] height_record = BiometricHeightRecords.objects.create(height=height, height_unit_to_present=unit) - biometric_record = BiometricRecord.objects.create( + _biometric_record = BiometricRecord.objects.create( animal=animal, related_note=related_note, height_biometric_record=height_record, @@ -57,7 +58,7 @@ def form_valid(self, form): record_value=custom_value, record_unit=custom_unit, ) - biometric_record = BiometricRecord.objects.create( + _biometric_record = BiometricRecord.objects.create( animal=animal, related_note=related_note, custom_biometric_record=custom_record, diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml index cb462e0..f93635a 100644 --- a/AHC_app/pyproject.toml +++ b/AHC_app/pyproject.toml @@ -42,9 +42,36 @@ dependencies = [ [dependency-groups] dev = [ "pre-commit", - "black", - "isort", + "ruff", + "ty", + "codespell", + "bandit[toml]", ] [tool.uv] package = false + +[tool.ruff] +line-length = 124 + +[tool.ruff.lint] +select = ["E", "F", "UP", "B", "SIM", "I", "RUF", "DJ"] +ignore = [ + "DJ001", + "DJ008", + "RUF012", +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" + +[tool.ty.environment] +python-version = "3.14" + +[tool.codespell] +skip = "uv.lock,./static,./static_collected" + +[tool.bandit] +exclude_dirs = [".venv"] +skips = ["B101", "B404", "B603", "B607"] diff --git a/AHC_app/users/admin.py b/AHC_app/users/admin.py index ed54a24..8886aeb 100644 --- a/AHC_app/users/admin.py +++ b/AHC_app/users/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin + from users.models import Profile admin.site.register(Profile) diff --git a/AHC_app/users/forms.py b/AHC_app/users/forms.py index 37c2b8d..9a4bd29 100644 --- a/AHC_app/users/forms.py +++ b/AHC_app/users/forms.py @@ -1,5 +1,6 @@ from django import forms from django.contrib.auth.forms import User, UserCreationForm + from users.models import Profile diff --git a/AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py b/AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py index 7fee793..f7296ae 100644 --- a/AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py +++ b/AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("users", "0001_initial"), ] diff --git a/AHC_app/users/migrations/0003_profile_pinned_animals.py b/AHC_app/users/migrations/0003_profile_pinned_animals.py index c039789..4087290 100644 --- a/AHC_app/users/migrations/0003_profile_pinned_animals.py +++ b/AHC_app/users/migrations/0003_profile_pinned_animals.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("animals", "0001_initial"), ("users", "0002_profile_allow_recennt_animals_list"), diff --git a/AHC_app/users/models.py b/AHC_app/users/models.py index 9a67048..f64ad5a 100644 --- a/AHC_app/users/models.py +++ b/AHC_app/users/models.py @@ -1,8 +1,9 @@ from django.contrib.auth.models import User from django.db import models -from homepage.models import Privilege, ProfileBackground from PIL import Image +from homepage.models import Privilege, ProfileBackground + class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) diff --git a/AHC_app/users/signals.py b/AHC_app/users/signals.py index 6821573..068927b 100644 --- a/AHC_app/users/signals.py +++ b/AHC_app/users/signals.py @@ -1,6 +1,7 @@ from django.contrib.auth.models import User from django.db.models.signals import post_save, pre_save from django.dispatch import receiver + from homepage.models import Privilege, ProfileBackground from users.models import Profile @@ -22,7 +23,7 @@ def create_background(sender, instance, **kwargs): @receiver(post_save, sender=User) def create_profile(sender, instance, created, **kwargs): if created: - background, _ = ProfileBackground.objects.get_or_create(title="Default Background") + _background, _ = ProfileBackground.objects.get_or_create(title="Default Background") Profile.objects.create(user=instance) diff --git a/AHC_app/users/templates/users/login.html b/AHC_app/users/templates/users/login.html index b08716c..2322254 100644 --- a/AHC_app/users/templates/users/login.html +++ b/AHC_app/users/templates/users/login.html @@ -23,4 +23,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/users/templates/users/login_success.html b/AHC_app/users/templates/users/login_success.html index cf6e929..106bfab 100644 --- a/AHC_app/users/templates/users/login_success.html +++ b/AHC_app/users/templates/users/login_success.html @@ -21,4 +21,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/AHC_app/users/templates/users/password_reset.html b/AHC_app/users/templates/users/password_reset.html index 1c2f919..02384ea 100644 --- a/AHC_app/users/templates/users/password_reset.html +++ b/AHC_app/users/templates/users/password_reset.html @@ -5,10 +5,10 @@ {% csrf_token %}

Reset Password

{% for field in form %} - {{ field.label_tag }} + {{ field.label_tag }} {{ field }} {% if field.errors %} - {{ field.errors|striptags }} + {{ field.errors|striptags }} {% endif %} {% endfor %}
@@ -17,4 +17,4 @@

Reset Password

-{%endblock content%} \ No newline at end of file +{%endblock content%} diff --git a/AHC_app/users/templates/users/password_reset_complete.html b/AHC_app/users/templates/users/password_reset_complete.html index 23a11e2..c7160eb 100644 --- a/AHC_app/users/templates/users/password_reset_complete.html +++ b/AHC_app/users/templates/users/password_reset_complete.html @@ -4,5 +4,5 @@

Your password has been changed successfully. Please Login

- -{%endblock content%} \ No newline at end of file + +{%endblock content%} diff --git a/AHC_app/users/templates/users/password_reset_confirm.html b/AHC_app/users/templates/users/password_reset_confirm.html index 4ddd783..5e3d36c 100644 --- a/AHC_app/users/templates/users/password_reset_confirm.html +++ b/AHC_app/users/templates/users/password_reset_confirm.html @@ -5,16 +5,16 @@
{% csrf_token %}

Password Reset Confirm

- + {% for field in form %} - {{ field.label_tag }} + {{ field.label_tag }} {{ field }} {% if field.errors %} - {{ field.errors|striptags }} + {{ field.errors|striptags }} {% endif %} {% endfor %} - - + +
diff --git a/AHC_app/users/templates/users/password_reset_done.html b/AHC_app/users/templates/users/password_reset_done.html index adf83a3..f92eef6 100644 --- a/AHC_app/users/templates/users/password_reset_done.html +++ b/AHC_app/users/templates/users/password_reset_done.html @@ -6,4 +6,4 @@

Reset Password

Please check your inbox and follow the instruction to reset your password.

-{%endblock content%} \ No newline at end of file +{%endblock content%} diff --git a/AHC_app/users/templates/users/password_reset_email.html b/AHC_app/users/templates/users/password_reset_email.html index 098d1e9..7c768d7 100644 --- a/AHC_app/users/templates/users/password_reset_email.html +++ b/AHC_app/users/templates/users/password_reset_email.html @@ -7,4 +7,4 @@ {{ protocol }}://{{ domain }}{% url "password_reset_confirm" uidb64=uid token=token %}

Thanks

-

Todo App Team

\ No newline at end of file +

Todo App Team

diff --git a/AHC_app/users/tests.py b/AHC_app/users/tests.py index 7ce503c..a39b155 100644 --- a/AHC_app/users/tests.py +++ b/AHC_app/users/tests.py @@ -1,3 +1 @@ -from django.test import TestCase - # Create your tests here. diff --git a/AHC_app/users/urls.py b/AHC_app/users/urls.py index b0a23c4..056bd50 100644 --- a/AHC_app/users/urls.py +++ b/AHC_app/users/urls.py @@ -6,15 +6,14 @@ PasswordResetView, ) from django.urls import path + from users import views as user_views urlpatterns = [ path("", auth_views.LoginView.as_view(template_name="users/login.html"), name="login"), path("login/", auth_views.LoginView.as_view(template_name="users/login.html"), name="login"), path("register/", user_views.UserRegisterView.as_view(), name="register"), - path( - "login_success/", auth_views.LoginView.as_view(template_name="users/login_success.html"), name="login_success" - ), + path("login_success/", auth_views.LoginView.as_view(template_name="users/login_success.html"), name="login_success"), path("logout/", auth_views.LogoutView.as_view(template_name="users/logout.html"), name="logout"), path("profile/", user_views.UserProfileView.as_view(), name="profile"), path( diff --git a/AHC_app/users/views.py b/AHC_app/users/views.py index dc39aeb..538cbd9 100644 --- a/AHC_app/users/views.py +++ b/AHC_app/users/views.py @@ -2,6 +2,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.views.generic import CreateView, UpdateView + from users.forms import ProfileUpdateForm, UserRegisterForm, UserUpdateForm from users.models import Profile diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock index 390af96..4c03322 100644 --- a/AHC_app/uv.lock +++ b/AHC_app/uv.lock @@ -127,9 +127,11 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "black" }, - { name = "isort" }, + { name = "bandit" }, + { name = "codespell" }, { name = "pre-commit" }, + { name = "ruff" }, + { name = "ty" }, ] [package.metadata] @@ -170,9 +172,11 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "black" }, - { name = "isort" }, + { name = "bandit", extras = ["toml"] }, + { name = "codespell" }, { name = "pre-commit" }, + { name = "ruff" }, + { name = "ty" }, ] [[package]] @@ -243,34 +247,27 @@ wheels = [ ] [[package]] -name = "billiard" -version = "4.2.4" +name = "bandit" +version = "1.9.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/23/b12ac0bcdfb7360d664f40a00b1bda139cbbbced012c34e375506dbd0143/billiard-4.2.4.tar.gz", hash = "sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f", size = 156537, upload-time = "2025-11-30T13:28:48.52Z" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "stevedore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/87/8bab77b323f16d67be364031220069f79159117dd5e43eeb4be2fef1ac9b/billiard-4.2.4-py3-none-any.whl", hash = "sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5", size = 87070, upload-time = "2025-11-30T13:28:47.016Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" }, ] [[package]] -name = "black" -version = "26.5.1" +name = "billiard" +version = "4.2.4" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/23/b12ac0bcdfb7360d664f40a00b1bda139cbbbced012c34e375506dbd0143/billiard-4.2.4.tar.gz", hash = "sha256:55f542c371209e03cd5862299b74e52e4fbcba8250ba611ad94276b369b6a85f", size = 156537, upload-time = "2025-11-30T13:28:48.52Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, - { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, - { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, - { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, - { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, + { url = "https://files.pythonhosted.org/packages/cb/87/8bab77b323f16d67be364031220069f79159117dd5e43eeb4be2fef1ac9b/billiard-4.2.4-py3-none-any.whl", hash = "sha256:525b42bdec68d2b983347ac312f892db930858495db601b5836ac24e6477cde5", size = 87070, upload-time = "2025-11-30T13:28:47.016Z" }, ] [[package]] @@ -443,6 +440,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289, upload-time = "2023-06-15T12:43:48.626Z" }, ] +[[package]] +name = "codespell" +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/9d/1d0903dff693160f893ca6abcabad545088e7a2ee0a6deae7c24e958be69/codespell-2.4.2.tar.gz", hash = "sha256:3c33be9ae34543807f088aeb4832dfad8cb2dae38da61cac0a7045dd376cfdf3", size = 352058, upload-time = "2026-03-05T18:10:42.936Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/a1/52fa05533e95fe45bcc09bcf8a503874b1c08f221a4e35608017e0938f55/codespell-2.4.2-py3-none-any.whl", hash = "sha256:97e0c1060cf46bd1d5db89a936c98db8c2b804e1fdd4b5c645e82a1ec6b1f886", size = 353715, upload-time = "2026-03-05T18:10:41.398Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -813,15 +819,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, ] -[[package]] -name = "isort" -version = "8.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, -] - [[package]] name = "kombu" version = "5.6.2" @@ -850,6 +847,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/20/caf3c7cf2432d85263119798c45221ddf67bdd7dae8f626d14ff8db04040/libsass-0.23.0-cp38-abi3-win_amd64.whl", hash = "sha256:a2ec85d819f353cbe807432d7275d653710d12b08ec7ef61c124a580a8352f3c", size = 872914, upload-time = "2024-01-06T19:02:47.61Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "multidict" version = "6.7.1" @@ -895,15 +913,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - [[package]] name = "nodeenv" version = "1.10.0" @@ -931,15 +940,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, -] - [[package]] name = "pillow" version = "12.2.0" @@ -1192,25 +1192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/a5/c6ba13860bdf5525f1ab01e01cc667578d6f1efc8a1dba355700fb04c29b/python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b", size = 133681, upload-time = "2020-06-29T12:15:47.502Z" }, ] -[[package]] -name = "pytokens" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, - { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, - { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, - { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, - { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, - { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, - { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, - { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, - { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, -] - [[package]] name = "pytz" version = "2026.2" @@ -1303,6 +1284,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, ] +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "rjsmin" version = "1.2.5" @@ -1323,6 +1317,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/f3/37a4672ddb1307eb57d9b54ba89a48f483a04a63cac4e1471fdb4cba76e6/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47dad1732a2c4779bdc76d5b3183fdf2ec27838f31071fa9dfcc79483d3480e2", size = 34161, upload-time = "2025-10-12T10:51:23.761Z" }, ] +[[package]] +name = "ruff" +version = "0.15.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/8a/8bce2894573e9dae6ff4d77fe34ad727d79b9e6238ad288c5638990d90f6/ruff-0.15.14.tar.gz", hash = "sha256:48e866b165be4a9bdbf310f7d3c9a07edef2fe8cd63ffeb4e00bb590506ebf9f", size = 4700910, upload-time = "2026-05-21T14:34:55.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/c8/74a92c6ff9fcfb4f1f947126d3ebee8389276e161ecc85de5bda7cda51bd/ruff-0.15.14-py3-none-linux_armv6l.whl", hash = "sha256:8dd2db9416e487c8d4b01fa7056bb02c4d05969d4f8d17a08c229c2f4ff3c108", size = 10739177, upload-time = "2026-05-21T14:34:37.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/254a35c20acc38a7223c9d2d594af12e794432464f2cdeb52af1dc4a892d/ruff-0.15.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:be4ff55af755bd71a00ab3dc6bd7ffc467bd76e0df6881e286c2e3d23e8fb43b", size = 11144969, upload-time = "2026-05-21T14:34:43.978Z" }, + { url = "https://files.pythonhosted.org/packages/56/9e/d13e40f83b8d0a94430e6778ce1d94a43b38cf2efe63278bdd2b4c65abbf/ruff-0.15.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48d5909d7d06276ce7dde6d32bfa4b0d4cb2651145cd8ee4b440722cbc77832f", size = 10478207, upload-time = "2026-05-21T14:34:48.378Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f1/b15a7839fa4f332f8acec78e20564f26bb2d866e3d21710b877fd0263000/ruff-0.15.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca8cbfa94c4f90984a67561978602746d4cd27103568f745fa90eee3f0d4107d", size = 10818459, upload-time = "2026-05-21T14:34:22.318Z" }, + { url = "https://files.pythonhosted.org/packages/45/33/53d651177f84f94b400a0e27f8824eeada3dddc9d5ee8aeb048f4352a520/ruff-0.15.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a6bbc0333f1ab053423bcbf6226477d266ca7cec7738c4c8e3f55647803f3c4", size = 10541800, upload-time = "2026-05-21T14:34:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/868f87e0bf9786ed24b5d0d0ad8676b8a94fd1912f42cddf9cfc7857818a/ruff-0.15.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a24a4f7605d7003a6674d4387651effd939dead3fddd0f36561eb77a9a2e542", size = 11342149, upload-time = "2026-05-21T14:34:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/a7/8b/38cd5c19faffdcc05a408d2b78edccc69492ab9720eadb49ea15ef80d768/ruff-0.15.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:049b5326e53ed80978f2fc041a280603f69dd6b0c95464342a2bb4572d9d9e2f", size = 12212563, upload-time = "2026-05-21T14:34:28.579Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4d/a3c5b874a556d5731e3e657aaf04311bb76f0a5c3ec220ed43051be6b64b/ruff-0.15.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4ed42e6696c8dfa5f06728e6441993901f548eb92d73bc472cb5a38d1395fbf", size = 11493299, upload-time = "2026-05-21T14:34:41.836Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c0/56472c251d09858a53e51efbd485b09e1995d8731668b76d52e5dd6ee0f1/ruff-0.15.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715c543cf450c4888251f91c52f1942a800541d9bddd7ac060aa4e6b77ae7cba", size = 11455931, upload-time = "2026-05-21T14:34:57.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/4a/e2e7b4d8dbf233d4eace59c75bc3435fa6d8bd3bae82d351d4e4300c0fd1/ruff-0.15.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ebab6013ec887d439d8b7593737a0a4ffb06d45d209d4e4bf2e92813082d3f", size = 11400794, upload-time = "2026-05-21T14:34:39.773Z" }, + { url = "https://files.pythonhosted.org/packages/97/c7/83c0539fe34c3e09136204d1e75d6052492364e0b3cb05e9465423f567d7/ruff-0.15.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:49072d36abdbe97a8dd7f480afe9c675699c0c495d4c84076e2c1203c4550581", size = 10804759, upload-time = "2026-05-21T14:34:31.045Z" }, + { url = "https://files.pythonhosted.org/packages/86/a6/18f2bfc095a2ab4a78745644e428205532ce6653a5d0fa8501572891534d/ruff-0.15.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:958522aee105068640c2c2ceae08f413ae44d922f52a1374ac13d6a96032fc93", size = 10539517, upload-time = "2026-05-21T14:34:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/54/3a/5a8b3b69c654d4e4bf1d246ac5b49cbcdac6eaab6905925f8915f31e3b80/ruff-0.15.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f3707da619a143a2e8830e2abab8224478d69ace2d28cb6c20543ae97c36bf61", size = 11065169, upload-time = "2026-05-21T14:34:24.484Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c5/8864e4e7925b836ea354b31d57641ec03830564e281a8b6f061f8c3e0ec1/ruff-0.15.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bb01d645694e3ec0102105d07ef2d53703970407d59c04e59d3ba0b7a1d53553", size = 11560214, upload-time = "2026-05-21T14:34:50.975Z" }, + { url = "https://files.pythonhosted.org/packages/36/38/012bf76752e1f89ed50b77b99532d90f3a3e287bc7918e1fc0948ac866ac/ruff-0.15.14-py3-none-win32.whl", hash = "sha256:6d0c1ad2a0ab718d39b6d8fd2217981ce4d625cd96a720095f798fb47d8b13e6", size = 10805548, upload-time = "2026-05-21T14:34:33.453Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/4ea2c170f10ad760fff2a5250beb18897719dc8b52b53a24cddbb9dd3f19/ruff-0.15.14-py3-none-win_amd64.whl", hash = "sha256:802342981e056db3851a7836e5b070f8f15f67d4a685ae2a6160939d364b2902", size = 11939523, upload-time = "2026-05-21T14:34:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -1341,6 +1360,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, ] +[[package]] +name = "stevedore" +version = "5.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/88/35e4d27d9177d7df76d060e0a18f69c6c5794c96960c94042e20a12c8ba2/stevedore-5.8.0.tar.gz", hash = "sha256:b49867b32ca3016e94100e68dbf26e72aa7b8708d0a3f73c08aeb220370ac715", size = 514710, upload-time = "2026-05-18T09:15:27.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/ac/19f9941c74add59d17694930ec8105d5eddeee4ce56dd8632b765ca16d6c/stevedore-5.8.0-py3-none-any.whl", hash = "sha256:88eede9e66ca80e34085b9174e2327da2c61ac91f24f70e41c3ad76e4bb4872b", size = 54553, upload-time = "2026-05-18T09:15:25.82Z" }, +] + [[package]] name = "tornado" version = "6.5.5" @@ -1358,6 +1386,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, ] +[[package]] +name = "ty" +version = "0.0.39" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/8d/7b5c74dc287fbcb37bae9853cec13bf44717c1735298500e4aeba31579a9/ty-0.0.39.tar.gz", hash = "sha256:f750277e76a01ecd86185960eca73823c26a53c51103568d56d4d904575159fd", size = 5702365, upload-time = "2026-05-22T21:09:56.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/17/9b89802c26d12d0f7a27bc25d4066d941d42891e8898f9f26499f0067e32/ty-0.0.39-py3-none-linux_armv6l.whl", hash = "sha256:c1bb7ac70f1f7d70cc6655fd96558039e4562b10f489fa49c7ebfd5fcee73ad1", size = 11360431, upload-time = "2026-05-22T21:09:18.689Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c6/663ded50e823dbf9fb9d002eca46b7cb1fb2c72b744b84f22ce732a0ee0b/ty-0.0.39-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3435b64c1e59c14c9aa39c20cc018823937cd38d55db853e74d95b8f420569b0", size = 11096281, upload-time = "2026-05-22T21:09:15.383Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ae/5d38ba9a6456ff4c78d212cf464fd8b9a25d8118465197b0b2dc891c0b19/ty-0.0.39-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f136377ce46c73677701a9e1ad730bf72f699bcec046e422eb79d0886cac3ab", size = 10529674, upload-time = "2026-05-22T21:09:46.471Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/43638cb8106445d3c8817256a0731cde9dd7b6a53ae2e881294bc1930ca3/ty-0.0.39-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36b65fb0cc17f03e851d40e210d420be94ab8bc52d041328ad1e45f616036a61", size = 11055561, upload-time = "2026-05-22T21:09:36.981Z" }, + { url = "https://files.pythonhosted.org/packages/91/17/95e62cf4458527ce78dc386eba18f8b10c3fb64cd8c9e7e59b262ff6029d/ty-0.0.39-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4967967bfadf3860ff84c3fccdbaec8edf8aa20d0d727521084733d853de6657", size = 11127185, upload-time = "2026-05-22T21:09:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c0/93666c213db5c71ab1b1f1a0db5f66bf8c7c0e0b0bf59859f5da8f0b3c36/ty-0.0.39-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e10ecb1297099ddf9a1f054f8bd921d1863ce85fb819a3c96ed27865a1ba6ed", size = 11608459, upload-time = "2026-05-22T21:09:12.862Z" }, + { url = "https://files.pythonhosted.org/packages/79/85/3b26585afc8b50230d6464bb0642feef4fab3f847e38b1f0ffa971a81446/ty-0.0.39-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b19cca70e465d71b0510656343883d62372bbe74b7845cae7c0e701d6d5264b", size = 12177101, upload-time = "2026-05-22T21:09:40.519Z" }, + { url = "https://files.pythonhosted.org/packages/49/4a/1039e4f6afc576dc1c3a4d22a6478904a1ad3766597cd0b93c077ab9dfce/ty-0.0.39-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56c6704b01b9b3d80ff26b2918423b742516d1e469bef830e9254dcedc9185bf", size = 11827815, upload-time = "2026-05-22T21:09:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c5/4688652870e350a76a8157f7ffb59ad54f37d5d10725aa7076f66ac94ec8/ty-0.0.39-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b7840ff46764b6a6757f4ade1cd0530fc3e8a0b435ca93e7602360e4cb90b6", size = 11694429, upload-time = "2026-05-22T21:09:21.568Z" }, + { url = "https://files.pythonhosted.org/packages/fc/72/8a1c4e823bb5bdc935a1c8140e100304e36a68a4139592f170aa9736fdb7/ty-0.0.39-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c62a3a87ce26b50819f0dbf03bd95f23f19eeb87bbc7aa732ec64277c77f1aa", size = 11869846, upload-time = "2026-05-22T21:09:28.053Z" }, + { url = "https://files.pythonhosted.org/packages/17/9f/cf982457b861ae22d657c5dcdbc631199f7f90264279db1d17230dfbc3ff/ty-0.0.39-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f8c34bc81a9c3516e49904e9d8330aac385377cca98390193ea02b903a40fcf0", size = 11029763, upload-time = "2026-05-22T21:09:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/95b64f6d43ae6e8f0b7e13dacf9c196d35819af22b1924171fba31383156/ty-0.0.39-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:66f5ab11586a64e79cb692ad685ee5469325c31b5f30bd3554f52f36dbe28cc4", size = 11146761, upload-time = "2026-05-22T21:09:10.178Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/0a89cfb06f7632a05bf56c78e0affb4a40f81759e275376cea75c9c5abe9/ty-0.0.39-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e8d89732bcbbcb091f439e556dfc4932f198b118b47d5b85212c60662099670e", size = 11281843, upload-time = "2026-05-22T21:09:34.234Z" }, + { url = "https://files.pythonhosted.org/packages/0e/53/64c4a27067a46643fea2b3fcf21a8a2f838d91a65ffdd14f2e82945b9538/ty-0.0.39-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:eceb6c91dcd05a231119f82abdd9aa337513de23ca6ac990bc44f88791dc1799", size = 11792477, upload-time = "2026-05-22T21:09:24.923Z" }, + { url = "https://files.pythonhosted.org/packages/1a/e8/02f4dd4a12bcdbda0006f9c7ff3b99a4be06bd0d257d3bd4a5b66de074e6/ty-0.0.39-py3-none-win32.whl", hash = "sha256:891c3262314dbc80bf3e872634d23dd216306945daa9a9fcc206ce5ed21ac4c9", size = 10615377, upload-time = "2026-05-22T21:09:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b5/5a/aaeb22faa8d4dae90a287d4c3636c671edcff3b99be5f4fc8b79ad71eef6/ty-0.0.39-py3-none-win_amd64.whl", hash = "sha256:ba7f2d54452535419e90f6f03ff39282999e87b43c21c00559f6d7ad711a36d5", size = 11710711, upload-time = "2026-05-22T21:09:53.179Z" }, + { url = "https://files.pythonhosted.org/packages/a3/17/ae7339651bfcaa5f54698c8c70eaf5031baa400ecb67baec31d03a56cbd4/ty-0.0.39-py3-none-win_arm64.whl", hash = "sha256:eb4cf0fefbbfedf9a352597bb2431ebdcb7eb3a595c0f825f228e897a0ec285d", size = 11081409, upload-time = "2026-05-22T21:09:03.741Z" }, +] + [[package]] name = "tzdata" version = "2026.2" diff --git a/README.md b/README.md index 9fceab6..e770e65 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ 1. Download repository. 2. Set .env file based on template. 3. Install Docker Desktop. -4. Run containters: +4. Run containers: ``` docker-compose up -d --build ``` @@ -78,7 +78,7 @@ ``` pre-commit install ``` -7. Run containters: +7. Run containers: ``` docker-compose up -d --build ``` diff --git a/doc/01_adr_functionality.md b/doc/01_adr_functionality.md index dff92df..b4f56c0 100644 --- a/doc/01_adr_functionality.md +++ b/doc/01_adr_functionality.md @@ -5,15 +5,15 @@ `2023-06-04` -### Status +### Status In-building -### Context +### Context We need to create a list of main functions to define business justification of first version of an application and decide what functionality should be canceled or suspended to further implementation in next releases. -### Decision +### Decision A first brainstorm created a list of basic functions expectted to implement: - Create a databases to contain at least data like: @@ -25,18 +25,18 @@ A first brainstorm created a list of basic functions expectted to implement: - Generate static diagrams on button demand, like a charts of weight and consumed amount of medicines, - Creat sticky notes kanboard to manage a feeding period per purchased food, - Sending visit notifications via at least one of: sms, whatsap, messenger, e-mail, discord, -- Printable notes and charts into pdf reports, +- Printable notes and charts into pdf reports, - Synchronization into a google calendar, - Basic API to consider a transfer of charts into Dash-Plotly. List to-do's suspended until next iterations of application: - Interactive dashboards, -- Implementation all of proposed notification methods, +- Implementation all of proposed notification methods, - Direct chat between users, without using animal's notes. -### Consequences -An effortful list of functionality has been created to exercise a building process of web applications. +### Consequences +An effortful list of functionality has been created to exercise a building process of web applications. The demands have been divided into quickly attainable goals, leaving a basic draft of a further development. diff --git a/doc/02_adr_django.md b/doc/02_adr_django.md index adb99b8..454a427 100644 --- a/doc/02_adr_django.md +++ b/doc/02_adr_django.md @@ -5,35 +5,35 @@ `2023-06-05` -### Status +### Status Done -### Context +### Context We need to choose a main web framework for the project to create the core of the application.\ Considered technologies: -- [x] Django, +- [x] Django, - [ ] Flask, - [ ] Dash Plotly. -### Decision +### Decision Django was selected. The developer has the most recent experience and the desire to systematize knowledge. Flask, as a microframework, could extend the time to the first working prototype. -Dash is a derivative framework for Flask with extensive features for generating interactive dashboards. +Dash is a derivative framework for Flask with extensive features for generating interactive dashboards. This functionality has been postponed to a later stage of the application development. -### Consequences +### Consequences An expected short time to prepare the first working prototype. With good community support, plugins supporting specific functionalities should be available (ORM, logging, api, etc.). ### Keywords - Django, -- Flask, +- Flask, - Dash Plotly, - web framework. diff --git a/doc/03_adr_monolit.md b/doc/03_adr_monolit.md index 4f42667..fbd98a4 100644 --- a/doc/03_adr_monolit.md +++ b/doc/03_adr_monolit.md @@ -5,23 +5,23 @@ `2023-06-05` -### Status +### Status Done -### Context +### Context We need to choose an approach to building the project's application structure.\ -Considered approaches: +Considered approaches: - [x] Monolith, - [ ] Microservices. -### Decision -Due to the selection of Django as the main framework, an application in the monolithic architecture will be created, +### Decision +Due to the selection of Django as the main framework, an application in the monolithic architecture will be created, open via APIs to the possibility of adding selective functionalities in the form of microservices. -### Consequences +### Consequences Each new functionality will need to be considered to determine it will be easier to implement as a fragment of the monolith or as a new microservice. diff --git a/doc/04_adr_monorepo.md b/doc/04_adr_monorepo.md index 86d7b64..a9da6d9 100644 --- a/doc/04_adr_monorepo.md +++ b/doc/04_adr_monorepo.md @@ -5,13 +5,13 @@ `2023-06-05` -### Status +### Status Done -### Context +### Context We need to choose an approach to building and maintaining the repositories and branches.\ -Considered approaches: +Considered approaches: - [x] Monorepo, - [ ] Polirepo, --- @@ -21,14 +21,14 @@ Considered approaches: - [ ] Trunk-based development. -### Decision +### Decision The Monorepo approach will be used due to the small number of developers and the expected number of parallel branches. -The number of developers also affects the decision to manage branches and approach to deployment. -GitHub-Flow was selected. In a small organization, a least detailed approach will suffice. +The number of developers also affects the decision to manage branches and approach to deployment. +GitHub-Flow was selected. In a small organization, a least detailed approach will suffice. -### Consequences +### Consequences Possible future migrations will be easier in the direction from simpler to more complicated. diff --git a/doc/05_adr_matlibplot.md b/doc/05_adr_matlibplot.md index cc3f0fa..8f171bc 100644 --- a/doc/05_adr_matlibplot.md +++ b/doc/05_adr_matlibplot.md @@ -5,13 +5,13 @@ `2023-06-05` -### Status +### Status Proposed -### Context +### Context We need to choose a technology to create charts for the application.\ -Considered approaches: +Considered approaches: - [x] Static charts: - [x] Matplotlib, @@ -21,12 +21,12 @@ Considered approaches: - [x] Chart.js, -### Decision -To avoid the proliferation of microservices, a decision has been made to prototype using a static method of generating charts. +### Decision +To avoid the proliferation of microservices, a decision has been made to prototype using a static method of generating charts. The presented data is not expected to require frequent refreshing and filtering of the range. -### Consequences +### Consequences A faster development process and the possibility of future functionality replacement. After preparing the static prototype, tests with Chart.js will be carried out and the cost of implementation will be estimated. diff --git a/doc/06_adr_html_template.md b/doc/06_adr_html_template.md index 9caf563..62dc2db 100644 --- a/doc/06_adr_html_template.md +++ b/doc/06_adr_html_template.md @@ -5,19 +5,19 @@ `2023-06-05` -### Status +### Status Done -### Context +### Context We need to choose a main template for frontend part of the project. -### Decision +### Decision pass -### Consequences +### Consequences pass ### Keywords diff --git a/doc/07_adr_drf.md b/doc/07_adr_drf.md index 5e2c3db..45335b2 100644 --- a/doc/07_adr_drf.md +++ b/doc/07_adr_drf.md @@ -5,23 +5,23 @@ `2023-06-05` -### Status +### Status Proposed -### Context +### Context We need to choose a basic API framework to inside and outside communication.\ -Considered approaches: +Considered approaches: - Django REST Framework, - Django Ninja, - FastAPI, - GraphQL. -### Decision +### Decision pass -### Consequences +### Consequences pass diff --git a/doc/08_adr_databases.md b/doc/08_adr_databases.md index 18d6162..9c7e12a 100644 --- a/doc/08_adr_databases.md +++ b/doc/08_adr_databases.md @@ -5,13 +5,13 @@ `2023-06-05` -### Status +### Status In-building -### Context +### Context We need to choose a database for a specific task within the application.\ -Considered DBMS: +Considered DBMS: - [x] PostgreSQL, - [ ] MS SQL, - [ ] MySQL, @@ -22,7 +22,7 @@ Considered DBMS: - [x] CouchDB. -### Decision +### Decision Tree databases have been selected for routing testing. PostgreSQL - quick database creation and configuration with a good SQL interface. It has many use cases with Django. @@ -32,8 +32,8 @@ CouchDB - native support for files as attachments. Non-relational database, inte Redis - default broker for Celery queue. -### Consequences -In basic form database routing is required. +### Consequences +In basic form database routing is required. The implementation should be quick, as the second database will be used only for storing attachment files. @@ -53,11 +53,11 @@ Homepages: https://www.sqlite.org/index.html https://www.mongodb.com/ - + https://redis.io/ - + https://firebirdsql.org/ - + https://couchdb.apache.org/ *[2023-01-24]*\ diff --git a/doc/09_adr_user_data.md b/doc/09_adr_user_data.md index 49839c4..257e8df 100644 --- a/doc/09_adr_user_data.md +++ b/doc/09_adr_user_data.md @@ -5,13 +5,13 @@ `2023-07-09` -### Status +### Status In-building -### Context +### Context We need to set up a place in documentation to list all collect all data about users and group them by correct place in databases and direct tables. -Main sections of data by sources: +Main sections of data by sources: - user, - animal, - medical record note, @@ -19,14 +19,14 @@ Main sections of data by sources: - medicines, - medical facility, - veterinarian, -- dates sheduling, +- dates scheduling, - costs counting. -### Decision +### Decision User datatables: - collected by the registration process: - - basic provided by user informations: + - basic provided by user information: - name, - email, - password, @@ -34,21 +34,21 @@ User datatables: - date of registration, - default profile image, - default background image, - - default user priviliges (viever, owner, creator, moderator, admin etc.) + - default user privileges (viewer, owner, creator, moderator, admin etc.) - collected after the registration process: - provided in profil page: - profile image, - - bacground image choosen, + - background image chosen, - email-change, - password-change, - date of birthday, - stable view(compedium of animals: owned and cared) - connections other models: - - animal - ovner, viewer, - - medical record nove - participation in visit + - animal - owner, viewer, + - medical record note - participation in visit - medical_place_id, - note_ - - + - Medical records (animal timeline) @@ -73,7 +73,7 @@ Medical records (animal timeline) -### Consequences +### Consequences ##### _Placeholder_ diff --git a/doc/10_adr_notification_trigger.md b/doc/10_adr_notification_trigger.md index fc9047c..72f9713 100644 --- a/doc/10_adr_notification_trigger.md +++ b/doc/10_adr_notification_trigger.md @@ -5,11 +5,11 @@ `2023-12-19` -### Status +### Status In-building -### Context +### Context We need to choose a technology for sending set by users notifications. The basic channel for sending notifications include: - e-mail, @@ -25,11 +25,11 @@ Options: - django-cron. -### Decision +### Decision Django-crontab -### Consequences +### Consequences 1. **Integration with Django:** django-crontab is a Django extension, making it a natural choice for seamlessly scheduling tasks in a Django-based application. This integration facilitates code maintenance and management. @@ -43,7 +43,7 @@ Django-crontab ### Keywords - Celery, - Cronjobs, -- queue, +- queue, - broker, - subscriptions,. From 90670ec2108ea5eae4683d05caebe72728af975e Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:06:52 +0200 Subject: [PATCH 04/31] refactor(tests): migrate test runner to pytest with markers --- .github/workflows/django.yml | 8 +- AHC_app/AHC_app/settings.py | 9 +- AHC_app/conftest.py | 0 AHC_app/homepage/tests/test_homepage.py | 3 + AHC_app/pyproject.toml | 13 +++ AHC_app/uv.lock | 105 ++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 AHC_app/conftest.py diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index fbbc571..37672a8 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -47,13 +47,17 @@ jobs: python-version: "3.14" - name: Install dependencies - run: uv sync --no-group dev + run: uv sync working-directory: ./AHC_app - - name: Run tests + - name: Run tests (Django runner) run: PYTHONPATH=$(pwd) uv run python manage.py test working-directory: ./AHC_app + - name: Run tests (pytest) + run: uv run pytest -m "not slow" + working-directory: ./AHC_app + build-and-push: name: Build and Push to ECR runs-on: ubuntu-latest diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 45bcaf3..4ddaed6 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -17,6 +17,11 @@ import pycouchdb from decouple import config + +def _is_test_run() -> bool: + return "test" in sys.argv or (bool(sys.argv) and "pytest" in sys.argv[0]) + + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -94,7 +99,7 @@ # Database # https://docs.djangoproject.com/en/4.2/ref/settings/#databases -if "test" in sys.argv: +if _is_test_run(): DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", @@ -115,7 +120,7 @@ # COUCH_CONNECTOR = (config("COUCH_CONNECTOR"),) -if "test" not in sys.argv: +if not _is_test_run(): COUCHDB_USER = config("COUCHDB_USER") COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") COUCHDB_PORT = config("COUCHDB_PORT") diff --git a/AHC_app/conftest.py b/AHC_app/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/AHC_app/homepage/tests/test_homepage.py b/AHC_app/homepage/tests/test_homepage.py index fa3783f..ac3ac0b 100644 --- a/AHC_app/homepage/tests/test_homepage.py +++ b/AHC_app/homepage/tests/test_homepage.py @@ -1,5 +1,6 @@ from html.parser import HTMLParser +import pytest from django.contrib.auth.models import User from django.test import Client, TestCase @@ -18,6 +19,8 @@ def handle_starttag(self, tag, attrs): self.found_href = True +@pytest.mark.integration +@pytest.mark.django_db class TestHomepage(TestCase): def setUp(self) -> None: my_user = User.objects.create(username="test_user_placeholder") diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml index f93635a..843520b 100644 --- a/AHC_app/pyproject.toml +++ b/AHC_app/pyproject.toml @@ -46,6 +46,9 @@ dev = [ "ty", "codespell", "bandit[toml]", + "pytest", + "pytest-django", + "pytest-cov", ] [tool.uv] @@ -66,6 +69,16 @@ ignore = [ quote-style = "double" indent-style = "space" +[tool.pytest.ini_options] +DJANGO_SETTINGS_MODULE = "AHC_app.settings" +python_files = ["test_*.py", "tests.py"] +addopts = "--strict-markers" +markers = [ + "unit: fast isolated tests with no DB access", + "integration: tests that use the DB or external services", + "slow: tests that take more than a few seconds", +] + [tool.ty.environment] python-version = "3.14" diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock index 4c03322..e2de1ba 100644 --- a/AHC_app/uv.lock +++ b/AHC_app/uv.lock @@ -130,6 +130,9 @@ dev = [ { name = "bandit" }, { name = "codespell" }, { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-django" }, { name = "ruff" }, { name = "ty" }, ] @@ -175,6 +178,9 @@ dev = [ { name = "bandit", extras = ["toml"] }, { name = "codespell" }, { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-django" }, { name = "ruff" }, { name = "ty" }, ] @@ -458,6 +464,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "coverage" +version = "7.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/7f/d0720730a397a999ffc0fd3f5bebef347338e3a47b727da66fbb228e2ff2/coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74", size = 919489, upload-time = "2026-05-10T18:02:31.397Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/18/b9a6586d73992807c26f9a5f274131be3d76b56b18a82b9392e2a25d2e45/coverage-7.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9aed9fa983514ca032790f3fe0d1c0e42ca7e16b42432af1706b50a9a46bef5d", size = 220036, upload-time = "2026-05-10T18:01:33.057Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9b/4165a1d56ddc302a0e2d518fd9d412a4fd0b57562618c78c5f21c57194f5/coverage-7.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ba3b8390db29296dbbf49e91b6fe08f990743a90c8f447ba4c2ffc29670dfa63", size = 220368, upload-time = "2026-05-10T18:01:34.705Z" }, + { url = "https://files.pythonhosted.org/packages/69/aa/c12e52a5ba148d9995229d557e3be6e554fe469addc0e9241b2f0956d8ea/coverage-7.14.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3a5d8e876dfa2f102e970b183863d6dedd023d3c0eeca1fe7a9787bc5f28b212", size = 251417, upload-time = "2026-05-10T18:01:36.949Z" }, + { url = "https://files.pythonhosted.org/packages/d7/51/ec641c26e6dca1b25a7d2035ba6ecb7c884ef1a100a9e42fbe4ce4405139/coverage-7.14.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ebb8f4614a3787d567e610bbfdf96a4798dd69a1afb1bd8ad228d4111fe6ff3", size = 253924, upload-time = "2026-05-10T18:01:38.985Z" }, + { url = "https://files.pythonhosted.org/packages/33/c4/59c3de0bd1b538824173fd518fed51c1ce740ca5ed68e74545983f4053a9/coverage-7.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b9bf47223dd8db3d4c4b2e443b02bace480d428f0822c3f991600448a176c97", size = 255269, upload-time = "2026-05-10T18:01:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/36dfa153a62040296f6e7febfdb20a5720622f6ef5a81a41e8237b9a5344/coverage-7.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3485a836550b303d006d57cc06e3d5afaabc642c77050b7c985a97b13e3776b8", size = 257583, upload-time = "2026-05-10T18:01:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/26/7b/cc2c048d4114d9ab1c2409e9ee365e5ae10736df6dffcfc9444effa6c708/coverage-7.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e7e88110bae996d199d1693ca8ec3fd52441d426401ae963437598667b4c5eb", size = 251434, upload-time = "2026-05-10T18:01:44.537Z" }, + { url = "https://files.pythonhosted.org/packages/ee/df/6770eaa576e604575e9a78055313250faef5faa84bd6f71a39fece519c43/coverage-7.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15228a6800ce7bdf1b74800595e56db7138cecb338fdbf044806e10dcf182dfe", size = 253280, upload-time = "2026-05-10T18:01:46.175Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9e/1c0264514a3f98259a6d64765a397b2c8373e3ba59ee722a4802d3ec0c61/coverage-7.14.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d26ac7f5398bafc5b57421ad994e8a4749e8a7a0e62d05ec7d53014d5963bfa", size = 251241, upload-time = "2026-05-10T18:01:48.732Z" }, + { url = "https://files.pythonhosted.org/packages/64/16/4efdf3e3c4079cdbf0ece56a2fea872df9e8a3e15a13a0af4400e1075944/coverage-7.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb73254ff43c911c967a899e1359bc5049b4b115d6e8fbdde4937d0a2246cd5", size = 255516, upload-time = "2026-05-10T18:01:50.819Z" }, + { url = "https://files.pythonhosted.org/packages/93/69/b1de96346603881b3d1bc8d6447c83200e1c9700ffbaff926ba01ff5724c/coverage-7.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:454a380af72c6adada298ed270d38c7a391288198dbfb8467f786f588751a90c", size = 251059, upload-time = "2026-05-10T18:01:52.773Z" }, + { url = "https://files.pythonhosted.org/packages/a4/66/2881853e0363a5e0a724d1103e53650795367471b6afb234f8b49e713bc6/coverage-7.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:65c86fb646d2bd2972e96bd1a8b45817ed907cee68655d6295fe7ec031d04cca", size = 252716, upload-time = "2026-05-10T18:01:54.506Z" }, + { url = "https://files.pythonhosted.org/packages/55/5c/0d3305d002c41dcde873dbe456491e663dc55152ca526b630b5c47efd62f/coverage-7.14.0-cp314-cp314-win32.whl", hash = "sha256:6a6516b02a6101398e19a3f44820f69bab2590697f7def4331f668b14adaf828", size = 222788, upload-time = "2026-05-10T18:01:56.487Z" }, + { url = "https://files.pythonhosted.org/packages/f9/58/6e1b8f52fdc3184b47dc5037f5070d83a3d11042db1594b02d2a44d786c8/coverage-7.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:45e0f79d8351fa76e256716df91eab12890d32678b9590df7ae1042e4bd4cf5d", size = 223600, upload-time = "2026-05-10T18:01:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/00/70/a18c408e674bc26281cadaedc7351f929bd2094e191e4b15271c30b084cc/coverage-7.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:4b899594a8b2d81e5cc064a0d7f9cac2081fed91049456cae7676787e41549c9", size = 222168, upload-time = "2026-05-10T18:02:00.411Z" }, + { url = "https://files.pythonhosted.org/packages/3d/89/2681f071d238b62aff8dfc2ab44fc24cfdb38d1c01f391a80522ff5d3a16/coverage-7.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f580f8c80acd94ac72e863efe2cab791d8c38d153e0b463b92dfa000d5c84cd1", size = 220766, upload-time = "2026-05-10T18:02:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c7/c987babafd9207ffa1995e1ef1f9b26762cf4963aa768a66b6f0501e4616/coverage-7.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a2bd259c442cd43c49b30fbafc51776eb19ea396faf159d26a83e6a0a5f13b0c", size = 221035, upload-time = "2026-05-10T18:02:04.017Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e9/d6a5ac3b333088143d6fc877d398a9a674dc03124a2f776e131f03864823/coverage-7.14.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a706b908dfa85538863504c624b237a3cc34232bf403c057414ebfdb3b4d9f84", size = 262405, upload-time = "2026-05-10T18:02:05.915Z" }, + { url = "https://files.pythonhosted.org/packages/38/b1/e70838d29a7c08e22d44398a46db90815bbcbf28de06992bd9210d1a8d8e/coverage-7.14.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7333cd944ee4393b9b3d3c1b598c936d4fc8d70573a4c7dacfec5590dd50e436", size = 264530, upload-time = "2026-05-10T18:02:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/6b/73/5c31ef97763288d03d9995152b96d5475b527c63d91c84b01caea894b83a/coverage-7.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f162bc9a15b82d947b02651b0c7e1609d6f7a8735ca330cfadec8481dd97d5a", size = 266932, upload-time = "2026-05-10T18:02:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/e1/76/dd56d80f29c5f05b4d76f7e7c6d47cafacae017189c75c5759d24f9ff0cc/coverage-7.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:362cb78e01a5dc82009d88004cf60f2e6b6d6fcbfdec05b05af73b0abf40118f", size = 268062, upload-time = "2026-05-10T18:02:11.399Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c7/27ba85cd5b95614f159ff93ebff1901584a8d192e2e5e24c4943a7453f59/coverage-7.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:acebd068fca5512c3a6fde9c045f901613478781a73f0e82b307b214daef23fb", size = 261504, upload-time = "2026-05-10T18:02:13.257Z" }, + { url = "https://files.pythonhosted.org/packages/13/2e/e8149f60ab5d5684c6eee881bdf34b127115cddbb958b196768dd9d63473/coverage-7.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:29fe3da551dface75deb2ccbf87b6b66e2e7ef38f6d89050b428be94afff3490", size = 264398, upload-time = "2026-05-10T18:02:15.063Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7f/1261b025285323225f4b4abffa5a643649dfd67e25ddca7ebcbdea3b7cb3/coverage-7.14.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b4cc4fce8672fffcb09b0eafc167b396b3ba53c4a7230f54b7aaffbf6c835fa9", size = 262000, upload-time = "2026-05-10T18:02:16.756Z" }, + { url = "https://files.pythonhosted.org/packages/d3/dc/829c54f60b9d08389439c00f813c752781c496fc5788c78d8006db4b4f2b/coverage-7.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5d4a51aad8ba8bdcd2b8bd8f03d4aca19693fa2327a3470e4718a25b03481020", size = 265732, upload-time = "2026-05-10T18:02:18.817Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b0/70bd1419941652fa062689cba9c3eeafb8f5e6fbb890bce41c3bdda5dbd6/coverage-7.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9f323af3e1e4f68b60b7b247e37b8515563a61375518fa59de1af48ba28a3db6", size = 260847, upload-time = "2026-05-10T18:02:20.528Z" }, + { url = "https://files.pythonhosted.org/packages/f2/73/be40b2390656c654d35ea0015ea7ba3d945769cf80790ad5e0bb2d56d2ba/coverage-7.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1a0abc7342ea9711c469dd8b821c6c311e6bc6aac1442e5fbd6b27fae0a8f3db", size = 263166, upload-time = "2026-05-10T18:02:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/29/55/4a643f712fcf7cf2881f8ec1e0ccb7b164aff3108f69b51801246c8799f2/coverage-7.14.0-cp314-cp314t-win32.whl", hash = "sha256:a9f864ef57b7172e2db87a096642dd51e179e085ab6b2c371c29e885f65c8fb2", size = 223573, upload-time = "2026-05-10T18:02:24.11Z" }, + { url = "https://files.pythonhosted.org/packages/27/96/3acae5da0953be042c0b4dea6d6789d2f080701c77b88e44d5bd41b9219b/coverage-7.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29943e552fdc08e082eb51400fb2f58e118a83b5542bd06531214e084399b644", size = 224680, upload-time = "2026-05-10T18:02:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/6ab5d2dd8325d838737c6f8d83d62eb6230e0d70b87b51b57bbfd08fa767/coverage-7.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:742a73ea621953b012f2c4c2219b512180dd84489acf5b1596b0aafc55b9100b", size = 222703, upload-time = "2026-05-10T18:02:27.822Z" }, + { url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" }, +] + [[package]] name = "crispy-bootstrap4" version = "2026.2" @@ -819,6 +864,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, ] +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + [[package]] name = "kombu" version = "5.6.2" @@ -982,6 +1036,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, ] +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + [[package]] name = "pre-commit" version = "4.6.0" @@ -1146,6 +1209,48 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, ] +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "pytest-django" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/2b/db9a193df89e5660137f5428063bcc2ced7ad790003b26974adf5c5ceb3b/pytest_django-4.12.0.tar.gz", hash = "sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758", size = 91156, upload-time = "2026-02-14T18:40:49.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/a5/41d091f697c09609e7ef1d5d61925494e0454ebf51de7de05f0f0a728f1d/pytest_django-4.12.0-py3-none-any.whl", hash = "sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85", size = 26123, upload-time = "2026-02-14T18:40:47.381Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" From 70655b5b5571761b75ba11713253249b4c0fd289 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:09:58 +0200 Subject: [PATCH 05/31] chore(toolchain): add justfile with dev task recipes --- AHC_app/justfile | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 AHC_app/justfile diff --git a/AHC_app/justfile b/AHC_app/justfile new file mode 100644 index 0000000..87dccf8 --- /dev/null +++ b/AHC_app/justfile @@ -0,0 +1,52 @@ +set shell := ["pwsh", "-NoLogo", "-Command"] + +# Install all dependencies including dev group +install: + uv sync + +# Run all linters (ruff check, codespell, bandit) +lint: + uv run ruff check . + uv run codespell + uv run bandit -r . -c pyproject.toml + +# Format code and auto-fix lint issues +format: + uv run ruff format . + uv run ruff check --fix . + +# Run all tests excluding slow +test: + uv run pytest -m "not slow" + +# Run only unit tests +test-unit: + uv run pytest -m unit -v + +# Run only integration tests +test-integration: + uv run pytest -m integration -v + +# Start Django development server +runserver: + uv run python manage.py runserver + +# Apply database migrations +migrate: + uv run python manage.py migrate + +# Open Django shell +shell: + uv run python manage.py shell + +# Start all Docker services +docker-up: + docker-compose up -d --build + +# Stop all Docker services +docker-down: + docker-compose down + +# Run pre-commit hooks on all files +precommit: + uv run pre-commit run --all-files From 9be91b8634d71d642d526511797f7acd4314c492 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:15:05 +0200 Subject: [PATCH 06/31] refactor(settings): migrate STATICFILES_STORAGE to STORAGES for Django 5.1+ --- AHC_app/AHC_app/settings.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 4ddaed6..3c5dc3e 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -184,7 +184,14 @@ def _is_test_run() -> bool: COMPRESS_PRECOMPILERS = (("text/x-scss", "django_libsass.SassCompiler"),) COMPRESS_OFFLINE = True LIBSASS_OUTPUT_STYLE = "compressed" -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + }, +} """ DOCUMENTATION TO CUSTOM SCSS: https://picocss.com/docs/customization.html From 33c4a0137671338ff5862a0a331d5d08f462bd38 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:17:33 +0200 Subject: [PATCH 07/31] docs(readme): update stack references from Pipenv to uv and pytest --- README.md | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e770e65..4688021 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,13 @@ --- ### Requirements: -- Python 3.12.2 +- Python 3.14 +- [uv](https://docs.astral.sh/uv/) (package manager) +- [just](https://just.systems/) (task runner, optional) - Docker & Docker Compose - PostgreSQL 15 (instance for volumes) - Apache CouchDB 3.3.3 (instance for volumes) -- [Packages](AHC_app/Pipfile) +- [Packages](AHC_app/pyproject.toml) - [pico-1.5.10](https://github.com/picocss/pico/archive/refs/tags/v1.5.10.zip) --- @@ -65,24 +67,30 @@ ### Dev-instance steps: 1. Download repository. 2. Set .env file based on the template. -3. Install Python, Docker Desktop, PostgreSQL and CouchDB as in _Requirements_. -4. Install pipenv: +3. Install Python 3.14, Docker Desktop, PostgreSQL and CouchDB as in _Requirements_. +4. Install uv and sync dependencies: ``` - pipenv install + pip install uv + cd AHC_app + uv sync ``` -5. Deploy vevn and synch requirements: +5. Install pre-commit hooks: ``` - pipenv install --dev + uv run pre-commit install ``` -6. Install precommit hooks: - ``` - pre-commit install - ``` -7. Run containers: +6. Run containers: ``` docker-compose up -d --build ``` +With `just` installed, steps 4–6 simplify to: +``` +cd AHC_app +just install +just precommit +just docker-up +``` + --- ### Kubernetes Deploy steps (alternative deploy): 1. Download repository. @@ -127,8 +135,18 @@ --- ### Test running: -- by now tests are only reachable by terminal in main container's terminal (container_name: web) -- simply run command "python manage.py test" or use with needed flags +```bash +# pytest (recommended) +cd AHC_app +uv run pytest -m integration + +# or with just +just test +just test-integration + +# Django runner (legacy, still supported) +uv run python manage.py test +``` --- ### Sources: From 7f80bfdc68f476b1ee330a1b1f33e6345451af89 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:23:17 +0200 Subject: [PATCH 08/31] chore(infra): align PostgreSQL image to 18-alpine across docker-compose and k8s --- AHC_app/docker-compose.yml | 2 +- kubernetes/db/postgres/deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AHC_app/docker-compose.yml b/AHC_app/docker-compose.yml index fdd7181..016aeaf 100644 --- a/AHC_app/docker-compose.yml +++ b/AHC_app/docker-compose.yml @@ -32,7 +32,7 @@ services: python manage.py runserver 0.0.0.0:8000 postgres_db: - image: postgres:15-alpine + image: postgres:18-alpine container_name: main-db environment: - POSTGRES_DB=${POSTGRES_DB} diff --git a/kubernetes/db/postgres/deployment.yaml b/kubernetes/db/postgres/deployment.yaml index 886dd56..19dc611 100644 --- a/kubernetes/db/postgres/deployment.yaml +++ b/kubernetes/db/postgres/deployment.yaml @@ -17,7 +17,7 @@ spec: spec: containers: - name: postgres-db - image: postgres:16 + image: postgres:18-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 5433 From 8be4f60fd82f2cc29c3260e433d903cc0fbfb243 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 20:51:26 +0200 Subject: [PATCH 09/31] refactor(settings): skip CouchDB init for offline manage.py commands --- AHC_app/AHC_app/celery_notifications/cron.py | 6 ----- AHC_app/AHC_app/settings.py | 24 +++++++++++++++++++- AHC_app/pyproject.toml | 1 - AHC_app/uv.lock | 2 -- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/AHC_app/AHC_app/celery_notifications/cron.py b/AHC_app/AHC_app/celery_notifications/cron.py index 4a197fa..059fef4 100644 --- a/AHC_app/AHC_app/celery_notifications/cron.py +++ b/AHC_app/AHC_app/celery_notifications/cron.py @@ -85,12 +85,6 @@ def send_emails() -> None: for notification in notifications_to_send: _user_set_zone: str = notification.timezone - # user_weekday_number: int = datetime.now( - # tz=pytz.timezone(user_set_zone) - # ).weekday() - # - # if user_weekday_number in notification.related_note.days_of_week: - # break email: str = notification.email animal: str = notification.related_note.related_note.animal diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 3c5dc3e..b8f7095 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -17,11 +17,33 @@ import pycouchdb from decouple import config +_OFFLINE_COMMANDS = { + "check", + "collectstatic", + "compress", + "crontab", + "makemigrations", + "migrate", + "shell", + "showmigrations", + "sqlmigrate", + "test", +} + def _is_test_run() -> bool: return "test" in sys.argv or (bool(sys.argv) and "pytest" in sys.argv[0]) +def _skip_external_services() -> bool: + """Return True when live network backends (CouchDB) must not be initialised. + + Covers: pytest invocations, manage.py test, and any manage.py command that + does not require a running CouchDB connection at import time. + """ + return _is_test_run() or any(cmd in sys.argv for cmd in _OFFLINE_COMMANDS) + + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -120,7 +142,7 @@ def _is_test_run() -> bool: # COUCH_CONNECTOR = (config("COUCH_CONNECTOR"),) -if not _is_test_run(): +if not _skip_external_services(): COUCHDB_USER = config("COUCHDB_USER") COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") COUCHDB_PORT = config("COUCHDB_PORT") diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml index 843520b..fc31cc6 100644 --- a/AHC_app/pyproject.toml +++ b/AHC_app/pyproject.toml @@ -27,7 +27,6 @@ dependencies = [ "django-taggit", "django-timezone-field>=6.1", "django-crontab", - "pytz", "tzdata", "python3-openid", "requests", diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock index e2de1ba..15c1aef 100644 --- a/AHC_app/uv.lock +++ b/AHC_app/uv.lock @@ -118,7 +118,6 @@ dependencies = [ { name = "python-dateutil" }, { name = "python-decouple" }, { name = "python3-openid" }, - { name = "pytz" }, { name = "redis" }, { name = "requests" }, { name = "requests-oauthlib" }, @@ -166,7 +165,6 @@ requires-dist = [ { name = "python-dateutil" }, { name = "python-decouple" }, { name = "python3-openid" }, - { name = "pytz" }, { name = "redis", specifier = ">=5.0" }, { name = "requests" }, { name = "requests-oauthlib" }, From 079c397d0199fce245e8788e4810b500bbfef6f7 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 21:13:19 +0200 Subject: [PATCH 10/31] refactor(celery): replace django-crontab with Celery Beat for discord notifications --- .../AHC_app/celery_notifications/config.py | 16 +++++++ AHC_app/AHC_app/settings.py | 10 ----- AHC_app/Dockerfile-web | 3 -- AHC_app/docker-compose.yml | 21 ++++++++-- AHC_app/homepage/management/__init__.py | 1 - .../management/commands/sync_cronjobs.py | 24 ----------- AHC_app/pyproject.toml | 1 - AHC_app/uv.lock | 11 ----- kubernetes/queue/celery-beat/deployment.yaml | 42 +++++++++++++++++++ .../queue/celery-beat/kustomization.yaml | 2 + kubernetes/queue/kustomization.yaml | 1 + 11 files changed, 78 insertions(+), 54 deletions(-) delete mode 100644 AHC_app/homepage/management/commands/sync_cronjobs.py create mode 100644 kubernetes/queue/celery-beat/deployment.yaml create mode 100644 kubernetes/queue/celery-beat/kustomization.yaml diff --git a/AHC_app/AHC_app/celery_notifications/config.py b/AHC_app/AHC_app/celery_notifications/config.py index bcfa1c6..c474b79 100644 --- a/AHC_app/AHC_app/celery_notifications/config.py +++ b/AHC_app/AHC_app/celery_notifications/config.py @@ -1,6 +1,7 @@ import os from celery import Celery, shared_task +from celery.schedules import crontab from celery.utils.log import get_task_logger from django.conf import settings @@ -18,6 +19,21 @@ celery_obj.autodiscover_tasks(["AHC_app"]) +@celery_obj.task(name="ahc.beat.dispatch_discord_notes") +def dispatch_discord_notes(): + from AHC_app.celery_notifications.cron import send_discord_notes + + send_discord_notes() + + +celery_obj.conf.beat_schedule = { + "send-discord-notes-hourly": { + "task": "ahc.beat.dispatch_discord_notes", + "schedule": crontab(minute=6), + }, +} + + @celery_obj.task() def send_email_notifications(*args, **kwargs): recipient_list = kwargs.get("email") diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index b8f7095..5b45f13 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -74,7 +74,6 @@ def _skip_external_services() -> bool: "bootstrap_modal_forms", "compressor", "taggit", - "django_crontab", "homepage.apps.HomepageConfig", "users.apps.UsersConfig", "animals.apps.AnimalsConfig", @@ -233,15 +232,6 @@ def _skip_external_services() -> bool: DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" -CRONJOBS = [ - # ("*/4 * * * *", "AHC_app.celery_notifications.cron.send_emails"), - # ("*/2 * * * *", "AHC_app.celery_notifications.cron.send_email_example"), - # # ('2 * * * *', 'AHC_app.celery_notifications.cron:send_emails'), - # ("4 * * * *", "AHC_app.celery_notifications.cron.send_sms"), - ("6 * * * *", "AHC_app.celery_notifications.cron.send_discord_notes"), -] - - CELERY_BROKER_URL = config("CELERY_BROKER_URL") CELERY_BACKEND = config("CELERY_BACKEND") diff --git a/AHC_app/Dockerfile-web b/AHC_app/Dockerfile-web index cea64ab..c4ca1d3 100644 --- a/AHC_app/Dockerfile-web +++ b/AHC_app/Dockerfile-web @@ -10,9 +10,6 @@ RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ FROM python:3.14-slim AS runtime LABEL authors="AM" -RUN apt-get update && \ - apt-get install -y --no-install-recommends cron && \ - rm -rf /var/lib/apt/lists/* WORKDIR /app COPY --from=builder /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" diff --git a/AHC_app/docker-compose.yml b/AHC_app/docker-compose.yml index 016aeaf..d59a072 100644 --- a/AHC_app/docker-compose.yml +++ b/AHC_app/docker-compose.yml @@ -25,10 +25,6 @@ services: python manage.py makemigrations python manage.py migrate python manage.py collectstatic --noinput - python manage.py crontab remove - python manage.py crontab add - python manage.py sync_cronjobs - python manage.py runcrons python manage.py runserver 0.0.0.0:8000 postgres_db: @@ -91,6 +87,23 @@ services: - PYTHONUNBUFFERED=1 - PYTHONPATH=/app + celery_beat: + build: + context: . + dockerfile: Dockerfile-queue + command: celery -A AHC_app.celery_notifications.config:celery_obj beat -l info + depends_on: + redis: + condition: service_healthy + postgres_db: + condition: service_healthy + environment: + - DJANGO_SETTINGS_MODULE=AHC_app.settings + - PYTHONUNBUFFERED=1 + - PYTHONPATH=/app + env_file: + - .env + redis: image: redis:7-alpine healthcheck: diff --git a/AHC_app/homepage/management/__init__.py b/AHC_app/homepage/management/__init__.py index bd9f50c..e69de29 100644 --- a/AHC_app/homepage/management/__init__.py +++ b/AHC_app/homepage/management/__init__.py @@ -1 +0,0 @@ -from .commands.sync_cronjobs import Command # noqa: F401 diff --git a/AHC_app/homepage/management/commands/sync_cronjobs.py b/AHC_app/homepage/management/commands/sync_cronjobs.py deleted file mode 100644 index fc2ec7e..0000000 --- a/AHC_app/homepage/management/commands/sync_cronjobs.py +++ /dev/null @@ -1,24 +0,0 @@ -import subprocess - -from django.core.management.base import BaseCommand - -from homepage.models import CronJob - - -class Command(BaseCommand): - help = "Sync cronjobs with the actual cron configuration" - - def handle(self, *args, **options): - cronjob_info = subprocess.run(["crontab", "-l"], stdout=subprocess.PIPE, text=True).stdout.splitlines() - - CronJob.objects.all().delete() - - for job_info in cronjob_info: - schedule, command = job_info.split(" /", 1) - function_name = command.strip().split(".")[-1] - cron_job = CronJob(schedule=schedule, command=command) - - cron_job.name = f"Cron Job: {function_name}" - cron_job.last_execution = None - cron_job.next_execution = None - cron_job.save() diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml index fc31cc6..9eb9319 100644 --- a/AHC_app/pyproject.toml +++ b/AHC_app/pyproject.toml @@ -26,7 +26,6 @@ dependencies = [ "django-appconf", "django-taggit", "django-timezone-field>=6.1", - "django-crontab", "tzdata", "python3-openid", "requests", diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock index 15c1aef..ccec51b 100644 --- a/AHC_app/uv.lock +++ b/AHC_app/uv.lock @@ -102,7 +102,6 @@ dependencies = [ { name = "django-bootstrap-modal-forms" }, { name = "django-compressor" }, { name = "django-crispy-forms" }, - { name = "django-crontab" }, { name = "django-libsass" }, { name = "django-taggit" }, { name = "django-timezone-field" }, @@ -149,7 +148,6 @@ requires-dist = [ { name = "django-bootstrap-modal-forms" }, { name = "django-compressor" }, { name = "django-crispy-forms" }, - { name = "django-crontab" }, { name = "django-libsass" }, { name = "django-taggit" }, { name = "django-timezone-field", specifier = ">=6.1" }, @@ -675,15 +673,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/e3/4c5915a732d6ab54da8871400852b67529518eedfb6b78ecf10bbccfcabb/django_crispy_forms-2.6-py3-none-any.whl", hash = "sha256:8ee0ae28b6b0ac41ff48a65944480c049fe8d1b0047086874fd7efabf4ec1374", size = 31479, upload-time = "2026-03-01T09:03:36.048Z" }, ] -[[package]] -name = "django-crontab" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/37/bd/a122ba96167f5dfab70a58ca22fa046b7ef1ebad9ff026f7831bd6c2a49c/django-crontab-0.7.1.tar.gz", hash = "sha256:1201810a212460aaaa48eb6a766738740daf42c1a4f6aafecfb1525036929236", size = 7089, upload-time = "2016-03-07T19:35:54.714Z" } - [[package]] name = "django-libsass" version = "0.9" diff --git a/kubernetes/queue/celery-beat/deployment.yaml b/kubernetes/queue/celery-beat/deployment.yaml new file mode 100644 index 0000000..d7e4e6f --- /dev/null +++ b/kubernetes/queue/celery-beat/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: celery-beat + labels: + app: celery-beat +spec: + replicas: 1 + selector: + matchLabels: + app: celery-beat + template: + metadata: + name: celery-beat + labels: + app: celery-beat + spec: + initContainers: + - name: init-myservice + image: busybox + command: [ 'sh', '-c', 'env' ] + envFrom: + - secretRef: + name: web-secrets + containers: + - name: celery-beat + image: ahc_app-queue:latest + imagePullPolicy: Never + command: [ "celery", "-A", "AHC_app.celery_notifications.config:celery_obj", "beat", "-l", "info" ] + env: + - name: PYTHONUNBUFFERED + value: "1" + - name: PYTHONPATH + value: "/app" + envFrom: + - secretRef: + name: web-secrets + resources: + limits: + memory: "128Mi" + cpu: "250m" + restartPolicy: Always diff --git a/kubernetes/queue/celery-beat/kustomization.yaml b/kubernetes/queue/celery-beat/kustomization.yaml new file mode 100644 index 0000000..9519a26 --- /dev/null +++ b/kubernetes/queue/celery-beat/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - deployment.yaml diff --git a/kubernetes/queue/kustomization.yaml b/kubernetes/queue/kustomization.yaml index 1503853..d578b4a 100644 --- a/kubernetes/queue/kustomization.yaml +++ b/kubernetes/queue/kustomization.yaml @@ -1,5 +1,6 @@ resources: - secret.yaml - celery/ + - celery-beat/ - flower/ - redis/ From eddbbf89bdab97e277b789617380bd6a585dc157 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 22:58:53 +0200 Subject: [PATCH 11/31] feat(django): bump to 6.0.5 and register django.contrib.postgres for ArrayField --- AHC_app/AHC_app/settings.py | 1 + AHC_app/pyproject.toml | 2 +- AHC_app/uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 5b45f13..b03f41d 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -69,6 +69,7 @@ def _skip_external_services() -> bool: "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "django.contrib.postgres", "crispy_forms", "crispy_bootstrap4", "bootstrap_modal_forms", diff --git a/AHC_app/pyproject.toml b/AHC_app/pyproject.toml index 9eb9319..9fa84dd 100644 --- a/AHC_app/pyproject.toml +++ b/AHC_app/pyproject.toml @@ -5,7 +5,7 @@ description = "Animals Healthcare Application — pet health data management" requires-python = ">=3.14" dependencies = [ - "django>=5.2,<5.3", + "django>=6.0,<6.1", "djangorestframework>=3.14", "psycopg[binary]>=3.2", "celery>=5.4", diff --git a/AHC_app/uv.lock b/AHC_app/uv.lock index ccec51b..1a6dab0 100644 --- a/AHC_app/uv.lock +++ b/AHC_app/uv.lock @@ -143,7 +143,7 @@ requires-dist = [ { name = "cryptography", specifier = ">=43" }, { name = "defusedxml" }, { name = "discord" }, - { name = "django", specifier = ">=5.2,<5.3" }, + { name = "django", specifier = ">=6.0,<6.1" }, { name = "django-appconf" }, { name = "django-bootstrap-modal-forms" }, { name = "django-compressor" }, @@ -610,16 +610,16 @@ wheels = [ [[package]] name = "django" -version = "5.2.14" +version = "6.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, { name = "sqlparse" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f1/bf85f0d29ef76abf901f193fe8fef4769d3da7794197832bc30151c071d8/django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269", size = 10924131, upload-time = "2026-05-05T13:54:39.329Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" }, + { url = "https://files.pythonhosted.org/packages/94/5b/1328f8b84fce040c404f76822bf8c57d254e368e8cbd8bd67ec2b26d75f5/django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0", size = 8368680, upload-time = "2026-05-05T13:54:33.532Z" }, ] [[package]] From 3971293f77a1d4644ad19f4bb9e6f6a0ca23578e Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 23:04:11 +0200 Subject: [PATCH 12/31] feat(django6): add CSP middleware, Template Partials, and Background Tasks API --- AHC_app/AHC_app/celery_notifications/cron.py | 32 +++++++------------ AHC_app/AHC_app/settings.py | 21 ++++++++++++ .../templates/animals/all_animals_stable.html | 6 +--- .../homepage/templates/homepage/homepage.html | 12 ++----- AHC_app/templates/partials/animal_card.html | 7 ++++ 5 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 AHC_app/templates/partials/animal_card.html diff --git a/AHC_app/AHC_app/celery_notifications/cron.py b/AHC_app/AHC_app/celery_notifications/cron.py index 059fef4..12d4009 100644 --- a/AHC_app/AHC_app/celery_notifications/cron.py +++ b/AHC_app/AHC_app/celery_notifications/cron.py @@ -7,6 +7,7 @@ from functools import wraps from django.db.models import Q, QuerySet +from django.tasks import task from django.utils import timezone from AHC_app.celery_notifications.config import ( @@ -146,24 +147,13 @@ def send_discord_notes(): send_discord_notifications.apply_async(kwargs={"user_id": user_id, "user_message": user_message}, countdown=delay) -# class SynchNotificationsCron(CronJobBase): -# RUN_EVERY_MINS = 60 -# -# schedule = Schedule(run_every_mins=RUN_EVERY_MINS) -# code = "AHC_app.SynchNotificationsCronJob" -# -# # run_at_times = ["55"] -# -# @staticmethod -# def cron_send_emails(): -# from icecream import ic -# ic() -# -# send_emails() -# -# @staticmethod -# def cron_send_discord(): -# from icecream import ic -# ic() -# -# send_discord_notes() +@task +def log_notification_count() -> int: + """Django Background Tasks example. Use for simple in-process tasks. + + For distributed / retryable work, use Celery (@shared_task). + Enqueue with: log_notification_count.enqueue() + """ + count = EmailNotification.objects.filter(is_active=True).count() + logger.info("Active email notifications: %d", count) + return count diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index b03f41d..6bec7d9 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -83,6 +83,7 @@ def _skip_external_services() -> bool: MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", + "django.middleware.csp.ContentSecurityPolicyMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", @@ -247,3 +248,23 @@ def _skip_external_services() -> bool: DISCORD_TOKEN = config("DISCORD_TOKEN") TEST_RUNNER = "django.test.runner.DiscoverRunner" + +from django.utils.csp import CSP # noqa: E402 + +SECURE_CSP = { + "default-src": [CSP.SELF], + "script-src": [CSP.SELF], + "style-src": [CSP.SELF, CSP.UNSAFE_INLINE, "https://fonts.googleapis.com"], + "img-src": [CSP.SELF, "data:"], + "font-src": [CSP.SELF, "https://fonts.gstatic.com"], + "connect-src": [CSP.SELF], + "object-src": [CSP.NONE], + "base-uri": [CSP.SELF], +} +SECURE_CSP_REPORT_ONLY = SECURE_CSP + +TASKS = { + "default": { + "BACKEND": "django.tasks.backends.immediate.ImmediateBackend", + }, +} diff --git a/AHC_app/animals/templates/animals/all_animals_stable.html b/AHC_app/animals/templates/animals/all_animals_stable.html index 7221dd3..b246f58 100644 --- a/AHC_app/animals/templates/animals/all_animals_stable.html +++ b/AHC_app/animals/templates/animals/all_animals_stable.html @@ -15,11 +15,7 @@

Placeholder title

All pets:

{% for animal in animals %} - {{ animal.full_name }} + {% partial "partials/animal_card.html#animal_card" %} {% endfor %}
{% endif %} diff --git a/AHC_app/homepage/templates/homepage/homepage.html b/AHC_app/homepage/templates/homepage/homepage.html index 83df364..18db231 100644 --- a/AHC_app/homepage/templates/homepage/homepage.html +++ b/AHC_app/homepage/templates/homepage/homepage.html @@ -28,11 +28,7 @@

Pinned up:

{% for animal in recent_animals %} - {{ animal.full_name }} + {% partial "partials/animal_card.html#animal_card" %} {% endfor %}

@@ -47,11 +43,7 @@

Recent added:

{% for animal in recent_animals %} - {{ animal.full_name }} + {% partial "partials/animal_card.html#animal_card" %} {% endfor %}

diff --git a/AHC_app/templates/partials/animal_card.html b/AHC_app/templates/partials/animal_card.html new file mode 100644 index 0000000..38ac3b1 --- /dev/null +++ b/AHC_app/templates/partials/animal_card.html @@ -0,0 +1,7 @@ +{% partialdef animal_card %} +{{ animal.full_name }} +{% endpartialdef %} From a2d124ba512d94105f8d58a17d4a6787b46b9ef5 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 24 May 2026 23:11:08 +0200 Subject: [PATCH 13/31] test(homepage): add CSP header tests and fix static manifest in CI --- AHC_app/AHC_app/settings.py | 8 +++++++- AHC_app/homepage/tests/test_csp.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 AHC_app/homepage/tests/test_csp.py diff --git a/AHC_app/AHC_app/settings.py b/AHC_app/AHC_app/settings.py index 6bec7d9..7ea92f3 100644 --- a/AHC_app/AHC_app/settings.py +++ b/AHC_app/AHC_app/settings.py @@ -207,12 +207,18 @@ def _skip_external_services() -> bool: COMPRESS_PRECOMPILERS = (("text/x-scss", "django_libsass.SassCompiler"),) COMPRESS_OFFLINE = True LIBSASS_OUTPUT_STYLE = "compressed" + +_staticfiles_backend = ( + "django.contrib.staticfiles.storage.StaticFilesStorage" + if _is_test_run() + else "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" +) STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + "BACKEND": _staticfiles_backend, }, } diff --git a/AHC_app/homepage/tests/test_csp.py b/AHC_app/homepage/tests/test_csp.py new file mode 100644 index 0000000..c52486e --- /dev/null +++ b/AHC_app/homepage/tests/test_csp.py @@ -0,0 +1,21 @@ +import pytest +from django.test import Client, TestCase + + +@pytest.mark.integration +@pytest.mark.django_db +class TestCSPHeaders(TestCase): + def test_report_only_header_present(self): + response = Client().get("/") + self.assertIn("Content-Security-Policy-Report-Only", response.headers) + + def test_report_only_default_src_self(self): + response = Client().get("/") + header = response.headers.get("Content-Security-Policy-Report-Only", "") + self.assertIn("default-src 'self'", header) + + def test_report_only_no_unsafe_scripts(self): + response = Client().get("/") + header = response.headers.get("Content-Security-Policy-Report-Only", "") + self.assertIn("script-src 'self'", header) + self.assertNotIn("'unsafe-inline'", header.split("script-src")[1].split(";")[0]) From 37b4e9d601631958105dec8551b211644a19c47d Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Mon, 25 May 2026 00:34:46 +0200 Subject: [PATCH 14/31] refactor(layout): hoist tooling files from AHC_app/ to repo root --- AHC_app/.env.template => .env.template | 0 .github/workflows/django.yml | 7 ++-- .pre-commit-config.yaml | 6 ++-- AHC_app/.python-version => .python-version | 0 README.md | 4 +-- AHC_app/conftest.py => conftest.py | 0 AHC_app/justfile => justfile | 4 +-- AHC_app/manage.py => manage.py | 2 ++ AHC_app/pyproject.toml => pyproject.toml | 1 + .../CACHE/css/custom_pico.fc2f6099451a.css | 32 +++++++++++++++++++ AHC_app/uv.lock => uv.lock | 0 11 files changed, 44 insertions(+), 12 deletions(-) rename AHC_app/.env.template => .env.template (100%) rename AHC_app/.python-version => .python-version (100%) rename AHC_app/conftest.py => conftest.py (100%) rename AHC_app/justfile => justfile (89%) rename AHC_app/manage.py => manage.py (87%) rename AHC_app/pyproject.toml => pyproject.toml (98%) create mode 100644 static_collected/CACHE/css/custom_pico.fc2f6099451a.css rename AHC_app/uv.lock => uv.lock (100%) diff --git a/AHC_app/.env.template b/.env.template similarity index 100% rename from AHC_app/.env.template rename to .env.template diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 37672a8..0de0866 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -43,20 +43,17 @@ jobs: uses: astral-sh/setup-uv@v3 with: enable-cache: true - cache-dependency-glob: "AHC_app/uv.lock" + cache-dependency-glob: "uv.lock" python-version: "3.14" - name: Install dependencies run: uv sync - working-directory: ./AHC_app - name: Run tests (Django runner) - run: PYTHONPATH=$(pwd) uv run python manage.py test - working-directory: ./AHC_app + run: PYTHONPATH=AHC_app uv run python manage.py test - name: Run tests (pytest) run: uv run pytest -m "not slow" - working-directory: ./AHC_app build-and-push: name: Build and Push to ECR diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1a9f553..65414c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,10 +5,10 @@ repos: hooks: - id: uv-lock-check name: uv lock --check - entry: uv --directory AHC_app lock --check + entry: uv lock --check language: system pass_filenames: false - files: ^AHC_app/pyproject\.toml$ + files: ^pyproject\.toml$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -34,7 +34,7 @@ repos: hooks: - id: bandit name: bandit - entry: bandit -r AHC_app -c AHC_app/pyproject.toml + entry: bandit -r AHC_app -c pyproject.toml language: python additional_dependencies: ["bandit[toml]==1.9.4"] pass_filenames: false diff --git a/AHC_app/.python-version b/.python-version similarity index 100% rename from AHC_app/.python-version rename to .python-version diff --git a/README.md b/README.md index 4688021..68d1c1e 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ just docker-up docker image save -o ahc_app-web.tar ahc_app-web:latest docker image save -o ahc_app-queue.tar ahc_app-queue:latest docker image save -o ahc_app-couch_db.tar ahc_app-couch_db:latest - docker image save -o postgres.tar postgres:15-alpine + docker image save -o postgres.tar postgres:18-alpine ``` 5. Push Docker images to a registry: @@ -115,7 +115,7 @@ just docker-up minikube image load ahc_app-web.tar minikube image load ahc_app-queue.tar minikube image load ahc_app-couch_db.tar - minikube image load postgres.tar + minikube image load postgres.tar # postgres:18-alpine ``` 6. Deploy to Kubernetes using kustom files: diff --git a/AHC_app/conftest.py b/conftest.py similarity index 100% rename from AHC_app/conftest.py rename to conftest.py diff --git a/AHC_app/justfile b/justfile similarity index 89% rename from AHC_app/justfile rename to justfile index 87dccf8..5bb1bae 100644 --- a/AHC_app/justfile +++ b/justfile @@ -41,11 +41,11 @@ shell: # Start all Docker services docker-up: - docker-compose up -d --build + docker-compose -f AHC_app/docker-compose.yml up -d --build # Stop all Docker services docker-down: - docker-compose down + docker-compose -f AHC_app/docker-compose.yml down # Run pre-commit hooks on all files precommit: diff --git a/AHC_app/manage.py b/manage.py similarity index 87% rename from AHC_app/manage.py rename to manage.py index c262845..e51f677 100644 --- a/AHC_app/manage.py +++ b/manage.py @@ -3,10 +3,12 @@ import os import sys +from pathlib import Path def main(): """Run administrative tasks.""" + sys.path.insert(0, str(Path(__file__).resolve().parent / "AHC_app")) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "AHC_app.settings") try: from django.core.management import execute_from_command_line diff --git a/AHC_app/pyproject.toml b/pyproject.toml similarity index 98% rename from AHC_app/pyproject.toml rename to pyproject.toml index 9fa84dd..510afe5 100644 --- a/AHC_app/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,7 @@ indent-style = "space" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "AHC_app.settings" +pythonpath = ["AHC_app"] python_files = ["test_*.py", "tests.py"] addopts = "--strict-markers" markers = [ diff --git a/static_collected/CACHE/css/custom_pico.fc2f6099451a.css b/static_collected/CACHE/css/custom_pico.fc2f6099451a.css new file mode 100644 index 0000000..62dafb0 --- /dev/null +++ b/static_collected/CACHE/css/custom_pico.fc2f6099451a.css @@ -0,0 +1,32 @@ +/*! + * Pico CSS v1.5.9 (https://picocss.com) + * Copyright 2019-2023 - Licensed under MIT + */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", + "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px;--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--grid-spacing-vertical: 0;--grid-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media (min-width: 576px){:root{--font-size: 17px}}@media (min-width: 768px){:root{--font-size: 18px}}@media (min-width: 992px){:root{--font-size: 19px}}@media (min-width: 1200px){:root{--font-size: 20px}}@media (min-width: 576px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 2.5)}}@media (min-width: 768px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3)}}@media (min-width: 992px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3.5)}}@media (min-width: 1200px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 4)}}@media (min-width: 576px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}@media (min-width: 992px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.75)}}@media (min-width: 1200px){article{--block-spacing-horizontal: calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing)}@media (min-width: 576px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2.5);--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 3);--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}a{--text-decoration: none}a.secondary,a.contrast{--text-decoration: underline}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type="checkbox"],[type="radio"]{--border-width: 2px}[type="checkbox"][role="switch"]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", + "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme="light"],:root:not([data-theme="dark"]){--background-color: #fff;--color: #415462;--h1-color: #1b2832;--h2-color: #23333e;--h3-color: #2c3d49;--h4-color: #374956;--h5-color: #415462;--h6-color: #998133;--muted-color: #73828c;--muted-border-color: #edf0f3;--primary: #fdd835;--primary-hover: #fbc02d;--primary-focus: rgba(253,216,53,0.125);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: #415462;--secondary-focus: rgba(240,173,5,0.125);--secondary-inverse: #fff;--contrast: #1b2832;--contrast-hover: #000;--contrast-focus: rgba(240,173,5,0.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: #543a25;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: #a2afb9;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #fbc02d;--form-element-disabled-border-color: #a2afb9;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211,47,47,0.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67,160,71,0.125);--switch-background-color: #bbc6ce;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #fbc02d;--range-active-border-color: #bbc6ce;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: #f6f8f9;--code-background-color: #edf0f3;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #b34d80;--code-property-color: #3d888f;--code-value-color: #986;--code-comment-color: #a2afb9;--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: + .0145rem .029rem .174rem rgba(27,40,50,0.01698), + .0335rem .067rem .402rem rgba(27,40,50,0.024), + .0625rem .125rem .75rem rgba(27,40,50,0.03), + .1125rem .225rem 1.35rem rgba(27,40,50,0.036), + .2085rem .417rem 2.502rem rgba(27,40,50,0.04302), + .5rem 1rem 6rem rgba(27,40,50,0.06), + 0 0 0 0.0625rem rgba(27,40,50,0.015);--card-sectionning-background-color: #fafbfc;--dropdown-background-color: #fafbfc;--dropdown-border-color: #f4d890;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: #edf0f3;--modal-overlay-background-color: rgba(251,192,45,0.7);--progress-background-color: #fbc02d;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65.28, 84.32, 97.92)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(114.75, 129.625, 140.25)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65.28, 84.32, 97.92)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65.28, 84.32, 97.92)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65.28, 84.32, 97.92)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #f4d890;--h3-color: #fbc02d;--h4-color: #dbc37d;--h5-color: #bbc6ce;--h6-color: #aebbc3;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253,216,53,0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,0.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,0.25);--contrast-inverse: #000;--mark-background-color: #d0c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,0.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #23333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,0.05);--code-background-color: #17232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #998133;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e25;--card-border-color: var(--card-background-color);--card-box-shadow: + .0145rem .029rem .174rem rgba(0,0,0,0.01698), + .0335rem .067rem .402rem rgba(0,0,0,0.024), + .0625rem .125rem .75rem rgba(0,0,0,0.03), + .1125rem .225rem 1.35rem rgba(0,0,0,0.036), + .2085rem .417rem 2.502rem rgba(0,0,0,0.04302), + .5rem 1rem 6rem rgba(0,0,0,0.06), + 0 0 0 0.0625rem rgba(0,0,0,0.015);--card-sectionning-background-color: #17232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #23333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35,51,62,0.75);--modal-overlay-background-color: rgba(35,51,62,0.8);--progress-background-color: #23333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(114.75, 129.625, 140.25)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme="dark"]{--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #f4d890;--h3-color: #fbc02d;--h4-color: #dbc37d;--h5-color: #bbc6ce;--h6-color: #aebbc3;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253,216,53,0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,0.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,0.25);--contrast-inverse: #000;--mark-background-color: #d0c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,0.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #23333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,0.05);--code-background-color: #17232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #998133;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e25;--card-border-color: var(--card-background-color);--card-box-shadow: + .0145rem .029rem .174rem rgba(0,0,0,0.01698), + .0335rem .067rem .402rem rgba(0,0,0,0.024), + .0625rem .125rem .75rem rgba(0,0,0,0.03), + .1125rem .225rem 1.35rem rgba(0,0,0,0.036), + .2085rem .417rem 2.502rem rgba(0,0,0,0.04302), + .5rem 1rem 6rem rgba(0,0,0,0.06), + 0 0 0 0.0625rem rgba(0,0,0,0.015);--card-sectionning-background-color: #17232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #23333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35,51,62,0.75);--modal-overlay-background-color: rgba(35,51,62,0.8);--progress-background-color: #23333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(114.75, 129.625, 140.25)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(161.976, 175.304, 184.824)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type="checkbox"],[type="radio"],[type="range"]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media (min-width: 576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width: 768px){.container{max-width:700px}}@media (min-width: 992px){.container{max-width:920px}}@media (min-width: 1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media (min-width: 992px){.grid{grid-template-columns:repeat(auto-fit, minmax(0%, 1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing) * 0.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:0.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role="link"]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition)}a:is([aria-current], :hover, :active, :focus),[role="link"]:is([aria-current], :hover, :active, :focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role="link"]:focus{--background-color: var(--primary-focus)}a.secondary,[role="link"].secondary{--color: var(--secondary)}a.secondary:is([aria-current], :hover, :active, :focus),[role="link"].secondary:is([aria-current], :hover, :active, :focus){--color: var(--secondary-hover)}a.secondary:focus,[role="link"].secondary:focus{--background-color: var(--secondary-focus)}a.contrast,[role="link"].contrast{--color: var(--contrast)}a.contrast:is([aria-current], :hover, :active, :focus),[role="link"].contrast:is([aria-current], :hover, :active, :focus){--color: var(--contrast-hover)}a.contrast:focus,[role="link"].contrast:focus{--background-color: var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address, blockquote, dl, figure, form, ol, p, pre, table, ul)~:is(h1, h2, h3, h4, h5, h6){margin-top:var(--typography-spacing-vertical)}hgroup,.headings{margin-bottom:var(--typography-spacing-vertical)}hgroup>*,.headings>*{margin-bottom:0}hgroup>*:last-child,.headings>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl, ol, ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl, ol, ul) li{margin-bottom:calc(var(--typography-spacing-vertical) * 0.25)}:where(dl, ol, ul) :is(dl, ol, ul){margin:0;margin-top:calc(var(--typography-spacing-vertical) * 0.25)}ul li{list-style:square}mark{padding:0.125rem 0.25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:0.25rem solid var(--blockquote-border-color);border-inline-start:0.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical) * 0.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio, canvas, iframe, img, svg, video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role="button"]{display:inline-block;text-decoration:none}button,input[type="submit"],input[type="button"],input[type="reset"],[role="button"]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}button:is([aria-current], :hover, :active, :focus),input[type="submit"]:is([aria-current], :hover, :active, :focus),input[type="button"]:is([aria-current], :hover, :active, :focus),input[type="reset"]:is([aria-current], :hover, :active, :focus),[role="button"]:is([aria-current], :hover, :active, :focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type="submit"]:focus,input[type="button"]:focus,input[type="reset"]:focus,[role="button"]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--outline-width) var(--primary-focus)}:is(button, input[type="submit"], input[type="button"], [role="button"]).secondary,input[type="reset"]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}:is(button, input[type="submit"], input[type="button"], [role="button"]).secondary:is([aria-current], :hover, :active, :focus),input[type="reset"]:is([aria-current], :hover, :active, :focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover);--color: var(--secondary-inverse)}:is(button, input[type="submit"], input[type="button"], [role="button"]).secondary:focus,input[type="reset"]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--outline-width) var(--secondary-focus)}:is(button, input[type="submit"], input[type="button"], [role="button"]).contrast{--background-color: var(--contrast);--border-color: var(--contrast);--color: var(--contrast-inverse)}:is(button, input[type="submit"], input[type="button"], [role="button"]).contrast:is([aria-current], :hover, :active, :focus){--background-color: var(--contrast-hover);--border-color: var(--contrast-hover);--color: var(--contrast-inverse)}:is(button, input[type="submit"], input[type="button"], [role="button"]).contrast:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), + 0 0 0 var(--outline-width) var(--contrast-focus)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline,input[type="reset"].outline{--background-color: transparent;--color: var(--primary)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline:is([aria-current], :hover, :active, :focus),input[type="reset"].outline:is([aria-current], :hover, :active, :focus){--background-color: transparent;--color: var(--primary-hover)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline.secondary,input[type="reset"].outline{--color: var(--secondary)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline.secondary:is([aria-current], :hover, :active, :focus),input[type="reset"].outline:is([aria-current], :hover, :active, :focus){--color: var(--secondary-hover)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline.contrast{--color: var(--contrast)}:is(button, input[type="submit"], input[type="button"], [role="button"]).outline.contrast:is([aria-current], :hover, :active, :focus){--color: var(--contrast-hover)}:where(button, [type="submit"], [type="button"], [type="reset"], [role="button"])[disabled],:where(fieldset[disabled]) :is(button, [type="submit"], [type="button"], [type="reset"], [role="button"]),a[role="button"]:not([href]){opacity:0.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type="file"],[type="range"]{padding:0;border-width:0}input:not([type="checkbox"],[type="radio"],[type="range"]){height:calc( (1rem * var(--line-height)) + (var(--form-element-spacing-vertical) * 2) + (var(--border-width) * 2))}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing) * 0.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type="checkbox"],[type="radio"]),select,textarea{width:100%}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}input:not([type="submit"],[type="button"],[type="reset"],[type="checkbox"],[type="radio"],[readonly]):is(:active, :focus),:where(select, textarea):is(:active, :focus){--background-color: var(--form-element-active-background-color)}input:not([type="submit"],[type="button"],[type="reset"],[role="switch"],[readonly]):is(:active, :focus),:where(select, textarea):is(:active, :focus){--border-color: var(--form-element-active-border-color)}input:not([type="submit"],[type="button"],[type="reset"],[type="range"],[type="file"],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type="submit"],[type="button"],[type="reset"])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type="submit"], [type="button"], [type="reset"]), select, textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input, select, textarea):not([type="checkbox"],[type="radio"],[type="date"],[type="datetime-local"],[type="month"],[type="time"],[type="week"])[aria-invalid]{padding-right:calc( var(--form-element-spacing-horizontal) + 1.5rem) !important;padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal) !important;padding-inline-end:calc( var(--form-element-spacing-horizontal) + 1.5rem) !important;background-position:center right 0.75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input, select, textarea):not([type="checkbox"],[type="radio"],[type="date"],[type="datetime-local"],[type="month"],[type="time"],[type="week"])[aria-invalid="false"]{background-image:var(--icon-valid)}:where(input, select, textarea):not([type="checkbox"],[type="radio"],[type="date"],[type="datetime-local"],[type="month"],[type="time"],[type="week"])[aria-invalid="true"]{background-image:var(--icon-invalid)}:where(input, select, textarea)[aria-invalid="false"]{--border-color: var(--form-element-valid-border-color)}:where(input, select, textarea)[aria-invalid="false"]:is(:active, :focus){--border-color: var(--form-element-valid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important}:where(input, select, textarea)[aria-invalid="true"]{--border-color: var(--form-element-invalid-border-color)}:where(input, select, textarea)[aria-invalid="true"]:is(:active, :focus){--border-color: var(--form-element-invalid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important}[dir="rtl"] :where(input, select, textarea):not([type="checkbox"],[type="radio"]):is([aria-invalid], [aria-invalid="true"], [aria-invalid="false"] ){background-position:center left 0.75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type="checkbox"],[type="radio"]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:transparent}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right 0.75rem;background-size:1rem auto;background-repeat:no-repeat}[dir="rtl"] select:not([multiple],[size]){background-position:center left 0.75rem}:where(input, select, textarea, .grid)+small{display:block;width:100%;margin-top:calc(var(--spacing) * -0.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input, select, textarea){margin-top:calc(var(--spacing) * 0.25)}[type="checkbox"],[type="radio"]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-0.125em;margin-right:0.375em;margin-left:0;margin-inline-start:0;margin-inline-end:0.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type="checkbox"]::-ms-check,[type="radio"]::-ms-check{display:none}[type="checkbox"]:checked,[type="checkbox"]:checked:active,[type="checkbox"]:checked:focus,[type="radio"]:checked,[type="radio"]:checked:active,[type="radio"]:checked:focus{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:0.75em auto;background-repeat:no-repeat}[type="checkbox"]~label,[type="radio"]~label{display:inline-block;margin-right:0.375em;margin-bottom:0;cursor:pointer}[type="checkbox"]:indeterminate{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-minus);background-position:center;background-size:0.75em auto;background-repeat:no-repeat}[type="radio"]{border-radius:50%}[type="radio"]:checked,[type="radio"]:checked:active,[type="radio"]:checked:focus{--background-color: var(--primary-inverse);border-width:0.35em;background-image:none}[type="checkbox"][role="switch"]{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color);--color: var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type="checkbox"][role="switch"]:focus{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color)}[type="checkbox"][role="switch"]:checked{--background-color: var(--switch-checked-background-color);--border-color: var(--switch-checked-background-color)}[type="checkbox"][role="switch"]:before{display:block;width:calc(1.25em - (var(--border-width) * 2));height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin 0.1s ease-in-out}[type="checkbox"][role="switch"]:checked{background-image:none}[type="checkbox"][role="switch"]:checked::before{margin-left:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type="checkbox"][aria-invalid="false"],[type="checkbox"]:checked[aria-invalid="false"],[type="radio"][aria-invalid="false"],[type="radio"]:checked[aria-invalid="false"],[type="checkbox"][role="switch"][aria-invalid="false"],[type="checkbox"][role="switch"]:checked[aria-invalid="false"]{--border-color: var(--form-element-valid-border-color)}[type="checkbox"][aria-invalid="true"],[type="checkbox"]:checked[aria-invalid="true"],[type="radio"][aria-invalid="true"],[type="radio"]:checked[aria-invalid="true"],[type="checkbox"][role="switch"][aria-invalid="true"],[type="checkbox"][role="switch"]:checked[aria-invalid="true"]{--border-color: var(--form-element-invalid-border-color)}[type="color"]::-webkit-color-swatch-wrapper{padding:0}[type="color"]::-moz-focus-inner{padding:0}[type="color"]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius) * 0.5)}[type="color"]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius) * 0.5)}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"]):is([type="date"], [type="datetime-local"], [type="month"], [type="time"], [type="week"]){--icon-position: 0.75rem;--icon-width: 1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="time"]{background-image:var(--icon-time)}[type="date"]::-webkit-calendar-picker-indicator,[type="datetime-local"]::-webkit-calendar-picker-indicator,[type="month"]::-webkit-calendar-picker-indicator,[type="time"]::-webkit-calendar-picker-indicator,[type="week"]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width) * -1);margin-left:var(--icon-position);opacity:0}[dir="rtl"] :is([type="date"], [type="datetime-local"], [type="month"], [type="time"], [type="week"]){text-align:right}[type="file"]{--color: var(--muted-color);padding:calc(var(--form-element-spacing-vertical) * 0.5) 0;border:0;border-radius:0;background:none}[type="file"]::file-selector-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing) / 2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing) / 2);padding:calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type="file"]::file-selector-button:is(:hover, :active, :focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type="file"]::-webkit-file-upload-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing) / 2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing) / 2);padding:calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type="file"]::-webkit-file-upload-button:is(:hover, :active, :focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type="file"]::-ms-browse{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing) / 2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing) / 2);padding:calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type="file"]::-ms-browse:is(:hover, :active, :focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type="range"]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}[type="range"]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type="range"]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type="range"]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type="range"]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type="range"]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type="range"]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type="range"]:hover,[type="range"]:focus{--range-border-color: var(--range-active-border-color);--range-thumb-color: var(--range-thumb-hover-color)}[type="range"]:active{--range-thumb-color: var(--range-thumb-active-color)}[type="range"]:active::-webkit-slider-thumb{transform:scale(1.25)}[type="range"]:active::-moz-range-thumb{transform:scale(1.25)}[type="range"]:active::-ms-thumb{transform:scale(1.25)}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"][aria-invalid]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;background-position:center left 1.125rem, center right 0.75rem}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"][aria-invalid="false"]{background-image:var(--icon-search),var(--icon-valid)}input:not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"][aria-invalid="true"]{background-image:var(--icon-search),var(--icon-invalid)}[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir="rtl"] :where(input):not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"]{background-position:center right 1.125rem}[dir="rtl"] :where(input):not([type="checkbox"],[type="radio"],[type="range"],[type="file"])[type="search"][aria-invalid]{background-position:center right 1.125rem, center left 0.75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing) / 2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role="grid"] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}pre,code,kbd,samp{font-size:0.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}pre,code,kbd{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:0.375rem 0.5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:none;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none !important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--transition)}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--spacing, 1rem) * 0.5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:none}details summary:focus:not([role="button"]){color:var(--accordion-active-summary-color)}details summary[role="button"]{width:100%;text-align:left}details summary[role="button"]::after{height:calc(1rem * var(--line-height, 1.5));background-image:var(--icon-chevron-button)}details summary[role="button"]:not(.outline).contrast::after{background-image:var(--icon-chevron-button-inverse)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir="rtl"] details summary{text-align:right}[dir="rtl"] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>header,article>footer{margin-right:calc(var(--block-spacing-horizontal) * -1);margin-left:calc(var(--block-spacing-horizontal) * -1);padding:calc(var(--block-spacing-vertical) * 0.66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical) * -1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical) * -1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width: 0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;backdrop-filter:var(--modal-overlay-backdrop-filter);background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing) * 2);overflow:auto}@media (min-width: 576px){dialog article{max-width:510px}}@media (min-width: 768px){dialog article{max-width:700px}}dialog article>header,dialog article>footer{padding:calc(var(--block-spacing-vertical) * 0.5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role="button"]{margin-bottom:0}dialog article>footer [role="button"]:not(:first-of-type){margin-left:calc(var(--spacing) * 0.5)}dialog article p:last-of-type{margin:0}dialog article .close{display:block;width:1rem;height:1rem;margin-top:calc(var(--block-spacing-vertical) * -0.5);margin-bottom:var(--typography-spacing-vertical);margin-left:auto;background-image:var(--icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;opacity:0.5;transition:opacity var(--transition)}dialog article .close:is([aria-current], :hover, :active, :focus){opacity:1}dialog:not([open]),dialog[open="false"]{display:none}.modal-is-open{padding-right:var(--scrollbar-width, 0px);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto}:where(.modal-is-opening, .modal-is-closing) dialog,:where(.modal-is-opening, .modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening, .modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening, .modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{backdrop-filter:none;background-color:transparent}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:"\200B"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal) * -1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal) * -1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a, [role="link"]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical) * -1) calc(var(--nav-link-spacing-horizontal) * -1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a, [role="link"]):is([aria-current], :hover, :active, :focus){text-decoration:none}nav[aria-label="breadcrumb"]{align-items:center;justify-content:start}nav[aria-label="breadcrumb"] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label="breadcrumb"] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizontal) * 2);margin-inline-start:calc(var(--nav-link-spacing-horizontal) / 2);content:"/";color:var(--muted-color);text-align:center}nav[aria-label="breadcrumb"] a[aria-current]{background-color:transparent;color:inherit;text-decoration:none;pointer-events:none}nav [role="button"]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside nav,aside ol,aside ul,aside li{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical) * 0.5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role="button"]{margin:inherit}[dir="rtl"] nav[aria-label="breadcrumb"] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:0.5rem;margin-bottom:calc(var(--spacing) * 0.5);overflow:hidden;border:0;border-radius:var(--border-radius);background-color:var(--progress-background-color);color:var(--progress-color)}progress::-webkit-progress-bar{border-radius:var(--border-radius);background:none}progress[value]::-webkit-progress-value{background-color:var(--progress-color)}progress::-moz-progress-bar{background-color:var(--progress-color)}@media (prefers-reduced-motion: no-preference){progress:indeterminate{background:var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:transparent}progress:indeterminate::-moz-progress-bar{background-color:transparent}}@media (prefers-reduced-motion: no-preference){[dir="rtl"] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}details[role="list"],li[role="list"]{position:relative}details[role="list"] summary+ul,li[role="list"]>ul{display:flex;z-index:99;position:absolute;top:auto;right:0;left:0;flex-direction:column;margin:0;padding:0;border:var(--border-width) solid var(--dropdown-border-color);border-radius:var(--border-radius);border-top-right-radius:0;border-top-left-radius:0;background-color:var(--dropdown-background-color);box-shadow:var(--card-box-shadow);color:var(--dropdown-color);white-space:nowrap}details[role="list"] summary+ul li,li[role="list"]>ul li{width:100%;margin-bottom:0;padding:calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal);list-style:none}details[role="list"] summary+ul li:first-of-type,li[role="list"]>ul li:first-of-type{margin-top:calc(var(--form-element-spacing-vertical) * 0.5)}details[role="list"] summary+ul li:last-of-type,li[role="list"]>ul li:last-of-type{margin-bottom:calc(var(--form-element-spacing-vertical) * 0.5)}details[role="list"] summary+ul li a,li[role="list"]>ul li a{display:block;margin:calc(var(--form-element-spacing-vertical) * -0.5) calc(var(--form-element-spacing-horizontal) * -1);padding:calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal);overflow:hidden;color:var(--dropdown-color);text-decoration:none;text-overflow:ellipsis}details[role="list"] summary+ul li a:hover,li[role="list"]>ul li a:hover{background-color:var(--dropdown-hover-background-color)}details[role="list"] summary::after,li[role="list"]>a::after{display:block;width:1rem;height:calc(1rem * var(--line-height, 1.5));margin-inline-start:0.5rem;float:right;transform:rotate(0deg);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}details[role="list"]{padding:0;border-bottom:none}details[role="list"] summary{margin-bottom:0}details[role="list"] summary:not([role]){height:calc( 1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2);padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--form-element-border-color);border-radius:var(--border-radius);background-color:var(--form-element-background-color);color:var(--form-element-placeholder-color);line-height:inherit;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}details[role="list"] summary:not([role]):active,details[role="list"] summary:not([role]):focus{border-color:var(--form-element-active-border-color);background-color:var(--form-element-active-background-color)}details[role="list"] summary:not([role]):focus{box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}details[role="list"][open] summary{border-bottom-right-radius:0;border-bottom-left-radius:0}details[role="list"][open] summary::before{display:block;z-index:1;position:fixed;top:0;right:0;bottom:0;left:0;background:none;content:"";cursor:default}nav details[role="list"] summary,nav li[role="list"] a{display:flex;direction:ltr}nav details[role="list"] summary+ul,nav li[role="list"]>ul{min-width:fit-content;border-radius:var(--border-radius)}nav details[role="list"] summary+ul li a,nav li[role="list"]>ul li a{border-radius:0}nav details[role="list"] summary,nav details[role="list"] summary:not([role]){height:auto;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}nav details[role="list"][open] summary{border-radius:var(--border-radius)}nav details[role="list"] summary+ul{margin-top:var(--outline-width);margin-inline-start:0}nav details[role="list"] summary[role="link"]{margin-bottom:calc(var(--nav-link-spacing-vertical) * -1);line-height:var(--line-height)}nav details[role="list"] summary[role="link"]+ul{margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-link-spacing-horizontal) * -1)}li[role="list"]:hover>ul,li[role="list"] a:active~ul,li[role="list"] a:focus~ul{display:flex}li[role="list"]>ul{display:none;margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc( var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal))}li[role="list"]>a::after{background-image:var(--icon-chevron)}label>details[role="list"]{margin-top:calc(var(--spacing) * .25);margin-bottom:var(--spacing)}[aria-busy="true"]{cursor:progress}[aria-busy="true"]:not(input,select,textarea)::before{display:inline-block;width:1em;height:1em;border:0.1875em solid currentColor;border-radius:1em;border-right-color:transparent;content:"";vertical-align:text-bottom;vertical-align:-.125em;animation:spinner 0.75s linear infinite;opacity:var(--loading-spinner-opacity)}[aria-busy="true"]:not(input,select,textarea):not(:empty)::before{margin-right:calc(var(--spacing) * 0.5);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing) * 0.5)}[aria-busy="true"]:not(input,select,textarea):empty{text-align:center}button[aria-busy="true"],input[type="submit"][aria-busy="true"],input[type="button"][aria-busy="true"],input[type="reset"][aria-busy="true"],a[aria-busy="true"]{pointer-events:none}@keyframes spinner{to{transform:rotate(360deg)}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip][data-placement="top"]::before,[data-tooltip][data-placement="top"]::after,[data-tooltip]::before,[data-tooltip]::after{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%, -0.25rem);border-radius:var(--border-radius);background:var(--tooltip-background-color);content:attr(data-tooltip);color:var(--tooltip-color);font-style:normal;font-weight:var(--font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip][data-placement="top"]::after,[data-tooltip]::after{padding:0;transform:translate(-50%, 0rem);border-top:.3rem solid;border-right:.3rem solid transparent;border-left:.3rem solid transparent;border-radius:0;background-color:transparent;content:"";color:var(--tooltip-background-color)}[data-tooltip][data-placement="bottom"]::before,[data-tooltip][data-placement="bottom"]::after{top:100%;bottom:auto;transform:translate(-50%, 0.25rem)}[data-tooltip][data-placement="bottom"]:after{transform:translate(-50%, -0.3rem);border:.3rem solid transparent;border-bottom:.3rem solid}[data-tooltip][data-placement="left"]::before,[data-tooltip][data-placement="left"]::after{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-0.25rem, -50%)}[data-tooltip][data-placement="left"]:after{transform:translate(0.3rem, -50%);border:.3rem solid transparent;border-left:.3rem solid}[data-tooltip][data-placement="right"]::before,[data-tooltip][data-placement="right"]::after{top:50%;right:auto;bottom:auto;left:100%;transform:translate(0.25rem, -50%)}[data-tooltip][data-placement="right"]:after{transform:translate(-0.3rem, -50%);border:.3rem solid transparent;border-right:.3rem solid}[data-tooltip]:focus::before,[data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{opacity:1}@media (hover: hover) and (pointer: fine){[data-tooltip][data-placement="bottom"]:focus::before,[data-tooltip][data-placement="bottom"]:focus::after,[data-tooltip][data-placement="bottom"]:hover [data-tooltip]:focus::before,[data-tooltip][data-placement="bottom"]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-top}[data-tooltip][data-placement="bottom"]:focus::after,[data-tooltip][data-placement="bottom"]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::after{animation-name:tooltip-caret-slide-top}[data-tooltip][data-placement="bottom"]:focus::before,[data-tooltip][data-placement="bottom"]:focus::after,[data-tooltip][data-placement="bottom"]:hover::before,[data-tooltip][data-placement="bottom"]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-bottom}[data-tooltip][data-placement="bottom"]:focus::after,[data-tooltip][data-placement="bottom"]:hover::after{animation-name:tooltip-caret-slide-bottom}[data-tooltip][data-placement="left"]:focus::before,[data-tooltip][data-placement="left"]:focus::after,[data-tooltip][data-placement="left"]:hover::before,[data-tooltip][data-placement="left"]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-left}[data-tooltip][data-placement="left"]:focus::after,[data-tooltip][data-placement="left"]:hover::after{animation-name:tooltip-caret-slide-left}[data-tooltip][data-placement="right"]:focus::before,[data-tooltip][data-placement="right"]:focus::after,[data-tooltip][data-placement="right"]:hover::before,[data-tooltip][data-placement="right"]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-right}[data-tooltip][data-placement="right"]:focus::after,[data-tooltip][data-placement="right"]:hover::after{animation-name:tooltip-caret-slide-right}}@keyframes tooltip-slide-top{from{transform:translate(-50%, 0.75rem);opacity:0}to{transform:translate(-50%, -0.25rem);opacity:1}}@keyframes tooltip-caret-slide-top{from{opacity:0}50%{transform:translate(-50%, -0.25rem);opacity:0}to{transform:translate(-50%, 0rem);opacity:1}}@keyframes tooltip-slide-bottom{from{transform:translate(-50%, -0.75rem);opacity:0}to{transform:translate(-50%, 0.25rem);opacity:1}}@keyframes tooltip-caret-slide-bottom{from{opacity:0}50%{transform:translate(-50%, -0.5rem);opacity:0}to{transform:translate(-50%, -0.3rem);opacity:1}}@keyframes tooltip-slide-left{from{transform:translate(0.75rem, -50%);opacity:0}to{transform:translate(-0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-left{from{opacity:0}50%{transform:translate(0.05rem, -50%);opacity:0}to{transform:translate(0.3rem, -50%);opacity:1}}@keyframes tooltip-slide-right{from{transform:translate(-0.75rem, -50%);opacity:0}to{transform:translate(0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-right{from{opacity:0}50%{transform:translate(-0.05rem, -50%);opacity:0}to{transform:translate(-0.3rem, -50%);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled="true"],[disabled]{cursor:not-allowed}[aria-hidden="false"][hidden]{display:initial}[aria-hidden="false"][hidden]:not(:focus){clip:rect(0, 0, 0, 0);position:absolute}a,area,button,input,label,select,summary,textarea,[tabindex]{-ms-touch-action:manipulation}[dir="rtl"]{direction:rtl}@media (prefers-reduced-motion: reduce){*:not([aria-busy="true"]),:not([aria-busy="true"])::before,:not([aria-busy="true"])::after{background-attachment:initial !important;animation-duration:1ms !important;animation-delay:-1ms !important;animation-iteration-count:1 !important;scroll-behavior:auto !important;transition-delay:0s !important;transition-duration:0s !important}} diff --git a/AHC_app/uv.lock b/uv.lock similarity index 100% rename from AHC_app/uv.lock rename to uv.lock From 68be5d7a22e594ba52df622963b55f4aa4a1af7a Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Mon, 25 May 2026 00:38:12 +0200 Subject: [PATCH 15/31] refactor(docker): move Docker files to docker/ and update build contexts --- {AHC_app => docker}/.dockerignore | 0 {AHC_app => docker}/Dockerfile-couchdb | 2 +- {AHC_app => docker}/Dockerfile-queue | 0 {AHC_app => docker}/Dockerfile-web | 0 {AHC_app => docker}/docker-compose.yml | 26 +++++++++++++------------- {AHC_app => docker}/setup_couchdb.sh | 0 justfile | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) rename {AHC_app => docker}/.dockerignore (100%) rename {AHC_app => docker}/Dockerfile-couchdb (76%) rename {AHC_app => docker}/Dockerfile-queue (100%) rename {AHC_app => docker}/Dockerfile-web (100%) rename {AHC_app => docker}/docker-compose.yml (88%) rename {AHC_app => docker}/setup_couchdb.sh (100%) diff --git a/AHC_app/.dockerignore b/docker/.dockerignore similarity index 100% rename from AHC_app/.dockerignore rename to docker/.dockerignore diff --git a/AHC_app/Dockerfile-couchdb b/docker/Dockerfile-couchdb similarity index 76% rename from AHC_app/Dockerfile-couchdb rename to docker/Dockerfile-couchdb index 1e71bdb..bf03851 100644 --- a/AHC_app/Dockerfile-couchdb +++ b/docker/Dockerfile-couchdb @@ -5,6 +5,6 @@ ARG COUCHDB_PASSWORD ARG COUCHDB_PORT EXPOSE ${COUCHDB_PORT} -COPY setup_couchdb.sh setup_couchdb.sh +COPY docker/setup_couchdb.sh setup_couchdb.sh RUN chmod +x setup_couchdb.sh RUN sh setup_couchdb.sh diff --git a/AHC_app/Dockerfile-queue b/docker/Dockerfile-queue similarity index 100% rename from AHC_app/Dockerfile-queue rename to docker/Dockerfile-queue diff --git a/AHC_app/Dockerfile-web b/docker/Dockerfile-web similarity index 100% rename from AHC_app/Dockerfile-web rename to docker/Dockerfile-web diff --git a/AHC_app/docker-compose.yml b/docker/docker-compose.yml similarity index 88% rename from AHC_app/docker-compose.yml rename to docker/docker-compose.yml index d59a072..8c6b900 100644 --- a/AHC_app/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,13 +2,13 @@ version: "3.9" services: web: build: - context: . - dockerfile: Dockerfile-web + context: .. + dockerfile: docker/Dockerfile-web image: ahc_app-web:latest ports: - "8000:8000" volumes: - - .:/app + - ..:/app depends_on: postgres_db: condition: service_healthy @@ -18,7 +18,7 @@ services: condition: service_healthy environment: - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app + - PYTHONPATH=/app/AHC_app entrypoint: ["/bin/bash", "-c"] command: - | @@ -47,8 +47,8 @@ services: couch_db: build: - context: . - dockerfile: Dockerfile-couchdb + context: .. + dockerfile: docker/Dockerfile-couchdb args: COUCHDB_USER: ${COUCHDB_USER} COUCHDB_PASSWORD: ${COUCHDB_PASSWORD} @@ -70,8 +70,8 @@ services: queue: build: - context: . - dockerfile: Dockerfile-queue + context: .. + dockerfile: docker/Dockerfile-queue command: celery -A AHC_app.celery_notifications.config:celery_obj worker -l info depends_on: redis: @@ -85,12 +85,12 @@ services: environment: - DJANGO_SETTINGS_MODULE=AHC_app.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app + - PYTHONPATH=/app/AHC_app celery_beat: build: - context: . - dockerfile: Dockerfile-queue + context: .. + dockerfile: docker/Dockerfile-queue command: celery -A AHC_app.celery_notifications.config:celery_obj beat -l info depends_on: redis: @@ -100,9 +100,9 @@ services: environment: - DJANGO_SETTINGS_MODULE=AHC_app.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app + - PYTHONPATH=/app/AHC_app env_file: - - .env + - ../.env redis: image: redis:7-alpine diff --git a/AHC_app/setup_couchdb.sh b/docker/setup_couchdb.sh similarity index 100% rename from AHC_app/setup_couchdb.sh rename to docker/setup_couchdb.sh diff --git a/justfile b/justfile index 5bb1bae..242ee8f 100644 --- a/justfile +++ b/justfile @@ -41,11 +41,11 @@ shell: # Start all Docker services docker-up: - docker-compose -f AHC_app/docker-compose.yml up -d --build + docker-compose -f docker/docker-compose.yml up -d --build # Stop all Docker services docker-down: - docker-compose -f AHC_app/docker-compose.yml down + docker-compose -f docker/docker-compose.yml down # Run pre-commit hooks on all files precommit: From a5dbae27cfbea19dc50821f270129ac58c2880e3 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Mon, 25 May 2026 01:11:54 +0200 Subject: [PATCH 16/31] refactor(layout): move Django project package to src/ahc/ --- .github/workflows/django.yml | 2 +- docker/docker-compose.yml | 4 ++-- kubernetes/backend/web/secret.yaml.template | 2 +- kubernetes/queue/celery/configmap.yaml | 2 +- kubernetes/queue/celery/secret.yaml.template | 2 +- manage.py | 5 +++-- pyproject.toml | 4 ++-- {AHC_app/AHC_app => src}/__init__.py | 0 src/ahc/__init__.py | 0 src/ahc/apps/__init__.py | 0 {AHC_app/AHC_app => src/ahc}/asgi.py | 2 +- {AHC_app/AHC_app => src/ahc}/settings.py | 12 ++++++------ {AHC_app/AHC_app => src/ahc}/urls.py | 0 {AHC_app/AHC_app => src/ahc}/wsgi.py | 2 +- 14 files changed, 19 insertions(+), 18 deletions(-) rename {AHC_app/AHC_app => src}/__init__.py (100%) create mode 100644 src/ahc/__init__.py create mode 100644 src/ahc/apps/__init__.py rename {AHC_app/AHC_app => src/ahc}/asgi.py (82%) rename {AHC_app/AHC_app => src/ahc}/settings.py (96%) rename {AHC_app/AHC_app => src/ahc}/urls.py (100%) rename {AHC_app/AHC_app => src/ahc}/wsgi.py (82%) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 0de0866..41680ad 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -50,7 +50,7 @@ jobs: run: uv sync - name: Run tests (Django runner) - run: PYTHONPATH=AHC_app uv run python manage.py test + run: PYTHONPATH=src:AHC_app uv run python manage.py test - name: Run tests (pytest) run: uv run pytest -m "not slow" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8c6b900..f158cd1 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -83,7 +83,7 @@ services: ports: - "5000:5000" environment: - - DJANGO_SETTINGS_MODULE=AHC_app.settings + - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - PYTHONPATH=/app/AHC_app @@ -98,7 +98,7 @@ services: postgres_db: condition: service_healthy environment: - - DJANGO_SETTINGS_MODULE=AHC_app.settings + - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - PYTHONPATH=/app/AHC_app env_file: diff --git a/kubernetes/backend/web/secret.yaml.template b/kubernetes/backend/web/secret.yaml.template index 879694f..9b64c58 100644 --- a/kubernetes/backend/web/secret.yaml.template +++ b/kubernetes/backend/web/secret.yaml.template @@ -22,7 +22,7 @@ stringData: COUCH_CONNECTOR: "Server(\"http://127.0.0.1:5982\")" CELERY_BACKEND: "redis://redis:6379/0" - DJANGO_SETTINGS_MODULE: "AHC_app" + DJANGO_SETTINGS_MODULE: "ahc.settings" EMAIL_BACKEND: "django.core.mail.backends.smtp.EmailBackend" EMAIL_HOST: "sandbox.smtp.mailtrap.io" diff --git a/kubernetes/queue/celery/configmap.yaml b/kubernetes/queue/celery/configmap.yaml index f5c5e73..4b28051 100644 --- a/kubernetes/queue/celery/configmap.yaml +++ b/kubernetes/queue/celery/configmap.yaml @@ -3,4 +3,4 @@ #metadata: # name: celery-config #data: -# DJANGO_SETTINGS_MODULE: AHC_app.settings +# DJANGO_SETTINGS_MODULE: ahc.settings diff --git a/kubernetes/queue/celery/secret.yaml.template b/kubernetes/queue/celery/secret.yaml.template index 3757913..6e95f60 100644 --- a/kubernetes/queue/celery/secret.yaml.template +++ b/kubernetes/queue/celery/secret.yaml.template @@ -22,7 +22,7 @@ stringData: COUCH_CONNECTOR: "Server(\"http://127.0.0.1:5982\")" CELERY_BACKEND: "redis://redis:6379/0" - DJANGO_SETTINGS_MODULE: "AHC_app" + DJANGO_SETTINGS_MODULE: "ahc.settings" EMAIL_BACKEND: "django.core.mail.backends.smtp.EmailBackend" EMAIL_HOST: "sandbox.smtp.mailtrap.io" diff --git a/manage.py b/manage.py index e51f677..cce3cfd 100644 --- a/manage.py +++ b/manage.py @@ -8,8 +8,9 @@ def main(): """Run administrative tasks.""" - sys.path.insert(0, str(Path(__file__).resolve().parent / "AHC_app")) - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "AHC_app.settings") + sys.path.insert(0, str(Path(__file__).resolve().parent / "src")) + sys.path.insert(1, str(Path(__file__).resolve().parent / "AHC_app")) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ahc.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/pyproject.toml b/pyproject.toml index 510afe5..12a478c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,8 +68,8 @@ quote-style = "double" indent-style = "space" [tool.pytest.ini_options] -DJANGO_SETTINGS_MODULE = "AHC_app.settings" -pythonpath = ["AHC_app"] +DJANGO_SETTINGS_MODULE = "ahc.settings" +pythonpath = ["src", "AHC_app"] python_files = ["test_*.py", "tests.py"] addopts = "--strict-markers" markers = [ diff --git a/AHC_app/AHC_app/__init__.py b/src/__init__.py similarity index 100% rename from AHC_app/AHC_app/__init__.py rename to src/__init__.py diff --git a/src/ahc/__init__.py b/src/ahc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ahc/apps/__init__.py b/src/ahc/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/AHC_app/AHC_app/asgi.py b/src/ahc/asgi.py similarity index 82% rename from AHC_app/AHC_app/asgi.py rename to src/ahc/asgi.py index 5ce600a..edf19c7 100644 --- a/AHC_app/AHC_app/asgi.py +++ b/src/ahc/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "AHC_app.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ahc.settings") application = get_asgi_application() diff --git a/AHC_app/AHC_app/settings.py b/src/ahc/settings.py similarity index 96% rename from AHC_app/AHC_app/settings.py rename to src/ahc/settings.py index 7ea92f3..0e97190 100644 --- a/AHC_app/AHC_app/settings.py +++ b/src/ahc/settings.py @@ -45,7 +45,7 @@ def _skip_external_services() -> bool: # Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent +BASE_DIR = Path(__file__).resolve().parents[2] # Quick-start development settings - unsuitable for production @@ -92,13 +92,13 @@ def _skip_external_services() -> bool: "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = "AHC_app.urls" +ROOT_URLCONF = "ahc.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", # 'DIRS': [], - "DIRS": [BASE_DIR / "templates"], + "DIRS": [BASE_DIR / "AHC_app" / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -116,7 +116,7 @@ def _skip_external_services() -> bool: CRISPY_TEMPLATE_PACK = "bootstrap4" -WSGI_APPLICATION = "AHC_app.wsgi.application" +WSGI_APPLICATION = "ahc.wsgi.application" # Database @@ -194,7 +194,7 @@ def _skip_external_services() -> bool: STATIC_URL = "/static/" STATIC_ROOT = "static_collected" STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "static/"), + os.path.join(BASE_DIR, "AHC_app/static/"), ] STATICFILES_FINDERS = [ @@ -232,7 +232,7 @@ def _skip_external_services() -> bool: """ MEDIA_URL = "/media/" -MEDIA_ROOT = os.path.join(BASE_DIR, "static/media") +MEDIA_ROOT = os.path.join(BASE_DIR, "AHC_app/static/media") # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field diff --git a/AHC_app/AHC_app/urls.py b/src/ahc/urls.py similarity index 100% rename from AHC_app/AHC_app/urls.py rename to src/ahc/urls.py diff --git a/AHC_app/AHC_app/wsgi.py b/src/ahc/wsgi.py similarity index 82% rename from AHC_app/AHC_app/wsgi.py rename to src/ahc/wsgi.py index 7375290..96b6880 100644 --- a/AHC_app/AHC_app/wsgi.py +++ b/src/ahc/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "AHC_app.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ahc.settings") application = get_wsgi_application() From 199006b63ec8d673e03ef28df4634d9d6609ec56 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Fri, 29 May 2026 19:33:45 +0200 Subject: [PATCH 17/31] refactor(celery): move celery_notifications to src/celery_notifications/ Stage 4 of src-layout refactor. Extract celery_notifications from AHC_app/AHC_app/ to src/ so it lives alongside the ahc project package. - Fix all AHC_app.celery_notifications.* imports -> celery_notifications.* - Fix logger_config.json path to use __file__-relative resolution - Fix DJANGO_SETTINGS_MODULE typo and stale "django_with_celery" values - Switch autodiscover_tasks() to use INSTALLED_APPS (no argument) - Update docker-compose queue/celery_beat commands and PYTHONPATH - Update k8s celery/celery-beat commands and PYTHONPATH pyproject.toml pythonpath keeps ["src", "AHC_app"] until Stage 5; medical_notes (still in AHC_app/) is imported by cron.py. --- docker/docker-compose.yml | 10 +++++----- kubernetes/queue/celery-beat/deployment.yaml | 4 ++-- kubernetes/queue/celery/deployment.yaml | 4 ++-- .../AHC_app => src}/celery_notifications/__init__.py | 0 .../AHC_app => src}/celery_notifications/config.py | 12 ++++++------ .../AHC_app => src}/celery_notifications/cron.py | 8 ++++---- .../celery_notifications/logger_config.json | 0 .../celery_notifications/utils/__init__.py | 0 .../celery_notifications/utils/discord_utils.py | 0 .../celery_notifications/utils/example_task.py | 0 .../celery_notifications/utils/sending_utils.py | 0 11 files changed, 19 insertions(+), 19 deletions(-) rename {AHC_app/AHC_app => src}/celery_notifications/__init__.py (100%) rename {AHC_app/AHC_app => src}/celery_notifications/config.py (77%) rename {AHC_app/AHC_app => src}/celery_notifications/cron.py (95%) rename {AHC_app/AHC_app => src}/celery_notifications/logger_config.json (100%) rename {AHC_app/AHC_app => src}/celery_notifications/utils/__init__.py (100%) rename {AHC_app/AHC_app => src}/celery_notifications/utils/discord_utils.py (100%) rename {AHC_app/AHC_app => src}/celery_notifications/utils/example_task.py (100%) rename {AHC_app/AHC_app => src}/celery_notifications/utils/sending_utils.py (100%) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f158cd1..b8aab76 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ services: condition: service_healthy environment: - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/AHC_app + - PYTHONPATH=/app/src:/app/AHC_app entrypoint: ["/bin/bash", "-c"] command: - | @@ -72,7 +72,7 @@ services: build: context: .. dockerfile: docker/Dockerfile-queue - command: celery -A AHC_app.celery_notifications.config:celery_obj worker -l info + command: celery -A celery_notifications.config:celery_obj worker -l info depends_on: redis: condition: service_healthy @@ -85,13 +85,13 @@ services: environment: - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/AHC_app + - PYTHONPATH=/app/src:/app/AHC_app celery_beat: build: context: .. dockerfile: docker/Dockerfile-queue - command: celery -A AHC_app.celery_notifications.config:celery_obj beat -l info + command: celery -A celery_notifications.config:celery_obj beat -l info depends_on: redis: condition: service_healthy @@ -100,7 +100,7 @@ services: environment: - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/AHC_app + - PYTHONPATH=/app/src:/app/AHC_app env_file: - ../.env diff --git a/kubernetes/queue/celery-beat/deployment.yaml b/kubernetes/queue/celery-beat/deployment.yaml index d7e4e6f..c4b655d 100644 --- a/kubernetes/queue/celery-beat/deployment.yaml +++ b/kubernetes/queue/celery-beat/deployment.yaml @@ -26,12 +26,12 @@ spec: - name: celery-beat image: ahc_app-queue:latest imagePullPolicy: Never - command: [ "celery", "-A", "AHC_app.celery_notifications.config:celery_obj", "beat", "-l", "info" ] + command: [ "celery", "-A", "celery_notifications.config:celery_obj", "beat", "-l", "info" ] env: - name: PYTHONUNBUFFERED value: "1" - name: PYTHONPATH - value: "/app" + value: "/app/src:/app/AHC_app" envFrom: - secretRef: name: web-secrets diff --git a/kubernetes/queue/celery/deployment.yaml b/kubernetes/queue/celery/deployment.yaml index ccfc7d9..fe4dc11 100644 --- a/kubernetes/queue/celery/deployment.yaml +++ b/kubernetes/queue/celery/deployment.yaml @@ -26,12 +26,12 @@ spec: - name: queue image: ahc_app-queue:latest imagePullPolicy: Never - command: [ "celery", "-A", "AHC_app.celery_notifications.config:celery_obj", "worker", "-l", "info"] + command: [ "celery", "-A", "celery_notifications.config:celery_obj", "worker", "-l", "info"] env: - name: PYTHONUNBUFFERED value: "1" - name: PYTHONPATH - value: "/app" + value: "/app/src:/app/AHC_app" envFrom: - secretRef: name: web-secrets diff --git a/AHC_app/AHC_app/celery_notifications/__init__.py b/src/celery_notifications/__init__.py similarity index 100% rename from AHC_app/AHC_app/celery_notifications/__init__.py rename to src/celery_notifications/__init__.py diff --git a/AHC_app/AHC_app/celery_notifications/config.py b/src/celery_notifications/config.py similarity index 77% rename from AHC_app/AHC_app/celery_notifications/config.py rename to src/celery_notifications/config.py index c474b79..6f4a3b1 100644 --- a/AHC_app/AHC_app/celery_notifications/config.py +++ b/src/celery_notifications/config.py @@ -5,23 +5,23 @@ from celery.utils.log import get_task_logger from django.conf import settings -# from AHC_app.celery_notifications.utils.discord_utils import send_via_discord -from AHC_app.celery_notifications.utils.sending_utils import send_via_email +# from celery_notifications.utils.discord_utils import send_via_discord +from celery_notifications.utils.sending_utils import send_via_email logger = get_task_logger(__name__) -os.environ.setdefault("DJANGO_SETTING_MODULE", "django_with_celery.settings") -celery_obj = Celery("django_with_celery") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ahc.settings") +celery_obj = Celery("ahc") celery_obj.config_from_object("django.conf:settings", namespace="CELERY") celery_obj.conf.broker_connection_retry_on_startup = True -celery_obj.autodiscover_tasks(["AHC_app"]) +celery_obj.autodiscover_tasks() @celery_obj.task(name="ahc.beat.dispatch_discord_notes") def dispatch_discord_notes(): - from AHC_app.celery_notifications.cron import send_discord_notes + from celery_notifications.cron import send_discord_notes send_discord_notes() diff --git a/AHC_app/AHC_app/celery_notifications/cron.py b/src/celery_notifications/cron.py similarity index 95% rename from AHC_app/AHC_app/celery_notifications/cron.py rename to src/celery_notifications/cron.py index 12d4009..a4aa3fe 100644 --- a/AHC_app/AHC_app/celery_notifications/cron.py +++ b/src/celery_notifications/cron.py @@ -9,19 +9,19 @@ from django.db.models import Q, QuerySet from django.tasks import task from django.utils import timezone +from medical_notes.models.type_feeding_notes import EmailNotification -from AHC_app.celery_notifications.config import ( +from celery_notifications.config import ( send_discord_notifications, send_email_notifications, ) -from AHC_app.celery_notifications.utils.example_task import send_mail_fnc -from medical_notes.models.type_feeding_notes import EmailNotification +from celery_notifications.utils.example_task import send_mail_fnc logger = logging.getLogger("crons_logger") def setup_logging(): - config_file = pathlib.Path("AHC_app/celery_notifications/logger_config.json") + config_file = pathlib.Path(__file__).parent / "logger_config.json" with open(config_file) as file: config = json.load(file) logging.config.dictConfig(config) diff --git a/AHC_app/AHC_app/celery_notifications/logger_config.json b/src/celery_notifications/logger_config.json similarity index 100% rename from AHC_app/AHC_app/celery_notifications/logger_config.json rename to src/celery_notifications/logger_config.json diff --git a/AHC_app/AHC_app/celery_notifications/utils/__init__.py b/src/celery_notifications/utils/__init__.py similarity index 100% rename from AHC_app/AHC_app/celery_notifications/utils/__init__.py rename to src/celery_notifications/utils/__init__.py diff --git a/AHC_app/AHC_app/celery_notifications/utils/discord_utils.py b/src/celery_notifications/utils/discord_utils.py similarity index 100% rename from AHC_app/AHC_app/celery_notifications/utils/discord_utils.py rename to src/celery_notifications/utils/discord_utils.py diff --git a/AHC_app/AHC_app/celery_notifications/utils/example_task.py b/src/celery_notifications/utils/example_task.py similarity index 100% rename from AHC_app/AHC_app/celery_notifications/utils/example_task.py rename to src/celery_notifications/utils/example_task.py diff --git a/AHC_app/AHC_app/celery_notifications/utils/sending_utils.py b/src/celery_notifications/utils/sending_utils.py similarity index 100% rename from AHC_app/AHC_app/celery_notifications/utils/sending_utils.py rename to src/celery_notifications/utils/sending_utils.py From 1d2267ca009eb03c3fe5596fd3c679e05e889c08 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Fri, 29 May 2026 20:22:19 +0200 Subject: [PATCH 18/31] refactor(layout): move Django apps to src/ahc/apps/ --- .github/workflows/django.yml | 2 +- .pre-commit-config.yaml | 2 +- docker/docker-compose.yml | 6 +++--- justfile | 2 +- kubernetes/queue/celery-beat/deployment.yaml | 2 +- kubernetes/queue/celery/deployment.yaml | 2 +- manage.py | 1 - pyproject.toml | 4 ++-- {AHC_app => src/ahc/apps}/animals/__init__.py | 0 {AHC_app => src/ahc/apps}/animals/admin.py | 0 {AHC_app => src/ahc/apps}/animals/apps.py | 2 +- {AHC_app => src/ahc/apps}/animals/forms.py | 2 +- .../ahc/apps}/animals/migrations/0001_initial.py | 0 {AHC_app => src/ahc/apps}/animals/migrations/__init__.py | 0 {AHC_app => src/ahc/apps}/animals/mixins/__init__.py | 0 .../ahc/apps}/animals/mixins/animal_owner_permissions.py | 2 +- {AHC_app => src/ahc/apps}/animals/models.py | 2 +- {AHC_app => src/ahc/apps}/animals/signals.py | 4 ++-- .../animals/templates/animals/all_animals_stable.html | 0 .../animals/templates/animals/animal_confirm_delete.html | 0 .../apps}/animals/templates/animals/change_birthday.html | 0 .../animals/templates/animals/change_first_contact.html | 0 .../ahc/apps}/animals/templates/animals/change_owner.html | 0 .../ahc/apps}/animals/templates/animals/create.html | 0 .../ahc/apps}/animals/templates/animals/image.html | 0 .../apps}/animals/templates/animals/manage_keepers.html | 0 .../ahc/apps}/animals/templates/animals/profile.html | 0 .../ahc/apps}/animals/templatetags/__init__.py | 0 .../ahc/apps}/animals/templatetags/custom_timesince.py | 0 {AHC_app => src/ahc/apps}/animals/tests.py | 0 {AHC_app => src/ahc/apps}/animals/urls.py | 4 ++-- {AHC_app => src/ahc/apps}/animals/utils_owner/__init__.py | 0 {AHC_app => src/ahc/apps}/animals/utils_owner/forms.py | 4 ++-- {AHC_app => src/ahc/apps}/animals/utils_owner/views.py | 6 +++--- {AHC_app => src/ahc/apps}/animals/views.py | 6 +++--- {AHC_app => src/ahc/apps}/homepage/__init__.py | 0 {AHC_app => src/ahc/apps}/homepage/admin.py | 2 +- {AHC_app => src/ahc/apps}/homepage/apps.py | 2 +- {AHC_app => src/ahc/apps}/homepage/management/__init__.py | 0 .../ahc/apps}/homepage/management/commands/__init__.py | 0 .../ahc/apps}/homepage/migrations/0001_initial.py | 4 ++-- .../migrations/0002_alter_profilebackground_content.py | 4 ++-- .../ahc/apps}/homepage/migrations/0003_cronjob.py | 0 {AHC_app => src/ahc/apps}/homepage/migrations/__init__.py | 0 {AHC_app => src/ahc/apps}/homepage/models.py | 2 +- .../ahc/apps}/homepage/templates/homepage/base.html | 0 .../homepage/templates/homepage/change_list_results.html | 0 .../ahc/apps}/homepage/templates/homepage/homepage.html | 0 {AHC_app => src/ahc/apps}/homepage/tests/__init__.py | 0 .../ahc/apps}/homepage/tests/models/__init__.py | 0 .../ahc/apps}/homepage/tests/models/test_animal_title.py | 0 {AHC_app => src/ahc/apps}/homepage/tests/test_csp.py | 0 {AHC_app => src/ahc/apps}/homepage/tests/test_homepage.py | 2 +- {AHC_app => src/ahc/apps}/homepage/urls.py | 2 +- {AHC_app => src/ahc/apps}/homepage/utils.py | 0 {AHC_app => src/ahc/apps}/homepage/views.py | 4 ++-- {AHC_app => src/ahc/apps}/medical_notes/__init__.py | 0 {AHC_app => src/ahc/apps}/medical_notes/admin.py | 0 {AHC_app => src/ahc/apps}/medical_notes/apps.py | 2 +- {AHC_app => src/ahc/apps}/medical_notes/forms.py | 0 {AHC_app => src/ahc/apps}/medical_notes/forms/__init__.py | 0 .../ahc/apps}/medical_notes/forms/type_basic_note.py | 2 +- .../ahc/apps}/medical_notes/forms/type_feeding_notes.py | 2 +- .../apps}/medical_notes/forms/type_measurement_notes.py | 2 +- .../ahc/apps}/medical_notes/migrations/0001_initial.py | 0 ..._currentmedicine_animal_delete_currentdiet_and_more.py | 0 ...dingnote_smsnotification_emailnotification_and_more.py | 0 .../0004_alter_discordnotification_timezone_and_more.py | 0 .../migrations/0005_medicalrecordattachment.py | 0 .../0006_remove_medicalrecordattachment_description.py | 0 .../migrations/0007_medicalrecordattachment_url.py | 0 .../migrations/0008_alter_medicalrecordattachment_file.py | 0 ...0009_discordnotification_last_modification_and_more.py | 0 ...lter_discordnotification_last_modification_and_more.py | 0 .../0011_medicalrecordattachment_description.py | 0 .../0012_medicalrecordattachment_file_name_and_more.py | 0 .../0013_remove_medicalrecordattachment_url_and_more.py | 0 ...lter_discordnotification_last_modification_and_more.py | 0 .../ahc/apps}/medical_notes/migrations/__init__.py | 0 {AHC_app => src/ahc/apps}/medical_notes/models.py | 0 .../ahc/apps}/medical_notes/models/__init__.py | 0 .../ahc/apps}/medical_notes/models/type_basic_note.py | 4 ++-- .../ahc/apps}/medical_notes/models/type_feeding_notes.py | 2 +- .../apps}/medical_notes/models/type_measurement_notes.py | 4 ++-- {AHC_app => src/ahc/apps}/medical_notes/signals.py | 0 .../ahc/apps}/medical_notes/signals/__init__.py | 0 .../ahc/apps}/medical_notes/signals/type_feeding_notes.py | 4 ++-- .../apps}/medical_notes/signals/type_measurement_notes.py | 6 +++--- .../medical_notes/templates/medical_notes/create.html | 0 .../templates/medical_notes/create_notify.html | 0 .../templates/medical_notes/delete_confirm.html | 0 .../apps}/medical_notes/templates/medical_notes/edit.html | 0 .../templates/medical_notes/feeding_notes_list.html | 0 .../templates/medical_notes/full_timeline_of_notes.html | 0 .../templates/medical_notes/notification_list.html | 0 .../ahc/apps}/medical_notes/templatetags/__init__.py | 0 .../apps}/medical_notes/templatetags/custom_file_name.py | 2 +- .../medical_notes/templatetags/custom_to_class_name.py | 0 {AHC_app => src/ahc/apps}/medical_notes/tests.py | 0 {AHC_app => src/ahc/apps}/medical_notes/urls.py | 6 +++--- {AHC_app => src/ahc/apps}/medical_notes/views.py | 0 {AHC_app => src/ahc/apps}/medical_notes/views/__init__.py | 0 .../medical_notes/views/mixins/user_animal_permisions.py | 0 .../ahc/apps}/medical_notes/views/type_basic_note.py | 6 +++--- .../ahc/apps}/medical_notes/views/type_feeding_notes.py | 8 ++++---- .../apps}/medical_notes/views/type_measurement_notes.py | 8 ++++---- {AHC_app => src/ahc/apps}/users/__init__.py | 0 {AHC_app => src/ahc/apps}/users/admin.py | 2 +- {AHC_app => src/ahc/apps}/users/apps.py | 2 +- {AHC_app => src/ahc/apps}/users/forms.py | 2 +- .../ahc/apps}/users/migrations/0001_initial.py | 0 .../migrations/0002_profile_allow_recennt_animals_list.py | 0 .../apps}/users/migrations/0003_profile_pinned_animals.py | 0 {AHC_app => src/ahc/apps}/users/migrations/__init__.py | 0 {AHC_app => src/ahc/apps}/users/models.py | 2 +- {AHC_app => src/ahc/apps}/users/signals.py | 4 ++-- .../ahc/apps}/users/templates/users/login.html | 0 .../ahc/apps}/users/templates/users/login_success.html | 0 .../ahc/apps}/users/templates/users/logout.html | 0 .../ahc/apps}/users/templates/users/password_reset.html | 0 .../users/templates/users/password_reset_complete.html | 0 .../users/templates/users/password_reset_confirm.html | 0 .../apps}/users/templates/users/password_reset_done.html | 0 .../apps}/users/templates/users/password_reset_email.html | 0 .../ahc/apps}/users/templates/users/profile.html | 0 .../ahc/apps}/users/templates/users/register.html | 0 {AHC_app => src/ahc/apps}/users/tests.py | 0 {AHC_app => src/ahc/apps}/users/urls.py | 2 +- {AHC_app => src/ahc/apps}/users/views.py | 4 ++-- src/ahc/settings.py | 8 ++++---- src/ahc/urls.py | 8 ++++---- src/celery_notifications/cron.py | 2 +- 132 files changed, 84 insertions(+), 85 deletions(-) rename {AHC_app => src/ahc/apps}/animals/__init__.py (100%) rename {AHC_app => src/ahc/apps}/animals/admin.py (100%) rename {AHC_app => src/ahc/apps}/animals/apps.py (84%) rename {AHC_app => src/ahc/apps}/animals/forms.py (96%) rename {AHC_app => src/ahc/apps}/animals/migrations/0001_initial.py (100%) rename {AHC_app => src/ahc/apps}/animals/migrations/__init__.py (100%) rename {AHC_app => src/ahc/apps}/animals/mixins/__init__.py (100%) rename {AHC_app => src/ahc/apps}/animals/mixins/animal_owner_permissions.py (86%) rename {AHC_app => src/ahc/apps}/animals/models.py (95%) rename {AHC_app => src/ahc/apps}/animals/signals.py (95%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/all_animals_stable.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/animal_confirm_delete.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/change_birthday.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/change_first_contact.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/change_owner.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/create.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/image.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/manage_keepers.html (100%) rename {AHC_app => src/ahc/apps}/animals/templates/animals/profile.html (100%) rename {AHC_app => src/ahc/apps}/animals/templatetags/__init__.py (100%) rename {AHC_app => src/ahc/apps}/animals/templatetags/custom_timesince.py (100%) rename {AHC_app => src/ahc/apps}/animals/tests.py (100%) rename {AHC_app => src/ahc/apps}/animals/urls.py (89%) rename {AHC_app => src/ahc/apps}/animals/utils_owner/__init__.py (100%) rename {AHC_app => src/ahc/apps}/animals/utils_owner/forms.py (97%) rename {AHC_app => src/ahc/apps}/animals/utils_owner/views.py (96%) rename {AHC_app => src/ahc/apps}/animals/views.py (94%) rename {AHC_app => src/ahc/apps}/homepage/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/admin.py (92%) rename {AHC_app => src/ahc/apps}/homepage/apps.py (80%) rename {AHC_app => src/ahc/apps}/homepage/management/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/management/commands/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/migrations/0001_initial.py (95%) rename {AHC_app => src/ahc/apps}/homepage/migrations/0002_alter_profilebackground_content.py (79%) rename {AHC_app => src/ahc/apps}/homepage/migrations/0003_cronjob.py (100%) rename {AHC_app => src/ahc/apps}/homepage/migrations/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/models.py (97%) rename {AHC_app => src/ahc/apps}/homepage/templates/homepage/base.html (100%) rename {AHC_app => src/ahc/apps}/homepage/templates/homepage/change_list_results.html (100%) rename {AHC_app => src/ahc/apps}/homepage/templates/homepage/homepage.html (100%) rename {AHC_app => src/ahc/apps}/homepage/tests/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/tests/models/__init__.py (100%) rename {AHC_app => src/ahc/apps}/homepage/tests/models/test_animal_title.py (100%) rename {AHC_app => src/ahc/apps}/homepage/tests/test_csp.py (100%) rename {AHC_app => src/ahc/apps}/homepage/tests/test_homepage.py (97%) rename {AHC_app => src/ahc/apps}/homepage/urls.py (67%) rename {AHC_app => src/ahc/apps}/homepage/utils.py (100%) rename {AHC_app => src/ahc/apps}/homepage/views.py (90%) rename {AHC_app => src/ahc/apps}/medical_notes/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/admin.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/apps.py (82%) rename {AHC_app => src/ahc/apps}/medical_notes/forms.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/forms/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/forms/type_basic_note.py (98%) rename {AHC_app => src/ahc/apps}/medical_notes/forms/type_feeding_notes.py (96%) rename {AHC_app => src/ahc/apps}/medical_notes/forms/type_measurement_notes.py (91%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0001_initial.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0002_remove_currentmedicine_animal_delete_currentdiet_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0003_feedingnote_smsnotification_emailnotification_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0004_alter_discordnotification_timezone_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0005_medicalrecordattachment.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0006_remove_medicalrecordattachment_description.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0007_medicalrecordattachment_url.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0008_alter_medicalrecordattachment_file.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0011_medicalrecordattachment_description.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/migrations/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/models.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/models/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/models/type_basic_note.py (95%) rename {AHC_app => src/ahc/apps}/medical_notes/models/type_feeding_notes.py (97%) rename {AHC_app => src/ahc/apps}/medical_notes/models/type_measurement_notes.py (93%) rename {AHC_app => src/ahc/apps}/medical_notes/signals.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/signals/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/signals/type_feeding_notes.py (77%) rename {AHC_app => src/ahc/apps}/medical_notes/signals/type_measurement_notes.py (86%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/create.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/create_notify.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/delete_confirm.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/edit.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/feeding_notes_list.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/full_timeline_of_notes.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templates/medical_notes/notification_list.html (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templatetags/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/templatetags/custom_file_name.py (82%) rename {AHC_app => src/ahc/apps}/medical_notes/templatetags/custom_to_class_name.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/tests.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/urls.py (88%) rename {AHC_app => src/ahc/apps}/medical_notes/views.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/views/__init__.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/views/mixins/user_animal_permisions.py (100%) rename {AHC_app => src/ahc/apps}/medical_notes/views/type_basic_note.py (98%) rename {AHC_app => src/ahc/apps}/medical_notes/views/type_feeding_notes.py (96%) rename {AHC_app => src/ahc/apps}/medical_notes/views/type_measurement_notes.py (90%) rename {AHC_app => src/ahc/apps}/users/__init__.py (100%) rename {AHC_app => src/ahc/apps}/users/admin.py (60%) rename {AHC_app => src/ahc/apps}/users/apps.py (84%) rename {AHC_app => src/ahc/apps}/users/forms.py (92%) rename {AHC_app => src/ahc/apps}/users/migrations/0001_initial.py (100%) rename {AHC_app => src/ahc/apps}/users/migrations/0002_profile_allow_recennt_animals_list.py (100%) rename {AHC_app => src/ahc/apps}/users/migrations/0003_profile_pinned_animals.py (100%) rename {AHC_app => src/ahc/apps}/users/migrations/__init__.py (100%) rename {AHC_app => src/ahc/apps}/users/models.py (94%) rename {AHC_app => src/ahc/apps}/users/signals.py (90%) rename {AHC_app => src/ahc/apps}/users/templates/users/login.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/login_success.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/logout.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/password_reset.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/password_reset_complete.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/password_reset_confirm.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/password_reset_done.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/password_reset_email.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/profile.html (100%) rename {AHC_app => src/ahc/apps}/users/templates/users/register.html (100%) rename {AHC_app => src/ahc/apps}/users/tests.py (100%) rename {AHC_app => src/ahc/apps}/users/urls.py (97%) rename {AHC_app => src/ahc/apps}/users/views.py (90%) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 41680ad..65986f7 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -50,7 +50,7 @@ jobs: run: uv sync - name: Run tests (Django runner) - run: PYTHONPATH=src:AHC_app uv run python manage.py test + run: PYTHONPATH=src uv run python manage.py test - name: Run tests (pytest) run: uv run pytest -m "not slow" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65414c6..682aad7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: hooks: - id: bandit name: bandit - entry: bandit -r AHC_app -c pyproject.toml + entry: bandit -r src -c pyproject.toml -q language: python additional_dependencies: ["bandit[toml]==1.9.4"] pass_filenames: false diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b8aab76..380ed09 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ services: condition: service_healthy environment: - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src:/app/AHC_app + - PYTHONPATH=/app/src entrypoint: ["/bin/bash", "-c"] command: - | @@ -85,7 +85,7 @@ services: environment: - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src:/app/AHC_app + - PYTHONPATH=/app/src celery_beat: build: @@ -100,7 +100,7 @@ services: environment: - DJANGO_SETTINGS_MODULE=ahc.settings - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src:/app/AHC_app + - PYTHONPATH=/app/src env_file: - ../.env diff --git a/justfile b/justfile index 242ee8f..ccaa8dc 100644 --- a/justfile +++ b/justfile @@ -8,7 +8,7 @@ install: lint: uv run ruff check . uv run codespell - uv run bandit -r . -c pyproject.toml + uv run bandit -r . -c pyproject.toml -q # Format code and auto-fix lint issues format: diff --git a/kubernetes/queue/celery-beat/deployment.yaml b/kubernetes/queue/celery-beat/deployment.yaml index c4b655d..5952f2e 100644 --- a/kubernetes/queue/celery-beat/deployment.yaml +++ b/kubernetes/queue/celery-beat/deployment.yaml @@ -31,7 +31,7 @@ spec: - name: PYTHONUNBUFFERED value: "1" - name: PYTHONPATH - value: "/app/src:/app/AHC_app" + value: "/app/src" envFrom: - secretRef: name: web-secrets diff --git a/kubernetes/queue/celery/deployment.yaml b/kubernetes/queue/celery/deployment.yaml index fe4dc11..e89a7cc 100644 --- a/kubernetes/queue/celery/deployment.yaml +++ b/kubernetes/queue/celery/deployment.yaml @@ -31,7 +31,7 @@ spec: - name: PYTHONUNBUFFERED value: "1" - name: PYTHONPATH - value: "/app/src:/app/AHC_app" + value: "/app/src" envFrom: - secretRef: name: web-secrets diff --git a/manage.py b/manage.py index cce3cfd..fe42511 100644 --- a/manage.py +++ b/manage.py @@ -9,7 +9,6 @@ def main(): """Run administrative tasks.""" sys.path.insert(0, str(Path(__file__).resolve().parent / "src")) - sys.path.insert(1, str(Path(__file__).resolve().parent / "AHC_app")) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ahc.settings") try: from django.core.management import execute_from_command_line diff --git a/pyproject.toml b/pyproject.toml index 12a478c..afd39e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ indent-style = "space" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "ahc.settings" -pythonpath = ["src", "AHC_app"] +pythonpath = ["src"] python_files = ["test_*.py", "tests.py"] addopts = "--strict-markers" markers = [ @@ -82,7 +82,7 @@ markers = [ python-version = "3.14" [tool.codespell] -skip = "uv.lock,./static,./static_collected" +skip = "uv.lock,./AHC_app/static,./AHC_app/static_collected,./static,./static_collected" [tool.bandit] exclude_dirs = [".venv"] diff --git a/AHC_app/animals/__init__.py b/src/ahc/apps/animals/__init__.py similarity index 100% rename from AHC_app/animals/__init__.py rename to src/ahc/apps/animals/__init__.py diff --git a/AHC_app/animals/admin.py b/src/ahc/apps/animals/admin.py similarity index 100% rename from AHC_app/animals/admin.py rename to src/ahc/apps/animals/admin.py diff --git a/AHC_app/animals/apps.py b/src/ahc/apps/animals/apps.py similarity index 84% rename from AHC_app/animals/apps.py rename to src/ahc/apps/animals/apps.py index 47778cd..1023188 100644 --- a/AHC_app/animals/apps.py +++ b/src/ahc/apps/animals/apps.py @@ -3,7 +3,7 @@ class AnimalsConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "animals" + name = "ahc.apps.animals" def ready(self): pass diff --git a/AHC_app/animals/forms.py b/src/ahc/apps/animals/forms.py similarity index 96% rename from AHC_app/animals/forms.py rename to src/ahc/apps/animals/forms.py index 41503d0..ee9e217 100644 --- a/AHC_app/animals/forms.py +++ b/src/ahc/apps/animals/forms.py @@ -2,7 +2,7 @@ from django.core.validators import MaxLengthValidator, MinLengthValidator from django.db.models import Q -from animals.models import Animal +from ahc.apps.animals.models import Animal class AnimalRegisterForm(forms.ModelForm): diff --git a/AHC_app/animals/migrations/0001_initial.py b/src/ahc/apps/animals/migrations/0001_initial.py similarity index 100% rename from AHC_app/animals/migrations/0001_initial.py rename to src/ahc/apps/animals/migrations/0001_initial.py diff --git a/AHC_app/animals/migrations/__init__.py b/src/ahc/apps/animals/migrations/__init__.py similarity index 100% rename from AHC_app/animals/migrations/__init__.py rename to src/ahc/apps/animals/migrations/__init__.py diff --git a/AHC_app/animals/mixins/__init__.py b/src/ahc/apps/animals/mixins/__init__.py similarity index 100% rename from AHC_app/animals/mixins/__init__.py rename to src/ahc/apps/animals/mixins/__init__.py diff --git a/AHC_app/animals/mixins/animal_owner_permissions.py b/src/ahc/apps/animals/mixins/animal_owner_permissions.py similarity index 86% rename from AHC_app/animals/mixins/animal_owner_permissions.py rename to src/ahc/apps/animals/mixins/animal_owner_permissions.py index 4ebf125..e62efdb 100644 --- a/AHC_app/animals/mixins/animal_owner_permissions.py +++ b/src/ahc/apps/animals/mixins/animal_owner_permissions.py @@ -1,6 +1,6 @@ from django.contrib.auth.mixins import UserPassesTestMixin -from animals.models import Animal +from ahc.apps.animals.models import Animal class UserPassesOwnershipTestMixin(UserPassesTestMixin): diff --git a/AHC_app/animals/models.py b/src/ahc/apps/animals/models.py similarity index 95% rename from AHC_app/animals/models.py rename to src/ahc/apps/animals/models.py index 14cab2f..962f461 100644 --- a/AHC_app/animals/models.py +++ b/src/ahc/apps/animals/models.py @@ -2,7 +2,7 @@ from django.db import models -from users.models import Profile as UserProfile +from ahc.apps.users.models import Profile as UserProfile class Animal(models.Model): diff --git a/AHC_app/animals/signals.py b/src/ahc/apps/animals/signals.py similarity index 95% rename from AHC_app/animals/signals.py rename to src/ahc/apps/animals/signals.py index f0d6a88..daddc0f 100644 --- a/AHC_app/animals/signals.py +++ b/src/ahc/apps/animals/signals.py @@ -3,8 +3,8 @@ from django.db.models.signals import post_delete, post_save, pre_delete from django.dispatch import receiver -from animals.models import Animal -from users.models import Profile +from ahc.apps.animals.models import Animal +from ahc.apps.users.models import Profile @receiver(post_save, sender=Animal) diff --git a/AHC_app/animals/templates/animals/all_animals_stable.html b/src/ahc/apps/animals/templates/animals/all_animals_stable.html similarity index 100% rename from AHC_app/animals/templates/animals/all_animals_stable.html rename to src/ahc/apps/animals/templates/animals/all_animals_stable.html diff --git a/AHC_app/animals/templates/animals/animal_confirm_delete.html b/src/ahc/apps/animals/templates/animals/animal_confirm_delete.html similarity index 100% rename from AHC_app/animals/templates/animals/animal_confirm_delete.html rename to src/ahc/apps/animals/templates/animals/animal_confirm_delete.html diff --git a/AHC_app/animals/templates/animals/change_birthday.html b/src/ahc/apps/animals/templates/animals/change_birthday.html similarity index 100% rename from AHC_app/animals/templates/animals/change_birthday.html rename to src/ahc/apps/animals/templates/animals/change_birthday.html diff --git a/AHC_app/animals/templates/animals/change_first_contact.html b/src/ahc/apps/animals/templates/animals/change_first_contact.html similarity index 100% rename from AHC_app/animals/templates/animals/change_first_contact.html rename to src/ahc/apps/animals/templates/animals/change_first_contact.html diff --git a/AHC_app/animals/templates/animals/change_owner.html b/src/ahc/apps/animals/templates/animals/change_owner.html similarity index 100% rename from AHC_app/animals/templates/animals/change_owner.html rename to src/ahc/apps/animals/templates/animals/change_owner.html diff --git a/AHC_app/animals/templates/animals/create.html b/src/ahc/apps/animals/templates/animals/create.html similarity index 100% rename from AHC_app/animals/templates/animals/create.html rename to src/ahc/apps/animals/templates/animals/create.html diff --git a/AHC_app/animals/templates/animals/image.html b/src/ahc/apps/animals/templates/animals/image.html similarity index 100% rename from AHC_app/animals/templates/animals/image.html rename to src/ahc/apps/animals/templates/animals/image.html diff --git a/AHC_app/animals/templates/animals/manage_keepers.html b/src/ahc/apps/animals/templates/animals/manage_keepers.html similarity index 100% rename from AHC_app/animals/templates/animals/manage_keepers.html rename to src/ahc/apps/animals/templates/animals/manage_keepers.html diff --git a/AHC_app/animals/templates/animals/profile.html b/src/ahc/apps/animals/templates/animals/profile.html similarity index 100% rename from AHC_app/animals/templates/animals/profile.html rename to src/ahc/apps/animals/templates/animals/profile.html diff --git a/AHC_app/animals/templatetags/__init__.py b/src/ahc/apps/animals/templatetags/__init__.py similarity index 100% rename from AHC_app/animals/templatetags/__init__.py rename to src/ahc/apps/animals/templatetags/__init__.py diff --git a/AHC_app/animals/templatetags/custom_timesince.py b/src/ahc/apps/animals/templatetags/custom_timesince.py similarity index 100% rename from AHC_app/animals/templatetags/custom_timesince.py rename to src/ahc/apps/animals/templatetags/custom_timesince.py diff --git a/AHC_app/animals/tests.py b/src/ahc/apps/animals/tests.py similarity index 100% rename from AHC_app/animals/tests.py rename to src/ahc/apps/animals/tests.py diff --git a/AHC_app/animals/urls.py b/src/ahc/apps/animals/urls.py similarity index 89% rename from AHC_app/animals/urls.py rename to src/ahc/apps/animals/urls.py index 0ab0ade..f636055 100644 --- a/AHC_app/animals/urls.py +++ b/src/ahc/apps/animals/urls.py @@ -1,7 +1,7 @@ from django.urls import path -from animals import views as animal_views -from animals.utils_owner import views as animal_owner_views +from ahc.apps.animals import views as animal_views +from ahc.apps.animals.utils_owner import views as animal_owner_views urlpatterns = [ path("create/", animal_views.CreateAnimalView.as_view(), name="animal_create"), diff --git a/AHC_app/animals/utils_owner/__init__.py b/src/ahc/apps/animals/utils_owner/__init__.py similarity index 100% rename from AHC_app/animals/utils_owner/__init__.py rename to src/ahc/apps/animals/utils_owner/__init__.py diff --git a/AHC_app/animals/utils_owner/forms.py b/src/ahc/apps/animals/utils_owner/forms.py similarity index 97% rename from AHC_app/animals/utils_owner/forms.py rename to src/ahc/apps/animals/utils_owner/forms.py index 14fa925..fb7cf7f 100644 --- a/AHC_app/animals/utils_owner/forms.py +++ b/src/ahc/apps/animals/utils_owner/forms.py @@ -3,8 +3,8 @@ from django import forms from PIL import Image -from animals.models import Animal -from users.models import Profile +from ahc.apps.animals.models import Animal +from ahc.apps.users.models import Profile class ImageUploadForm(forms.ModelForm): diff --git a/AHC_app/animals/utils_owner/views.py b/src/ahc/apps/animals/utils_owner/views.py similarity index 96% rename from AHC_app/animals/utils_owner/views.py rename to src/ahc/apps/animals/utils_owner/views.py index 4e6f706..fe72dad 100644 --- a/AHC_app/animals/utils_owner/views.py +++ b/src/ahc/apps/animals/utils_owner/views.py @@ -5,9 +5,9 @@ from django.views.generic.edit import FormView from PIL import Image -from animals.mixins.animal_owner_permissions import UserPassesOwnershipTestMixin -from animals.models import Animal -from animals.utils_owner.forms import ( +from ahc.apps.animals.mixins.animal_owner_permissions import UserPassesOwnershipTestMixin +from ahc.apps.animals.models import Animal +from ahc.apps.animals.utils_owner.forms import ( ChangeBirthdayForm, ChangeFirstContactForm, ChangeOwnerForm, diff --git a/AHC_app/animals/views.py b/src/ahc/apps/animals/views.py similarity index 94% rename from AHC_app/animals/views.py rename to src/ahc/apps/animals/views.py index d4d362d..f3eed53 100644 --- a/AHC_app/animals/views.py +++ b/src/ahc/apps/animals/views.py @@ -7,9 +7,9 @@ from django.views.generic.detail import DetailView from django.views.generic.edit import FormView -from animals.forms import AnimalRegisterForm, PinAnimalForm -from animals.models import Animal -from medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.animals.forms import AnimalRegisterForm, PinAnimalForm +from ahc.apps.animals.models import Animal +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord # from users.models import Profile as UserProfile diff --git a/AHC_app/homepage/__init__.py b/src/ahc/apps/homepage/__init__.py similarity index 100% rename from AHC_app/homepage/__init__.py rename to src/ahc/apps/homepage/__init__.py diff --git a/AHC_app/homepage/admin.py b/src/ahc/apps/homepage/admin.py similarity index 92% rename from AHC_app/homepage/admin.py rename to src/ahc/apps/homepage/admin.py index bc300d6..1307303 100644 --- a/AHC_app/homepage/admin.py +++ b/src/ahc/apps/homepage/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from homepage.models import AnimalTitle, CronJob +from ahc.apps.homepage.models import AnimalTitle, CronJob admin.site.register(AnimalTitle) diff --git a/AHC_app/homepage/apps.py b/src/ahc/apps/homepage/apps.py similarity index 80% rename from AHC_app/homepage/apps.py rename to src/ahc/apps/homepage/apps.py index a4c9949..ef7e748 100644 --- a/AHC_app/homepage/apps.py +++ b/src/ahc/apps/homepage/apps.py @@ -3,4 +3,4 @@ class HomepageConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "homepage" + name = "ahc.apps.homepage" diff --git a/AHC_app/homepage/management/__init__.py b/src/ahc/apps/homepage/management/__init__.py similarity index 100% rename from AHC_app/homepage/management/__init__.py rename to src/ahc/apps/homepage/management/__init__.py diff --git a/AHC_app/homepage/management/commands/__init__.py b/src/ahc/apps/homepage/management/commands/__init__.py similarity index 100% rename from AHC_app/homepage/management/commands/__init__.py rename to src/ahc/apps/homepage/management/commands/__init__.py diff --git a/AHC_app/homepage/migrations/0001_initial.py b/src/ahc/apps/homepage/migrations/0001_initial.py similarity index 95% rename from AHC_app/homepage/migrations/0001_initial.py rename to src/ahc/apps/homepage/migrations/0001_initial.py index 4c846ca..47a51d2 100644 --- a/AHC_app/homepage/migrations/0001_initial.py +++ b/src/ahc/apps/homepage/migrations/0001_initial.py @@ -4,7 +4,7 @@ from django.conf import settings from django.db import migrations, models -import homepage.utils +import ahc.apps.homepage.utils class Migration(migrations.Migration): @@ -47,7 +47,7 @@ class Migration(migrations.Migration): ( "content", models.ImageField( - default=homepage.utils.ImageGenerator.default_profile_image, + default=ahc.apps.homepage.utils.ImageGenerator.default_profile_image, upload_to="static/media/background", ), ), diff --git a/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py b/src/ahc/apps/homepage/migrations/0002_alter_profilebackground_content.py similarity index 79% rename from AHC_app/homepage/migrations/0002_alter_profilebackground_content.py rename to src/ahc/apps/homepage/migrations/0002_alter_profilebackground_content.py index 273b3bb..572cb3e 100644 --- a/AHC_app/homepage/migrations/0002_alter_profilebackground_content.py +++ b/src/ahc/apps/homepage/migrations/0002_alter_profilebackground_content.py @@ -2,7 +2,7 @@ from django.db import migrations, models -import homepage.utils +import ahc.apps.homepage.utils class Migration(migrations.Migration): @@ -15,7 +15,7 @@ class Migration(migrations.Migration): model_name="profilebackground", name="content", field=models.ImageField( - default=homepage.utils.ImageGenerator.default_profile_image, + default=ahc.apps.homepage.utils.ImageGenerator.default_profile_image, upload_to="static/media/background", ), ), diff --git a/AHC_app/homepage/migrations/0003_cronjob.py b/src/ahc/apps/homepage/migrations/0003_cronjob.py similarity index 100% rename from AHC_app/homepage/migrations/0003_cronjob.py rename to src/ahc/apps/homepage/migrations/0003_cronjob.py diff --git a/AHC_app/homepage/migrations/__init__.py b/src/ahc/apps/homepage/migrations/__init__.py similarity index 100% rename from AHC_app/homepage/migrations/__init__.py rename to src/ahc/apps/homepage/migrations/__init__.py diff --git a/AHC_app/homepage/models.py b/src/ahc/apps/homepage/models.py similarity index 97% rename from AHC_app/homepage/models.py rename to src/ahc/apps/homepage/models.py index 8091afb..43cc191 100644 --- a/AHC_app/homepage/models.py +++ b/src/ahc/apps/homepage/models.py @@ -2,7 +2,7 @@ from django.db import models from django.urls import reverse -from homepage.utils import ImageGenerator +from ahc.apps.homepage.utils import ImageGenerator class Privilege(models.Model): diff --git a/AHC_app/homepage/templates/homepage/base.html b/src/ahc/apps/homepage/templates/homepage/base.html similarity index 100% rename from AHC_app/homepage/templates/homepage/base.html rename to src/ahc/apps/homepage/templates/homepage/base.html diff --git a/AHC_app/homepage/templates/homepage/change_list_results.html b/src/ahc/apps/homepage/templates/homepage/change_list_results.html similarity index 100% rename from AHC_app/homepage/templates/homepage/change_list_results.html rename to src/ahc/apps/homepage/templates/homepage/change_list_results.html diff --git a/AHC_app/homepage/templates/homepage/homepage.html b/src/ahc/apps/homepage/templates/homepage/homepage.html similarity index 100% rename from AHC_app/homepage/templates/homepage/homepage.html rename to src/ahc/apps/homepage/templates/homepage/homepage.html diff --git a/AHC_app/homepage/tests/__init__.py b/src/ahc/apps/homepage/tests/__init__.py similarity index 100% rename from AHC_app/homepage/tests/__init__.py rename to src/ahc/apps/homepage/tests/__init__.py diff --git a/AHC_app/homepage/tests/models/__init__.py b/src/ahc/apps/homepage/tests/models/__init__.py similarity index 100% rename from AHC_app/homepage/tests/models/__init__.py rename to src/ahc/apps/homepage/tests/models/__init__.py diff --git a/AHC_app/homepage/tests/models/test_animal_title.py b/src/ahc/apps/homepage/tests/models/test_animal_title.py similarity index 100% rename from AHC_app/homepage/tests/models/test_animal_title.py rename to src/ahc/apps/homepage/tests/models/test_animal_title.py diff --git a/AHC_app/homepage/tests/test_csp.py b/src/ahc/apps/homepage/tests/test_csp.py similarity index 100% rename from AHC_app/homepage/tests/test_csp.py rename to src/ahc/apps/homepage/tests/test_csp.py diff --git a/AHC_app/homepage/tests/test_homepage.py b/src/ahc/apps/homepage/tests/test_homepage.py similarity index 97% rename from AHC_app/homepage/tests/test_homepage.py rename to src/ahc/apps/homepage/tests/test_homepage.py index ac3ac0b..97b9797 100644 --- a/AHC_app/homepage/tests/test_homepage.py +++ b/src/ahc/apps/homepage/tests/test_homepage.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User from django.test import Client, TestCase -from homepage.models import AnimalTitle +from ahc.apps.homepage.models import AnimalTitle client = Client() diff --git a/AHC_app/homepage/urls.py b/src/ahc/apps/homepage/urls.py similarity index 67% rename from AHC_app/homepage/urls.py rename to src/ahc/apps/homepage/urls.py index 30af557..1ce25d8 100644 --- a/AHC_app/homepage/urls.py +++ b/src/ahc/apps/homepage/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from homepage.views import HomepageView +from ahc.apps.homepage.views import HomepageView urlpatterns = [ path("", HomepageView.as_view(), name="Homepage"), diff --git a/AHC_app/homepage/utils.py b/src/ahc/apps/homepage/utils.py similarity index 100% rename from AHC_app/homepage/utils.py rename to src/ahc/apps/homepage/utils.py diff --git a/AHC_app/homepage/views.py b/src/ahc/apps/homepage/views.py similarity index 90% rename from AHC_app/homepage/views.py rename to src/ahc/apps/homepage/views.py index 611875f..e100c62 100644 --- a/AHC_app/homepage/views.py +++ b/src/ahc/apps/homepage/views.py @@ -1,8 +1,8 @@ from django.db.models import Q from django.views.generic import TemplateView -from animals.models import Animal -from users.models import Profile as UserProfile +from ahc.apps.animals.models import Animal +from ahc.apps.users.models import Profile as UserProfile class HomepageView(TemplateView): diff --git a/AHC_app/medical_notes/__init__.py b/src/ahc/apps/medical_notes/__init__.py similarity index 100% rename from AHC_app/medical_notes/__init__.py rename to src/ahc/apps/medical_notes/__init__.py diff --git a/AHC_app/medical_notes/admin.py b/src/ahc/apps/medical_notes/admin.py similarity index 100% rename from AHC_app/medical_notes/admin.py rename to src/ahc/apps/medical_notes/admin.py diff --git a/AHC_app/medical_notes/apps.py b/src/ahc/apps/medical_notes/apps.py similarity index 82% rename from AHC_app/medical_notes/apps.py rename to src/ahc/apps/medical_notes/apps.py index 2fc8826..5d5a03a 100644 --- a/AHC_app/medical_notes/apps.py +++ b/src/ahc/apps/medical_notes/apps.py @@ -3,7 +3,7 @@ class MedicalNotesConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "medical_notes" + name = "ahc.apps.medical_notes" def ready(self): pass diff --git a/AHC_app/medical_notes/forms.py b/src/ahc/apps/medical_notes/forms.py similarity index 100% rename from AHC_app/medical_notes/forms.py rename to src/ahc/apps/medical_notes/forms.py diff --git a/AHC_app/medical_notes/forms/__init__.py b/src/ahc/apps/medical_notes/forms/__init__.py similarity index 100% rename from AHC_app/medical_notes/forms/__init__.py rename to src/ahc/apps/medical_notes/forms/__init__.py diff --git a/AHC_app/medical_notes/forms/type_basic_note.py b/src/ahc/apps/medical_notes/forms/type_basic_note.py similarity index 98% rename from AHC_app/medical_notes/forms/type_basic_note.py rename to src/ahc/apps/medical_notes/forms/type_basic_note.py index a03bc1b..766d818 100644 --- a/AHC_app/medical_notes/forms/type_basic_note.py +++ b/src/ahc/apps/medical_notes/forms/type_basic_note.py @@ -1,7 +1,7 @@ from django import forms # from animals.models import Animal as AnimalProfile -from medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment # from django.core.validators import MaxLengthValidator, MinLengthValidator # from django.db.models import Q diff --git a/AHC_app/medical_notes/forms/type_feeding_notes.py b/src/ahc/apps/medical_notes/forms/type_feeding_notes.py similarity index 96% rename from AHC_app/medical_notes/forms/type_feeding_notes.py rename to src/ahc/apps/medical_notes/forms/type_feeding_notes.py index 2ded162..31a8660 100644 --- a/AHC_app/medical_notes/forms/type_feeding_notes.py +++ b/src/ahc/apps/medical_notes/forms/type_feeding_notes.py @@ -2,7 +2,7 @@ from django.conf import settings from timezone_field import TimeZoneFormField -from medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote +from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote class DietRecordForm(forms.ModelForm): diff --git a/AHC_app/medical_notes/forms/type_measurement_notes.py b/src/ahc/apps/medical_notes/forms/type_measurement_notes.py similarity index 91% rename from AHC_app/medical_notes/forms/type_measurement_notes.py rename to src/ahc/apps/medical_notes/forms/type_measurement_notes.py index 7fa41f3..3923682 100644 --- a/AHC_app/medical_notes/forms/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/forms/type_measurement_notes.py @@ -1,6 +1,6 @@ from django import forms -from medical_notes.models.type_measurement_notes import BiometricHeightRecords, BiometricWeightRecords +from ahc.apps.medical_notes.models.type_measurement_notes import BiometricHeightRecords, BiometricWeightRecords class BiometricRecordForm(forms.Form): diff --git a/AHC_app/medical_notes/migrations/0001_initial.py b/src/ahc/apps/medical_notes/migrations/0001_initial.py similarity index 100% rename from AHC_app/medical_notes/migrations/0001_initial.py rename to src/ahc/apps/medical_notes/migrations/0001_initial.py diff --git a/AHC_app/medical_notes/migrations/0002_remove_currentmedicine_animal_delete_currentdiet_and_more.py b/src/ahc/apps/medical_notes/migrations/0002_remove_currentmedicine_animal_delete_currentdiet_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0002_remove_currentmedicine_animal_delete_currentdiet_and_more.py rename to src/ahc/apps/medical_notes/migrations/0002_remove_currentmedicine_animal_delete_currentdiet_and_more.py diff --git a/AHC_app/medical_notes/migrations/0003_feedingnote_smsnotification_emailnotification_and_more.py b/src/ahc/apps/medical_notes/migrations/0003_feedingnote_smsnotification_emailnotification_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0003_feedingnote_smsnotification_emailnotification_and_more.py rename to src/ahc/apps/medical_notes/migrations/0003_feedingnote_smsnotification_emailnotification_and_more.py diff --git a/AHC_app/medical_notes/migrations/0004_alter_discordnotification_timezone_and_more.py b/src/ahc/apps/medical_notes/migrations/0004_alter_discordnotification_timezone_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0004_alter_discordnotification_timezone_and_more.py rename to src/ahc/apps/medical_notes/migrations/0004_alter_discordnotification_timezone_and_more.py diff --git a/AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py b/src/ahc/apps/medical_notes/migrations/0005_medicalrecordattachment.py similarity index 100% rename from AHC_app/medical_notes/migrations/0005_medicalrecordattachment.py rename to src/ahc/apps/medical_notes/migrations/0005_medicalrecordattachment.py diff --git a/AHC_app/medical_notes/migrations/0006_remove_medicalrecordattachment_description.py b/src/ahc/apps/medical_notes/migrations/0006_remove_medicalrecordattachment_description.py similarity index 100% rename from AHC_app/medical_notes/migrations/0006_remove_medicalrecordattachment_description.py rename to src/ahc/apps/medical_notes/migrations/0006_remove_medicalrecordattachment_description.py diff --git a/AHC_app/medical_notes/migrations/0007_medicalrecordattachment_url.py b/src/ahc/apps/medical_notes/migrations/0007_medicalrecordattachment_url.py similarity index 100% rename from AHC_app/medical_notes/migrations/0007_medicalrecordattachment_url.py rename to src/ahc/apps/medical_notes/migrations/0007_medicalrecordattachment_url.py diff --git a/AHC_app/medical_notes/migrations/0008_alter_medicalrecordattachment_file.py b/src/ahc/apps/medical_notes/migrations/0008_alter_medicalrecordattachment_file.py similarity index 100% rename from AHC_app/medical_notes/migrations/0008_alter_medicalrecordattachment_file.py rename to src/ahc/apps/medical_notes/migrations/0008_alter_medicalrecordattachment_file.py diff --git a/AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py b/src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py rename to src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py diff --git a/AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py b/src/ahc/apps/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py rename to src/ahc/apps/medical_notes/migrations/0010_alter_discordnotification_last_modification_and_more.py diff --git a/AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py b/src/ahc/apps/medical_notes/migrations/0011_medicalrecordattachment_description.py similarity index 100% rename from AHC_app/medical_notes/migrations/0011_medicalrecordattachment_description.py rename to src/ahc/apps/medical_notes/migrations/0011_medicalrecordattachment_description.py diff --git a/AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py b/src/ahc/apps/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py rename to src/ahc/apps/medical_notes/migrations/0012_medicalrecordattachment_file_name_and_more.py diff --git a/AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py b/src/ahc/apps/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py rename to src/ahc/apps/medical_notes/migrations/0013_remove_medicalrecordattachment_url_and_more.py diff --git a/AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py b/src/ahc/apps/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py similarity index 100% rename from AHC_app/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py rename to src/ahc/apps/medical_notes/migrations/0014_alter_discordnotification_last_modification_and_more.py diff --git a/AHC_app/medical_notes/migrations/__init__.py b/src/ahc/apps/medical_notes/migrations/__init__.py similarity index 100% rename from AHC_app/medical_notes/migrations/__init__.py rename to src/ahc/apps/medical_notes/migrations/__init__.py diff --git a/AHC_app/medical_notes/models.py b/src/ahc/apps/medical_notes/models.py similarity index 100% rename from AHC_app/medical_notes/models.py rename to src/ahc/apps/medical_notes/models.py diff --git a/AHC_app/medical_notes/models/__init__.py b/src/ahc/apps/medical_notes/models/__init__.py similarity index 100% rename from AHC_app/medical_notes/models/__init__.py rename to src/ahc/apps/medical_notes/models/__init__.py diff --git a/AHC_app/medical_notes/models/type_basic_note.py b/src/ahc/apps/medical_notes/models/type_basic_note.py similarity index 95% rename from AHC_app/medical_notes/models/type_basic_note.py rename to src/ahc/apps/medical_notes/models/type_basic_note.py index 1edb49c..757be9e 100644 --- a/AHC_app/medical_notes/models/type_basic_note.py +++ b/src/ahc/apps/medical_notes/models/type_basic_note.py @@ -4,8 +4,8 @@ from taggit.managers import TaggableManager from taggit.models import GenericUUIDTaggedItemBase, TaggedItemBase -from animals.models import Animal -from users.models import Profile as UserProfile +from ahc.apps.animals.models import Animal +from ahc.apps.users.models import Profile as UserProfile class UUIDTaggedItem(GenericUUIDTaggedItemBase, TaggedItemBase): diff --git a/AHC_app/medical_notes/models/type_feeding_notes.py b/src/ahc/apps/medical_notes/models/type_feeding_notes.py similarity index 97% rename from AHC_app/medical_notes/models/type_feeding_notes.py rename to src/ahc/apps/medical_notes/models/type_feeding_notes.py index 87725b8..67a31c0 100644 --- a/AHC_app/medical_notes/models/type_feeding_notes.py +++ b/src/ahc/apps/medical_notes/models/type_feeding_notes.py @@ -5,7 +5,7 @@ from django.db import models from timezone_field import TimeZoneField -from medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord class FeedingNote(models.Model): diff --git a/AHC_app/medical_notes/models/type_measurement_notes.py b/src/ahc/apps/medical_notes/models/type_measurement_notes.py similarity index 93% rename from AHC_app/medical_notes/models/type_measurement_notes.py rename to src/ahc/apps/medical_notes/models/type_measurement_notes.py index 84eb0ec..d2a0925 100644 --- a/AHC_app/medical_notes/models/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/models/type_measurement_notes.py @@ -1,7 +1,7 @@ from django.db import models -from animals.models import Animal -from medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.animals.models import Animal +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord class BiometricHeightRecords(models.Model): diff --git a/AHC_app/medical_notes/signals.py b/src/ahc/apps/medical_notes/signals.py similarity index 100% rename from AHC_app/medical_notes/signals.py rename to src/ahc/apps/medical_notes/signals.py diff --git a/AHC_app/medical_notes/signals/__init__.py b/src/ahc/apps/medical_notes/signals/__init__.py similarity index 100% rename from AHC_app/medical_notes/signals/__init__.py rename to src/ahc/apps/medical_notes/signals/__init__.py diff --git a/AHC_app/medical_notes/signals/type_feeding_notes.py b/src/ahc/apps/medical_notes/signals/type_feeding_notes.py similarity index 77% rename from AHC_app/medical_notes/signals/type_feeding_notes.py rename to src/ahc/apps/medical_notes/signals/type_feeding_notes.py index 9fa7dc5..28dc8d5 100644 --- a/AHC_app/medical_notes/signals/type_feeding_notes.py +++ b/src/ahc/apps/medical_notes/signals/type_feeding_notes.py @@ -2,8 +2,8 @@ from django.db.models.signals import post_save from django.dispatch import receiver -from medical_notes.models.type_feeding_notes import FeedingNote -from users.models import Profile as UserProfile +from ahc.apps.medical_notes.models.type_feeding_notes import FeedingNote +from ahc.apps.users.models import Profile as UserProfile @receiver(post_save, sender=FeedingNote) diff --git a/AHC_app/medical_notes/signals/type_measurement_notes.py b/src/ahc/apps/medical_notes/signals/type_measurement_notes.py similarity index 86% rename from AHC_app/medical_notes/signals/type_measurement_notes.py rename to src/ahc/apps/medical_notes/signals/type_measurement_notes.py index 63783b1..38e3507 100644 --- a/AHC_app/medical_notes/signals/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/signals/type_measurement_notes.py @@ -3,9 +3,9 @@ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver -from medical_notes.models.type_basic_note import MedicalRecord -from medical_notes.models.type_measurement_notes import BiometricRecord -from users.models import Profile as UserProfile +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.medical_notes.models.type_measurement_notes import BiometricRecord +from ahc.apps.users.models import Profile as UserProfile @receiver(pre_save, sender=BiometricRecord) diff --git a/AHC_app/medical_notes/templates/medical_notes/create.html b/src/ahc/apps/medical_notes/templates/medical_notes/create.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/create.html rename to src/ahc/apps/medical_notes/templates/medical_notes/create.html diff --git a/AHC_app/medical_notes/templates/medical_notes/create_notify.html b/src/ahc/apps/medical_notes/templates/medical_notes/create_notify.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/create_notify.html rename to src/ahc/apps/medical_notes/templates/medical_notes/create_notify.html diff --git a/AHC_app/medical_notes/templates/medical_notes/delete_confirm.html b/src/ahc/apps/medical_notes/templates/medical_notes/delete_confirm.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/delete_confirm.html rename to src/ahc/apps/medical_notes/templates/medical_notes/delete_confirm.html diff --git a/AHC_app/medical_notes/templates/medical_notes/edit.html b/src/ahc/apps/medical_notes/templates/medical_notes/edit.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/edit.html rename to src/ahc/apps/medical_notes/templates/medical_notes/edit.html diff --git a/AHC_app/medical_notes/templates/medical_notes/feeding_notes_list.html b/src/ahc/apps/medical_notes/templates/medical_notes/feeding_notes_list.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/feeding_notes_list.html rename to src/ahc/apps/medical_notes/templates/medical_notes/feeding_notes_list.html diff --git a/AHC_app/medical_notes/templates/medical_notes/full_timeline_of_notes.html b/src/ahc/apps/medical_notes/templates/medical_notes/full_timeline_of_notes.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/full_timeline_of_notes.html rename to src/ahc/apps/medical_notes/templates/medical_notes/full_timeline_of_notes.html diff --git a/AHC_app/medical_notes/templates/medical_notes/notification_list.html b/src/ahc/apps/medical_notes/templates/medical_notes/notification_list.html similarity index 100% rename from AHC_app/medical_notes/templates/medical_notes/notification_list.html rename to src/ahc/apps/medical_notes/templates/medical_notes/notification_list.html diff --git a/AHC_app/medical_notes/templatetags/__init__.py b/src/ahc/apps/medical_notes/templatetags/__init__.py similarity index 100% rename from AHC_app/medical_notes/templatetags/__init__.py rename to src/ahc/apps/medical_notes/templatetags/__init__.py diff --git a/AHC_app/medical_notes/templatetags/custom_file_name.py b/src/ahc/apps/medical_notes/templatetags/custom_file_name.py similarity index 82% rename from AHC_app/medical_notes/templatetags/custom_file_name.py rename to src/ahc/apps/medical_notes/templatetags/custom_file_name.py index 9932126..79421c0 100644 --- a/AHC_app/medical_notes/templatetags/custom_file_name.py +++ b/src/ahc/apps/medical_notes/templatetags/custom_file_name.py @@ -1,6 +1,6 @@ from django import template -from medical_notes.models.type_feeding_notes import FeedingNotification +from ahc.apps.medical_notes.models.type_feeding_notes import FeedingNotification register = template.Library() diff --git a/AHC_app/medical_notes/templatetags/custom_to_class_name.py b/src/ahc/apps/medical_notes/templatetags/custom_to_class_name.py similarity index 100% rename from AHC_app/medical_notes/templatetags/custom_to_class_name.py rename to src/ahc/apps/medical_notes/templatetags/custom_to_class_name.py diff --git a/AHC_app/medical_notes/tests.py b/src/ahc/apps/medical_notes/tests.py similarity index 100% rename from AHC_app/medical_notes/tests.py rename to src/ahc/apps/medical_notes/tests.py diff --git a/AHC_app/medical_notes/urls.py b/src/ahc/apps/medical_notes/urls.py similarity index 88% rename from AHC_app/medical_notes/urls.py rename to src/ahc/apps/medical_notes/urls.py index e9fbfab..cf54c43 100644 --- a/AHC_app/medical_notes/urls.py +++ b/src/ahc/apps/medical_notes/urls.py @@ -1,8 +1,8 @@ from django.urls import path -from medical_notes.views import type_basic_note as notes_views -from medical_notes.views import type_feeding_notes as feeding_views -from medical_notes.views import type_measurement_notes as measurement_views +from ahc.apps.medical_notes.views import type_basic_note as notes_views +from ahc.apps.medical_notes.views import type_feeding_notes as feeding_views +from ahc.apps.medical_notes.views import type_measurement_notes as measurement_views urlpatterns = [ path("/create/", notes_views.CreateNoteFormView.as_view(), name="note_create"), diff --git a/AHC_app/medical_notes/views.py b/src/ahc/apps/medical_notes/views.py similarity index 100% rename from AHC_app/medical_notes/views.py rename to src/ahc/apps/medical_notes/views.py diff --git a/AHC_app/medical_notes/views/__init__.py b/src/ahc/apps/medical_notes/views/__init__.py similarity index 100% rename from AHC_app/medical_notes/views/__init__.py rename to src/ahc/apps/medical_notes/views/__init__.py diff --git a/AHC_app/medical_notes/views/mixins/user_animal_permisions.py b/src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py similarity index 100% rename from AHC_app/medical_notes/views/mixins/user_animal_permisions.py rename to src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py diff --git a/AHC_app/medical_notes/views/type_basic_note.py b/src/ahc/apps/medical_notes/views/type_basic_note.py similarity index 98% rename from AHC_app/medical_notes/views/type_basic_note.py rename to src/ahc/apps/medical_notes/views/type_basic_note.py index b7271ba..13110d8 100644 --- a/AHC_app/medical_notes/views/type_basic_note.py +++ b/src/ahc/apps/medical_notes/views/type_basic_note.py @@ -12,14 +12,14 @@ from django.views.generic.edit import DeleteView, FormView, UpdateView from django.views.generic.list import ListView -from animals.models import Animal as AnimalProfile -from medical_notes.forms.type_basic_note import ( +from ahc.apps.animals.models import Animal as AnimalProfile +from ahc.apps.medical_notes.forms.type_basic_note import ( MedicalRecordEditForm, MedicalRecordEditRelatedAnimalsForm, MedicalRecordForm, UploadAppendixForm, ) -from medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment # UploadAppendixFormSet = formset_factory(UploadAppendixForm, extra=0) diff --git a/AHC_app/medical_notes/views/type_feeding_notes.py b/src/ahc/apps/medical_notes/views/type_feeding_notes.py similarity index 96% rename from AHC_app/medical_notes/views/type_feeding_notes.py rename to src/ahc/apps/medical_notes/views/type_feeding_notes.py index 8c23a6b..13a4fc1 100644 --- a/AHC_app/medical_notes/views/type_feeding_notes.py +++ b/src/ahc/apps/medical_notes/views/type_feeding_notes.py @@ -6,13 +6,13 @@ from django.views.generic.edit import FormView, UpdateView from django.views.generic.list import ListView -from animals.models import Animal as AnimalProfile -from medical_notes.forms.type_feeding_notes import ( +from ahc.apps.animals.models import Animal as AnimalProfile +from ahc.apps.medical_notes.forms.type_feeding_notes import ( DietRecordForm, NotificationRecordForm, ) -from medical_notes.models.type_basic_note import MedicalRecord -from medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote class DietRecordCreateView(LoginRequiredMixin, UserPassesTestMixin, FormView): diff --git a/AHC_app/medical_notes/views/type_measurement_notes.py b/src/ahc/apps/medical_notes/views/type_measurement_notes.py similarity index 90% rename from AHC_app/medical_notes/views/type_measurement_notes.py rename to src/ahc/apps/medical_notes/views/type_measurement_notes.py index 7d89125..0e48e33 100644 --- a/AHC_app/medical_notes/views/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/views/type_measurement_notes.py @@ -1,10 +1,10 @@ from django.shortcuts import get_object_or_404, redirect, reverse from django.views.generic.edit import FormView -from animals.models import Animal as AnimalProfile -from medical_notes.forms.type_measurement_notes import BiometricRecordForm -from medical_notes.models.type_basic_note import MedicalRecord -from medical_notes.models.type_measurement_notes import ( +from ahc.apps.animals.models import Animal as AnimalProfile +from ahc.apps.medical_notes.forms.type_measurement_notes import BiometricRecordForm +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord +from ahc.apps.medical_notes.models.type_measurement_notes import ( BiometricCustomRecords, BiometricHeightRecords, BiometricRecord, diff --git a/AHC_app/users/__init__.py b/src/ahc/apps/users/__init__.py similarity index 100% rename from AHC_app/users/__init__.py rename to src/ahc/apps/users/__init__.py diff --git a/AHC_app/users/admin.py b/src/ahc/apps/users/admin.py similarity index 60% rename from AHC_app/users/admin.py rename to src/ahc/apps/users/admin.py index 8886aeb..6074baa 100644 --- a/AHC_app/users/admin.py +++ b/src/ahc/apps/users/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from users.models import Profile +from ahc.apps.users.models import Profile admin.site.register(Profile) diff --git a/AHC_app/users/apps.py b/src/ahc/apps/users/apps.py similarity index 84% rename from AHC_app/users/apps.py rename to src/ahc/apps/users/apps.py index d7ff0b2..ab86c76 100644 --- a/AHC_app/users/apps.py +++ b/src/ahc/apps/users/apps.py @@ -3,7 +3,7 @@ class UsersConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "users" + name = "ahc.apps.users" def ready(self): pass diff --git a/AHC_app/users/forms.py b/src/ahc/apps/users/forms.py similarity index 92% rename from AHC_app/users/forms.py rename to src/ahc/apps/users/forms.py index 9a4bd29..acc0b2f 100644 --- a/AHC_app/users/forms.py +++ b/src/ahc/apps/users/forms.py @@ -1,7 +1,7 @@ from django import forms from django.contrib.auth.forms import User, UserCreationForm -from users.models import Profile +from ahc.apps.users.models import Profile class UserRegisterForm(UserCreationForm): diff --git a/AHC_app/users/migrations/0001_initial.py b/src/ahc/apps/users/migrations/0001_initial.py similarity index 100% rename from AHC_app/users/migrations/0001_initial.py rename to src/ahc/apps/users/migrations/0001_initial.py diff --git a/AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py b/src/ahc/apps/users/migrations/0002_profile_allow_recennt_animals_list.py similarity index 100% rename from AHC_app/users/migrations/0002_profile_allow_recennt_animals_list.py rename to src/ahc/apps/users/migrations/0002_profile_allow_recennt_animals_list.py diff --git a/AHC_app/users/migrations/0003_profile_pinned_animals.py b/src/ahc/apps/users/migrations/0003_profile_pinned_animals.py similarity index 100% rename from AHC_app/users/migrations/0003_profile_pinned_animals.py rename to src/ahc/apps/users/migrations/0003_profile_pinned_animals.py diff --git a/AHC_app/users/migrations/__init__.py b/src/ahc/apps/users/migrations/__init__.py similarity index 100% rename from AHC_app/users/migrations/__init__.py rename to src/ahc/apps/users/migrations/__init__.py diff --git a/AHC_app/users/models.py b/src/ahc/apps/users/models.py similarity index 94% rename from AHC_app/users/models.py rename to src/ahc/apps/users/models.py index f64ad5a..badcf02 100644 --- a/AHC_app/users/models.py +++ b/src/ahc/apps/users/models.py @@ -2,7 +2,7 @@ from django.db import models from PIL import Image -from homepage.models import Privilege, ProfileBackground +from ahc.apps.homepage.models import Privilege, ProfileBackground class Profile(models.Model): diff --git a/AHC_app/users/signals.py b/src/ahc/apps/users/signals.py similarity index 90% rename from AHC_app/users/signals.py rename to src/ahc/apps/users/signals.py index 068927b..6a7f786 100644 --- a/AHC_app/users/signals.py +++ b/src/ahc/apps/users/signals.py @@ -2,8 +2,8 @@ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver -from homepage.models import Privilege, ProfileBackground -from users.models import Profile +from ahc.apps.homepage.models import Privilege, ProfileBackground +from ahc.apps.users.models import Profile @receiver(pre_save, sender=Profile) diff --git a/AHC_app/users/templates/users/login.html b/src/ahc/apps/users/templates/users/login.html similarity index 100% rename from AHC_app/users/templates/users/login.html rename to src/ahc/apps/users/templates/users/login.html diff --git a/AHC_app/users/templates/users/login_success.html b/src/ahc/apps/users/templates/users/login_success.html similarity index 100% rename from AHC_app/users/templates/users/login_success.html rename to src/ahc/apps/users/templates/users/login_success.html diff --git a/AHC_app/users/templates/users/logout.html b/src/ahc/apps/users/templates/users/logout.html similarity index 100% rename from AHC_app/users/templates/users/logout.html rename to src/ahc/apps/users/templates/users/logout.html diff --git a/AHC_app/users/templates/users/password_reset.html b/src/ahc/apps/users/templates/users/password_reset.html similarity index 100% rename from AHC_app/users/templates/users/password_reset.html rename to src/ahc/apps/users/templates/users/password_reset.html diff --git a/AHC_app/users/templates/users/password_reset_complete.html b/src/ahc/apps/users/templates/users/password_reset_complete.html similarity index 100% rename from AHC_app/users/templates/users/password_reset_complete.html rename to src/ahc/apps/users/templates/users/password_reset_complete.html diff --git a/AHC_app/users/templates/users/password_reset_confirm.html b/src/ahc/apps/users/templates/users/password_reset_confirm.html similarity index 100% rename from AHC_app/users/templates/users/password_reset_confirm.html rename to src/ahc/apps/users/templates/users/password_reset_confirm.html diff --git a/AHC_app/users/templates/users/password_reset_done.html b/src/ahc/apps/users/templates/users/password_reset_done.html similarity index 100% rename from AHC_app/users/templates/users/password_reset_done.html rename to src/ahc/apps/users/templates/users/password_reset_done.html diff --git a/AHC_app/users/templates/users/password_reset_email.html b/src/ahc/apps/users/templates/users/password_reset_email.html similarity index 100% rename from AHC_app/users/templates/users/password_reset_email.html rename to src/ahc/apps/users/templates/users/password_reset_email.html diff --git a/AHC_app/users/templates/users/profile.html b/src/ahc/apps/users/templates/users/profile.html similarity index 100% rename from AHC_app/users/templates/users/profile.html rename to src/ahc/apps/users/templates/users/profile.html diff --git a/AHC_app/users/templates/users/register.html b/src/ahc/apps/users/templates/users/register.html similarity index 100% rename from AHC_app/users/templates/users/register.html rename to src/ahc/apps/users/templates/users/register.html diff --git a/AHC_app/users/tests.py b/src/ahc/apps/users/tests.py similarity index 100% rename from AHC_app/users/tests.py rename to src/ahc/apps/users/tests.py diff --git a/AHC_app/users/urls.py b/src/ahc/apps/users/urls.py similarity index 97% rename from AHC_app/users/urls.py rename to src/ahc/apps/users/urls.py index 056bd50..33b0792 100644 --- a/AHC_app/users/urls.py +++ b/src/ahc/apps/users/urls.py @@ -7,7 +7,7 @@ ) from django.urls import path -from users import views as user_views +from ahc.apps.users import views as user_views urlpatterns = [ path("", auth_views.LoginView.as_view(template_name="users/login.html"), name="login"), diff --git a/AHC_app/users/views.py b/src/ahc/apps/users/views.py similarity index 90% rename from AHC_app/users/views.py rename to src/ahc/apps/users/views.py index 538cbd9..bb7e5c1 100644 --- a/AHC_app/users/views.py +++ b/src/ahc/apps/users/views.py @@ -3,8 +3,8 @@ from django.urls import reverse_lazy from django.views.generic import CreateView, UpdateView -from users.forms import ProfileUpdateForm, UserRegisterForm, UserUpdateForm -from users.models import Profile +from ahc.apps.users.forms import ProfileUpdateForm, UserRegisterForm, UserUpdateForm +from ahc.apps.users.models import Profile class UserRegisterView(CreateView): diff --git a/src/ahc/settings.py b/src/ahc/settings.py index 0e97190..bf3b365 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -75,10 +75,10 @@ def _skip_external_services() -> bool: "bootstrap_modal_forms", "compressor", "taggit", - "homepage.apps.HomepageConfig", - "users.apps.UsersConfig", - "animals.apps.AnimalsConfig", - "medical_notes.apps.MedicalNotesConfig", + "ahc.apps.homepage.apps.HomepageConfig", + "ahc.apps.users.apps.UsersConfig", + "ahc.apps.animals.apps.AnimalsConfig", + "ahc.apps.medical_notes.apps.MedicalNotesConfig", ] MIDDLEWARE = [ diff --git a/src/ahc/urls.py b/src/ahc/urls.py index 1f92f89..cb0ff0d 100644 --- a/src/ahc/urls.py +++ b/src/ahc/urls.py @@ -23,10 +23,10 @@ urlpatterns = [ path("admin/", admin.site.urls), - path("", include("homepage.urls")), - path("user/", include("users.urls")), - path("pet/", include("animals.urls")), - path("note/", include("medical_notes.urls")), + path("", include("ahc.apps.homepage.urls")), + path("user/", include("ahc.apps.users.urls")), + path("pet/", include("ahc.apps.animals.urls")), + path("note/", include("ahc.apps.medical_notes.urls")), path( "favicon.ico", RedirectView.as_view(url=static("media/icons/chinchilla.png")), diff --git a/src/celery_notifications/cron.py b/src/celery_notifications/cron.py index a4aa3fe..de1a3c2 100644 --- a/src/celery_notifications/cron.py +++ b/src/celery_notifications/cron.py @@ -9,8 +9,8 @@ from django.db.models import Q, QuerySet from django.tasks import task from django.utils import timezone -from medical_notes.models.type_feeding_notes import EmailNotification +from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification from celery_notifications.config import ( send_discord_notifications, send_email_notifications, From 3f15c973fd16c93b80bbf851627b87ebecf6608a Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 11:23:04 +0200 Subject: [PATCH 19/31] docs(layout): finalize src-layout refactor documentation --- .gitattributes | 4 ++-- .gitignore | 8 ++++---- .pre-commit-config.yaml | 2 +- README.md | 13 +++++-------- pyproject.toml | 2 +- src/ahc/settings.py | 7 +++---- {AHC_app/static => static}/AHC_app/base.css | 0 .../static => static}/css/expanding_sections.css | 0 .../css/hide_large_description.css | 0 .../css/pico-1.5.9/.github/CONTRIBUTING.md | 0 .../.github/ISSUE_TEMPLATE/bug_report.md | 0 .../pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml | 0 .../css/pico-1.5.9/.github/examples.jpg | Bin .../css/pico-1.5.9/.github/logo.svg | 0 .../static => static}/css/pico-1.5.9/.gitignore | 0 .../static => static}/css/pico-1.5.9/LICENSE.md | 0 .../static => static}/css/pico-1.5.9/README.md | 0 .../static => static}/css/pico-1.5.9/composer.json | 0 .../css/pico-1.5.9/css/pico.classless.css | 0 .../css/pico-1.5.9/css/pico.classless.css.map | 0 .../css/pico-1.5.9/css/pico.classless.min.css | 0 .../css/pico-1.5.9/css/pico.classless.min.css.map | 0 .../static => static}/css/pico-1.5.9/css/pico.css | 0 .../css/pico-1.5.9/css/pico.css.map | 0 .../css/pico-1.5.9/css/pico.fluid.classless.css | 0 .../pico-1.5.9/css/pico.fluid.classless.css.map | 0 .../pico-1.5.9/css/pico.fluid.classless.min.css | 0 .../css/pico.fluid.classless.min.css.map | 0 .../css/pico-1.5.9/css/pico.min.css | 0 .../css/pico-1.5.9/css/pico.min.css.map | 0 .../css/pico-1.5.9/css/pico.slim.css | 0 .../css/pico-1.5.9/css/pico.slim.css.map | 0 .../css/pico-1.5.9/css/pico.slim.min.css | 0 .../css/pico-1.5.9/css/pico.slim.min.css.map | 0 .../css/pico-1.5.9/css/postcss.config.js | 0 .../css/pico-1.5.9/css/themes/default.css | 0 .../css/pico-1.5.9/css/themes/default.css.map | 0 .../css/pico-1.5.9/css/themes/default.min.css | 0 .../css/pico-1.5.9/css/themes/default.min.css.map | 0 .../css/pico-1.5.9/docs/accordions.html | 0 .../css/pico-1.5.9/docs/buttons.html | 0 .../css/pico-1.5.9/docs/cards.html | 0 .../css/pico-1.5.9/docs/classless.html | 0 .../css/pico-1.5.9/docs/containers.html | 0 .../css/pico-1.5.9/docs/css/pico.docs.css | 0 .../css/pico-1.5.9/docs/css/pico.docs.css.map | 0 .../css/pico-1.5.9/docs/css/pico.docs.min.css | 0 .../css/pico-1.5.9/docs/css/pico.docs.min.css.map | 0 .../css/pico-1.5.9/docs/customization.html | 0 .../css/pico-1.5.9/docs/dropdowns.html | 0 .../css/pico-1.5.9/docs/forms.html | 0 .../css/pico-1.5.9/docs/grid.html | 0 .../css/pico-1.5.9/docs/index.html | 0 .../css/pico-1.5.9/docs/js/commons.js | 0 .../css/pico-1.5.9/docs/js/commons.min.js | 0 .../css/pico-1.5.9/docs/js/customization.js | 0 .../css/pico-1.5.9/docs/js/customization.min.js | 0 .../css/pico-1.5.9/docs/js/grid.js | 0 .../css/pico-1.5.9/docs/js/grid.min.js | 0 .../css/pico-1.5.9/docs/js/modal.js | 0 .../css/pico-1.5.9/docs/js/modal.min.js | 0 .../css/pico-1.5.9/docs/js/src/color-picker.js | 0 .../docs/js/src/material-design-colors.js | 0 .../css/pico-1.5.9/docs/js/src/theme-switcher.js | 0 .../pico-1.5.9/docs/js/src/toggle-navigation.js | 0 .../css/pico-1.5.9/docs/loading.html | 0 .../css/pico-1.5.9/docs/modal.html | 0 .../css/pico-1.5.9/docs/navs.html | 0 .../css/pico-1.5.9/docs/progress.html | 0 .../static => static}/css/pico-1.5.9/docs/rtl.html | 0 .../css/pico-1.5.9/docs/scroller.html | 0 .../pico-1.5.9/docs/scss/components/_modal.scss | 0 .../css/pico-1.5.9/docs/scss/components/_nav.scss | 0 .../docs/scss/components/_theme-switcher.scss | 0 .../css/pico-1.5.9/docs/scss/content/_code.scss | 0 .../pico-1.5.9/docs/scss/content/_typography.scss | 0 .../css/pico-1.5.9/docs/scss/layout/_aside.scss | 0 .../css/pico-1.5.9/docs/scss/layout/_document.scss | 0 .../docs/scss/layout/_documentation.scss | 0 .../css/pico-1.5.9/docs/scss/layout/_main.scss | 0 .../css/pico-1.5.9/docs/scss/pico.docs.scss | 0 .../css/pico-1.5.9/docs/scss/themes/_docs.scss | 0 .../pico-1.5.9/docs/scss/themes/docs/_dark.scss | 0 .../pico-1.5.9/docs/scss/themes/docs/_icons.scss | 0 .../pico-1.5.9/docs/scss/themes/docs/_light.scss | 0 .../css/pico-1.5.9/docs/src/_footer.html | 0 .../css/pico-1.5.9/docs/src/_head.html | 0 .../css/pico-1.5.9/docs/src/_nav.html | 0 .../css/pico-1.5.9/docs/src/_sidebar.html | 0 .../css/pico-1.5.9/docs/src/accordions.html | 0 .../css/pico-1.5.9/docs/src/buttons.html | 0 .../css/pico-1.5.9/docs/src/cards.html | 0 .../css/pico-1.5.9/docs/src/classless.html | 0 .../css/pico-1.5.9/docs/src/containers.html | 0 .../css/pico-1.5.9/docs/src/customization.html | 0 .../css/pico-1.5.9/docs/src/dropdowns.html | 0 .../css/pico-1.5.9/docs/src/forms.html | 0 .../css/pico-1.5.9/docs/src/grid.html | 0 .../css/pico-1.5.9/docs/src/index.html | 0 .../css/pico-1.5.9/docs/src/loading.html | 0 .../css/pico-1.5.9/docs/src/modal.html | 0 .../css/pico-1.5.9/docs/src/navs.html | 0 .../css/pico-1.5.9/docs/src/progress.html | 0 .../css/pico-1.5.9/docs/src/rtl.html | 0 .../css/pico-1.5.9/docs/src/scroller.html | 0 .../css/pico-1.5.9/docs/src/tables.html | 0 .../css/pico-1.5.9/docs/src/themes.html | 0 .../css/pico-1.5.9/docs/src/tooltips.html | 0 .../css/pico-1.5.9/docs/src/typography.html | 0 .../css/pico-1.5.9/docs/src/we-love-classes.html | 0 .../css/pico-1.5.9/docs/tables.html | 0 .../css/pico-1.5.9/docs/themes.html | 0 .../css/pico-1.5.9/docs/tooltips.html | 0 .../css/pico-1.5.9/docs/typography.html | 0 .../css/pico-1.5.9/docs/we-love-classes.html | 0 .../css/pico-1.5.9/package-lock.json | 0 .../static => static}/css/pico-1.5.9/package.json | 0 .../css/pico-1.5.9/scss/_functions.scss | 0 .../css/pico-1.5.9/scss/_variables.scss | 0 .../css/pico-1.5.9/scss/components/_accordion.scss | 0 .../css/pico-1.5.9/scss/components/_card.scss | 0 .../css/pico-1.5.9/scss/components/_dropdown.scss | 0 .../css/pico-1.5.9/scss/components/_modal.scss | 0 .../css/pico-1.5.9/scss/components/_nav.scss | 0 .../css/pico-1.5.9/scss/components/_progress.scss | 0 .../css/pico-1.5.9/scss/content/_button.scss | 0 .../css/pico-1.5.9/scss/content/_code.scss | 0 .../css/pico-1.5.9/scss/content/_embedded.scss | 0 .../scss/content/_form-alt-input-types.scss | 0 .../scss/content/_form-checkbox-radio.scss | 0 .../css/pico-1.5.9/scss/content/_form.scss | 0 .../css/pico-1.5.9/scss/content/_miscs.scss | 0 .../css/pico-1.5.9/scss/content/_table.scss | 0 .../css/pico-1.5.9/scss/content/_typography.scss | 0 .../css/pico-1.5.9/scss/layout/_container.scss | 0 .../css/pico-1.5.9/scss/layout/_document.scss | 0 .../css/pico-1.5.9/scss/layout/_grid.scss | 0 .../css/pico-1.5.9/scss/layout/_scroller.scss | 0 .../css/pico-1.5.9/scss/layout/_section.scss | 0 .../css/pico-1.5.9/scss/layout/_sectioning.scss | 0 .../css/pico-1.5.9/scss/pico.classless.scss | 0 .../css/pico-1.5.9/scss/pico.fluid.classless.scss | 0 .../css/pico-1.5.9/scss/pico.scss | 0 .../css/pico-1.5.9/scss/pico.slim.scss | 0 .../css/pico-1.5.9/scss/postcss.config.js | 0 .../css/pico-1.5.9/scss/themes/default.scss | 0 .../pico-1.5.9/scss/themes/default/_colors.scss | 0 .../css/pico-1.5.9/scss/themes/default/_dark.scss | 0 .../css/pico-1.5.9/scss/themes/default/_light.scss | 0 .../pico-1.5.9/scss/themes/default/_styles.scss | 0 .../pico-1.5.9/scss/utilities/_accessibility.scss | 0 .../css/pico-1.5.9/scss/utilities/_loading.scss | 0 .../pico-1.5.9/scss/utilities/_reduce-motion.scss | 0 .../css/pico-1.5.9/scss/utilities/_tooltip.scss | 0 {AHC_app/static => static}/css/stable_grid.css | 0 {AHC_app/static => static}/css/timeline.css | 0 {AHC_app/static => static}/custom_pico.scss | 0 .../static => static}/js/expanding_sections.js | 0 .../js/hiding_note_fields_in_form.js | 0 .../js/hiding_note_fields_in_measurement_form.js | 0 {AHC_app/static => static}/js/pin_animal.js | 0 {AHC_app/static => static}/js/timeline.js | 0 .../media/background/background-1169534_1920.png | Bin .../static => static}/media/background/black.jpg | Bin .../media/background/default_title.jpg | Bin .../static => static}/media/background/lizard2.jpg | Bin .../static => static}/media/icons/chinchilla.png | Bin .../media/profile_pics/pet-care-icon.png | Bin .../media/profile_pics/pet-care.png | Bin .../media/profile_pics/signup.png | Bin .../media/profile_pics/signup2.png | Bin .../media/readme_examples/Animal profile.png | Bin .../media/readme_examples/Diet note details.png | Bin .../readme_examples/Full timeline of notes.png | Bin .../media/readme_examples/User registration.png | Bin .../partials/animal_card.html | 0 176 files changed, 16 insertions(+), 20 deletions(-) rename {AHC_app/static => static}/AHC_app/base.css (100%) rename {AHC_app/static => static}/css/expanding_sections.css (100%) rename {AHC_app/static => static}/css/hide_large_description.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.github/CONTRIBUTING.md (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.github/examples.jpg (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.github/logo.svg (100%) rename {AHC_app/static => static}/css/pico-1.5.9/.gitignore (100%) rename {AHC_app/static => static}/css/pico-1.5.9/LICENSE.md (100%) rename {AHC_app/static => static}/css/pico-1.5.9/README.md (100%) rename {AHC_app/static => static}/css/pico-1.5.9/composer.json (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.classless.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.classless.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.classless.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.classless.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.fluid.classless.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.fluid.classless.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.fluid.classless.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.fluid.classless.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.slim.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.slim.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.slim.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/pico.slim.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/postcss.config.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/themes/default.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/themes/default.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/themes/default.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/css/themes/default.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/accordions.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/buttons.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/cards.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/classless.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/containers.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/css/pico.docs.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/css/pico.docs.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/css/pico.docs.min.css (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/css/pico.docs.min.css.map (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/customization.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/dropdowns.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/forms.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/grid.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/index.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/commons.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/commons.min.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/customization.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/customization.min.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/grid.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/grid.min.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/modal.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/modal.min.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/src/color-picker.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/src/material-design-colors.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/src/theme-switcher.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/js/src/toggle-navigation.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/loading.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/modal.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/navs.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/progress.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/rtl.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scroller.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/components/_modal.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/components/_nav.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/content/_code.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/content/_typography.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/layout/_aside.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/layout/_document.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/layout/_documentation.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/layout/_main.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/pico.docs.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/themes/_docs.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/scss/themes/docs/_light.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/_footer.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/_head.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/_nav.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/_sidebar.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/accordions.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/buttons.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/cards.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/classless.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/containers.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/customization.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/dropdowns.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/forms.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/grid.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/index.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/loading.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/modal.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/navs.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/progress.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/rtl.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/scroller.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/tables.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/themes.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/tooltips.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/typography.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/src/we-love-classes.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/tables.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/themes.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/tooltips.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/typography.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/docs/we-love-classes.html (100%) rename {AHC_app/static => static}/css/pico-1.5.9/package-lock.json (100%) rename {AHC_app/static => static}/css/pico-1.5.9/package.json (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/_functions.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/_variables.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_accordion.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_card.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_dropdown.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_modal.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_nav.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/components/_progress.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_button.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_code.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_embedded.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_form-alt-input-types.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_form.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_miscs.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_table.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/content/_typography.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_container.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_document.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_grid.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_scroller.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_section.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/layout/_sectioning.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/pico.classless.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/pico.fluid.classless.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/pico.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/pico.slim.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/postcss.config.js (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/themes/default.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/themes/default/_colors.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/themes/default/_dark.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/themes/default/_light.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/themes/default/_styles.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/utilities/_accessibility.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/utilities/_loading.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/utilities/_reduce-motion.scss (100%) rename {AHC_app/static => static}/css/pico-1.5.9/scss/utilities/_tooltip.scss (100%) rename {AHC_app/static => static}/css/stable_grid.css (100%) rename {AHC_app/static => static}/css/timeline.css (100%) rename {AHC_app/static => static}/custom_pico.scss (100%) rename {AHC_app/static => static}/js/expanding_sections.js (100%) rename {AHC_app/static => static}/js/hiding_note_fields_in_form.js (100%) rename {AHC_app/static => static}/js/hiding_note_fields_in_measurement_form.js (100%) rename {AHC_app/static => static}/js/pin_animal.js (100%) rename {AHC_app/static => static}/js/timeline.js (100%) rename {AHC_app/static => static}/media/background/background-1169534_1920.png (100%) rename {AHC_app/static => static}/media/background/black.jpg (100%) rename {AHC_app/static => static}/media/background/default_title.jpg (100%) rename {AHC_app/static => static}/media/background/lizard2.jpg (100%) rename {AHC_app/static => static}/media/icons/chinchilla.png (100%) rename {AHC_app/static => static}/media/profile_pics/pet-care-icon.png (100%) rename {AHC_app/static => static}/media/profile_pics/pet-care.png (100%) rename {AHC_app/static => static}/media/profile_pics/signup.png (100%) rename {AHC_app/static => static}/media/profile_pics/signup2.png (100%) rename {AHC_app/static => static}/media/readme_examples/Animal profile.png (100%) rename {AHC_app/static => static}/media/readme_examples/Diet note details.png (100%) rename {AHC_app/static => static}/media/readme_examples/Full timeline of notes.png (100%) rename {AHC_app/static => static}/media/readme_examples/User registration.png (100%) rename {AHC_app/templates => templates}/partials/animal_card.html (100%) diff --git a/.gitattributes b/.gitattributes index 287d346..6915582 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,5 @@ * text=auto # Vendor static files — preserve LF line endings as published -AHC_app/static/**/* text eol=lf -AHC_app/static_collected/**/* text eol=lf +static/**/* text eol=lf +static_collected/**/* text eol=lf diff --git a/.gitignore b/.gitignore index 60345ef..afebe7a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,12 +50,12 @@ CLAUDE.md .idea/ # Project-specific -/AHC_app/static_collected/ -/AHC_app/static/media/profile_pics/animals/ -/AHC_app/static/media/attachments/ +/static_collected/ +/static/media/profile_pics/animals/ +/static/media/attachments/ # Kubernetes -/AHC_app/tars/ +/tars/ /kubernetes/**/*.tar secret.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 682aad7..4cfcfde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: 'AHC_app/static/|AHC_app/static_collected/' +exclude: 'static/|static_collected/' repos: - repo: local diff --git a/README.md b/README.md index 68d1c1e..8bc0102 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,12 @@
- - + + - - + +

Animal profile

Full timeline of notes

Animal profile

Full timeline of notes

Diet note details

User registration

Diet note details

User registration

@@ -50,7 +50,7 @@ - Docker & Docker Compose - PostgreSQL 15 (instance for volumes) - Apache CouchDB 3.3.3 (instance for volumes) -- [Packages](AHC_app/pyproject.toml) +- [Packages](pyproject.toml) - [pico-1.5.10](https://github.com/picocss/pico/archive/refs/tags/v1.5.10.zip) --- @@ -71,7 +71,6 @@ 4. Install uv and sync dependencies: ``` pip install uv - cd AHC_app uv sync ``` 5. Install pre-commit hooks: @@ -85,7 +84,6 @@ With `just` installed, steps 4–6 simplify to: ``` -cd AHC_app just install just precommit just docker-up @@ -137,7 +135,6 @@ just docker-up ### Test running: ```bash # pytest (recommended) -cd AHC_app uv run pytest -m integration # or with just diff --git a/pyproject.toml b/pyproject.toml index afd39e3..f2bdd19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,7 +82,7 @@ markers = [ python-version = "3.14" [tool.codespell] -skip = "uv.lock,./AHC_app/static,./AHC_app/static_collected,./static,./static_collected" +skip = "uv.lock,./static,./static_collected" [tool.bandit] exclude_dirs = [".venv"] diff --git a/src/ahc/settings.py b/src/ahc/settings.py index bf3b365..077c99b 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -10,7 +10,6 @@ https://docs.djangoproject.com/en/4.2/ref/settings/ """ -import os import sys from pathlib import Path @@ -98,7 +97,7 @@ def _skip_external_services() -> bool: { "BACKEND": "django.template.backends.django.DjangoTemplates", # 'DIRS': [], - "DIRS": [BASE_DIR / "AHC_app" / "templates"], + "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -194,7 +193,7 @@ def _skip_external_services() -> bool: STATIC_URL = "/static/" STATIC_ROOT = "static_collected" STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "AHC_app/static/"), + BASE_DIR / "static", ] STATICFILES_FINDERS = [ @@ -232,7 +231,7 @@ def _skip_external_services() -> bool: """ MEDIA_URL = "/media/" -MEDIA_ROOT = os.path.join(BASE_DIR, "AHC_app/static/media") +MEDIA_ROOT = BASE_DIR / "static" / "media" # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field diff --git a/AHC_app/static/AHC_app/base.css b/static/AHC_app/base.css similarity index 100% rename from AHC_app/static/AHC_app/base.css rename to static/AHC_app/base.css diff --git a/AHC_app/static/css/expanding_sections.css b/static/css/expanding_sections.css similarity index 100% rename from AHC_app/static/css/expanding_sections.css rename to static/css/expanding_sections.css diff --git a/AHC_app/static/css/hide_large_description.css b/static/css/hide_large_description.css similarity index 100% rename from AHC_app/static/css/hide_large_description.css rename to static/css/hide_large_description.css diff --git a/AHC_app/static/css/pico-1.5.9/.github/CONTRIBUTING.md b/static/css/pico-1.5.9/.github/CONTRIBUTING.md similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.github/CONTRIBUTING.md rename to static/css/pico-1.5.9/.github/CONTRIBUTING.md diff --git a/AHC_app/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md b/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md rename to static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/AHC_app/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml b/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml rename to static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml diff --git a/AHC_app/static/css/pico-1.5.9/.github/examples.jpg b/static/css/pico-1.5.9/.github/examples.jpg similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.github/examples.jpg rename to static/css/pico-1.5.9/.github/examples.jpg diff --git a/AHC_app/static/css/pico-1.5.9/.github/logo.svg b/static/css/pico-1.5.9/.github/logo.svg similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.github/logo.svg rename to static/css/pico-1.5.9/.github/logo.svg diff --git a/AHC_app/static/css/pico-1.5.9/.gitignore b/static/css/pico-1.5.9/.gitignore similarity index 100% rename from AHC_app/static/css/pico-1.5.9/.gitignore rename to static/css/pico-1.5.9/.gitignore diff --git a/AHC_app/static/css/pico-1.5.9/LICENSE.md b/static/css/pico-1.5.9/LICENSE.md similarity index 100% rename from AHC_app/static/css/pico-1.5.9/LICENSE.md rename to static/css/pico-1.5.9/LICENSE.md diff --git a/AHC_app/static/css/pico-1.5.9/README.md b/static/css/pico-1.5.9/README.md similarity index 100% rename from AHC_app/static/css/pico-1.5.9/README.md rename to static/css/pico-1.5.9/README.md diff --git a/AHC_app/static/css/pico-1.5.9/composer.json b/static/css/pico-1.5.9/composer.json similarity index 100% rename from AHC_app/static/css/pico-1.5.9/composer.json rename to static/css/pico-1.5.9/composer.json diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.classless.css b/static/css/pico-1.5.9/css/pico.classless.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.classless.css rename to static/css/pico-1.5.9/css/pico.classless.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.classless.css.map b/static/css/pico-1.5.9/css/pico.classless.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.classless.css.map rename to static/css/pico-1.5.9/css/pico.classless.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.classless.min.css b/static/css/pico-1.5.9/css/pico.classless.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.classless.min.css rename to static/css/pico-1.5.9/css/pico.classless.min.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.classless.min.css.map b/static/css/pico-1.5.9/css/pico.classless.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.classless.min.css.map rename to static/css/pico-1.5.9/css/pico.classless.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.css b/static/css/pico-1.5.9/css/pico.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.css rename to static/css/pico-1.5.9/css/pico.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.css.map b/static/css/pico-1.5.9/css/pico.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.css.map rename to static/css/pico-1.5.9/css/pico.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.css b/static/css/pico-1.5.9/css/pico.fluid.classless.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.css rename to static/css/pico-1.5.9/css/pico.fluid.classless.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.css.map b/static/css/pico-1.5.9/css/pico.fluid.classless.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.css.map rename to static/css/pico-1.5.9/css/pico.fluid.classless.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.min.css b/static/css/pico-1.5.9/css/pico.fluid.classless.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.min.css rename to static/css/pico-1.5.9/css/pico.fluid.classless.min.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.min.css.map b/static/css/pico-1.5.9/css/pico.fluid.classless.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.fluid.classless.min.css.map rename to static/css/pico-1.5.9/css/pico.fluid.classless.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.min.css b/static/css/pico-1.5.9/css/pico.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.min.css rename to static/css/pico-1.5.9/css/pico.min.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.min.css.map b/static/css/pico-1.5.9/css/pico.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.min.css.map rename to static/css/pico-1.5.9/css/pico.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.slim.css b/static/css/pico-1.5.9/css/pico.slim.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.slim.css rename to static/css/pico-1.5.9/css/pico.slim.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.slim.css.map b/static/css/pico-1.5.9/css/pico.slim.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.slim.css.map rename to static/css/pico-1.5.9/css/pico.slim.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.slim.min.css b/static/css/pico-1.5.9/css/pico.slim.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.slim.min.css rename to static/css/pico-1.5.9/css/pico.slim.min.css diff --git a/AHC_app/static/css/pico-1.5.9/css/pico.slim.min.css.map b/static/css/pico-1.5.9/css/pico.slim.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/pico.slim.min.css.map rename to static/css/pico-1.5.9/css/pico.slim.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/postcss.config.js b/static/css/pico-1.5.9/css/postcss.config.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/postcss.config.js rename to static/css/pico-1.5.9/css/postcss.config.js diff --git a/AHC_app/static/css/pico-1.5.9/css/themes/default.css b/static/css/pico-1.5.9/css/themes/default.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/themes/default.css rename to static/css/pico-1.5.9/css/themes/default.css diff --git a/AHC_app/static/css/pico-1.5.9/css/themes/default.css.map b/static/css/pico-1.5.9/css/themes/default.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/themes/default.css.map rename to static/css/pico-1.5.9/css/themes/default.css.map diff --git a/AHC_app/static/css/pico-1.5.9/css/themes/default.min.css b/static/css/pico-1.5.9/css/themes/default.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/themes/default.min.css rename to static/css/pico-1.5.9/css/themes/default.min.css diff --git a/AHC_app/static/css/pico-1.5.9/css/themes/default.min.css.map b/static/css/pico-1.5.9/css/themes/default.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/css/themes/default.min.css.map rename to static/css/pico-1.5.9/css/themes/default.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/docs/accordions.html b/static/css/pico-1.5.9/docs/accordions.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/accordions.html rename to static/css/pico-1.5.9/docs/accordions.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/buttons.html b/static/css/pico-1.5.9/docs/buttons.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/buttons.html rename to static/css/pico-1.5.9/docs/buttons.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/cards.html b/static/css/pico-1.5.9/docs/cards.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/cards.html rename to static/css/pico-1.5.9/docs/cards.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/classless.html b/static/css/pico-1.5.9/docs/classless.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/classless.html rename to static/css/pico-1.5.9/docs/classless.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/containers.html b/static/css/pico-1.5.9/docs/containers.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/containers.html rename to static/css/pico-1.5.9/docs/containers.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.css b/static/css/pico-1.5.9/docs/css/pico.docs.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.css rename to static/css/pico-1.5.9/docs/css/pico.docs.css diff --git a/AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.css.map b/static/css/pico-1.5.9/docs/css/pico.docs.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.css.map rename to static/css/pico-1.5.9/docs/css/pico.docs.css.map diff --git a/AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.min.css b/static/css/pico-1.5.9/docs/css/pico.docs.min.css similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.min.css rename to static/css/pico-1.5.9/docs/css/pico.docs.min.css diff --git a/AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.min.css.map b/static/css/pico-1.5.9/docs/css/pico.docs.min.css.map similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/css/pico.docs.min.css.map rename to static/css/pico-1.5.9/docs/css/pico.docs.min.css.map diff --git a/AHC_app/static/css/pico-1.5.9/docs/customization.html b/static/css/pico-1.5.9/docs/customization.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/customization.html rename to static/css/pico-1.5.9/docs/customization.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/dropdowns.html b/static/css/pico-1.5.9/docs/dropdowns.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/dropdowns.html rename to static/css/pico-1.5.9/docs/dropdowns.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/forms.html b/static/css/pico-1.5.9/docs/forms.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/forms.html rename to static/css/pico-1.5.9/docs/forms.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/grid.html b/static/css/pico-1.5.9/docs/grid.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/grid.html rename to static/css/pico-1.5.9/docs/grid.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/index.html b/static/css/pico-1.5.9/docs/index.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/index.html rename to static/css/pico-1.5.9/docs/index.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/commons.js b/static/css/pico-1.5.9/docs/js/commons.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/commons.js rename to static/css/pico-1.5.9/docs/js/commons.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/commons.min.js b/static/css/pico-1.5.9/docs/js/commons.min.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/commons.min.js rename to static/css/pico-1.5.9/docs/js/commons.min.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/customization.js b/static/css/pico-1.5.9/docs/js/customization.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/customization.js rename to static/css/pico-1.5.9/docs/js/customization.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/customization.min.js b/static/css/pico-1.5.9/docs/js/customization.min.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/customization.min.js rename to static/css/pico-1.5.9/docs/js/customization.min.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/grid.js b/static/css/pico-1.5.9/docs/js/grid.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/grid.js rename to static/css/pico-1.5.9/docs/js/grid.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/grid.min.js b/static/css/pico-1.5.9/docs/js/grid.min.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/grid.min.js rename to static/css/pico-1.5.9/docs/js/grid.min.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/modal.js b/static/css/pico-1.5.9/docs/js/modal.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/modal.js rename to static/css/pico-1.5.9/docs/js/modal.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/modal.min.js b/static/css/pico-1.5.9/docs/js/modal.min.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/modal.min.js rename to static/css/pico-1.5.9/docs/js/modal.min.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/src/color-picker.js b/static/css/pico-1.5.9/docs/js/src/color-picker.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/src/color-picker.js rename to static/css/pico-1.5.9/docs/js/src/color-picker.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/src/material-design-colors.js b/static/css/pico-1.5.9/docs/js/src/material-design-colors.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/src/material-design-colors.js rename to static/css/pico-1.5.9/docs/js/src/material-design-colors.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/src/theme-switcher.js b/static/css/pico-1.5.9/docs/js/src/theme-switcher.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/src/theme-switcher.js rename to static/css/pico-1.5.9/docs/js/src/theme-switcher.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/js/src/toggle-navigation.js b/static/css/pico-1.5.9/docs/js/src/toggle-navigation.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/js/src/toggle-navigation.js rename to static/css/pico-1.5.9/docs/js/src/toggle-navigation.js diff --git a/AHC_app/static/css/pico-1.5.9/docs/loading.html b/static/css/pico-1.5.9/docs/loading.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/loading.html rename to static/css/pico-1.5.9/docs/loading.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/modal.html b/static/css/pico-1.5.9/docs/modal.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/modal.html rename to static/css/pico-1.5.9/docs/modal.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/navs.html b/static/css/pico-1.5.9/docs/navs.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/navs.html rename to static/css/pico-1.5.9/docs/navs.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/progress.html b/static/css/pico-1.5.9/docs/progress.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/progress.html rename to static/css/pico-1.5.9/docs/progress.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/rtl.html b/static/css/pico-1.5.9/docs/rtl.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/rtl.html rename to static/css/pico-1.5.9/docs/rtl.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/scroller.html b/static/css/pico-1.5.9/docs/scroller.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scroller.html rename to static/css/pico-1.5.9/docs/scroller.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/components/_modal.scss b/static/css/pico-1.5.9/docs/scss/components/_modal.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/components/_modal.scss rename to static/css/pico-1.5.9/docs/scss/components/_modal.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/components/_nav.scss b/static/css/pico-1.5.9/docs/scss/components/_nav.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/components/_nav.scss rename to static/css/pico-1.5.9/docs/scss/components/_nav.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss b/static/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss rename to static/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/content/_code.scss b/static/css/pico-1.5.9/docs/scss/content/_code.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/content/_code.scss rename to static/css/pico-1.5.9/docs/scss/content/_code.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/content/_typography.scss b/static/css/pico-1.5.9/docs/scss/content/_typography.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/content/_typography.scss rename to static/css/pico-1.5.9/docs/scss/content/_typography.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/layout/_aside.scss b/static/css/pico-1.5.9/docs/scss/layout/_aside.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/layout/_aside.scss rename to static/css/pico-1.5.9/docs/scss/layout/_aside.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/layout/_document.scss b/static/css/pico-1.5.9/docs/scss/layout/_document.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/layout/_document.scss rename to static/css/pico-1.5.9/docs/scss/layout/_document.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/layout/_documentation.scss b/static/css/pico-1.5.9/docs/scss/layout/_documentation.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/layout/_documentation.scss rename to static/css/pico-1.5.9/docs/scss/layout/_documentation.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/layout/_main.scss b/static/css/pico-1.5.9/docs/scss/layout/_main.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/layout/_main.scss rename to static/css/pico-1.5.9/docs/scss/layout/_main.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/pico.docs.scss b/static/css/pico-1.5.9/docs/scss/pico.docs.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/pico.docs.scss rename to static/css/pico-1.5.9/docs/scss/pico.docs.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/themes/_docs.scss b/static/css/pico-1.5.9/docs/scss/themes/_docs.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/themes/_docs.scss rename to static/css/pico-1.5.9/docs/scss/themes/_docs.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss b/static/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss rename to static/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss b/static/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss rename to static/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_light.scss b/static/css/pico-1.5.9/docs/scss/themes/docs/_light.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/scss/themes/docs/_light.scss rename to static/css/pico-1.5.9/docs/scss/themes/docs/_light.scss diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/_footer.html b/static/css/pico-1.5.9/docs/src/_footer.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/_footer.html rename to static/css/pico-1.5.9/docs/src/_footer.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/_head.html b/static/css/pico-1.5.9/docs/src/_head.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/_head.html rename to static/css/pico-1.5.9/docs/src/_head.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/_nav.html b/static/css/pico-1.5.9/docs/src/_nav.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/_nav.html rename to static/css/pico-1.5.9/docs/src/_nav.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/_sidebar.html b/static/css/pico-1.5.9/docs/src/_sidebar.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/_sidebar.html rename to static/css/pico-1.5.9/docs/src/_sidebar.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/accordions.html b/static/css/pico-1.5.9/docs/src/accordions.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/accordions.html rename to static/css/pico-1.5.9/docs/src/accordions.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/buttons.html b/static/css/pico-1.5.9/docs/src/buttons.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/buttons.html rename to static/css/pico-1.5.9/docs/src/buttons.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/cards.html b/static/css/pico-1.5.9/docs/src/cards.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/cards.html rename to static/css/pico-1.5.9/docs/src/cards.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/classless.html b/static/css/pico-1.5.9/docs/src/classless.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/classless.html rename to static/css/pico-1.5.9/docs/src/classless.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/containers.html b/static/css/pico-1.5.9/docs/src/containers.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/containers.html rename to static/css/pico-1.5.9/docs/src/containers.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/customization.html b/static/css/pico-1.5.9/docs/src/customization.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/customization.html rename to static/css/pico-1.5.9/docs/src/customization.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/dropdowns.html b/static/css/pico-1.5.9/docs/src/dropdowns.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/dropdowns.html rename to static/css/pico-1.5.9/docs/src/dropdowns.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/forms.html b/static/css/pico-1.5.9/docs/src/forms.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/forms.html rename to static/css/pico-1.5.9/docs/src/forms.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/grid.html b/static/css/pico-1.5.9/docs/src/grid.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/grid.html rename to static/css/pico-1.5.9/docs/src/grid.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/index.html b/static/css/pico-1.5.9/docs/src/index.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/index.html rename to static/css/pico-1.5.9/docs/src/index.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/loading.html b/static/css/pico-1.5.9/docs/src/loading.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/loading.html rename to static/css/pico-1.5.9/docs/src/loading.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/modal.html b/static/css/pico-1.5.9/docs/src/modal.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/modal.html rename to static/css/pico-1.5.9/docs/src/modal.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/navs.html b/static/css/pico-1.5.9/docs/src/navs.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/navs.html rename to static/css/pico-1.5.9/docs/src/navs.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/progress.html b/static/css/pico-1.5.9/docs/src/progress.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/progress.html rename to static/css/pico-1.5.9/docs/src/progress.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/rtl.html b/static/css/pico-1.5.9/docs/src/rtl.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/rtl.html rename to static/css/pico-1.5.9/docs/src/rtl.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/scroller.html b/static/css/pico-1.5.9/docs/src/scroller.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/scroller.html rename to static/css/pico-1.5.9/docs/src/scroller.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/tables.html b/static/css/pico-1.5.9/docs/src/tables.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/tables.html rename to static/css/pico-1.5.9/docs/src/tables.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/themes.html b/static/css/pico-1.5.9/docs/src/themes.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/themes.html rename to static/css/pico-1.5.9/docs/src/themes.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/tooltips.html b/static/css/pico-1.5.9/docs/src/tooltips.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/tooltips.html rename to static/css/pico-1.5.9/docs/src/tooltips.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/typography.html b/static/css/pico-1.5.9/docs/src/typography.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/typography.html rename to static/css/pico-1.5.9/docs/src/typography.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/src/we-love-classes.html b/static/css/pico-1.5.9/docs/src/we-love-classes.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/src/we-love-classes.html rename to static/css/pico-1.5.9/docs/src/we-love-classes.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/tables.html b/static/css/pico-1.5.9/docs/tables.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/tables.html rename to static/css/pico-1.5.9/docs/tables.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/themes.html b/static/css/pico-1.5.9/docs/themes.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/themes.html rename to static/css/pico-1.5.9/docs/themes.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/tooltips.html b/static/css/pico-1.5.9/docs/tooltips.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/tooltips.html rename to static/css/pico-1.5.9/docs/tooltips.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/typography.html b/static/css/pico-1.5.9/docs/typography.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/typography.html rename to static/css/pico-1.5.9/docs/typography.html diff --git a/AHC_app/static/css/pico-1.5.9/docs/we-love-classes.html b/static/css/pico-1.5.9/docs/we-love-classes.html similarity index 100% rename from AHC_app/static/css/pico-1.5.9/docs/we-love-classes.html rename to static/css/pico-1.5.9/docs/we-love-classes.html diff --git a/AHC_app/static/css/pico-1.5.9/package-lock.json b/static/css/pico-1.5.9/package-lock.json similarity index 100% rename from AHC_app/static/css/pico-1.5.9/package-lock.json rename to static/css/pico-1.5.9/package-lock.json diff --git a/AHC_app/static/css/pico-1.5.9/package.json b/static/css/pico-1.5.9/package.json similarity index 100% rename from AHC_app/static/css/pico-1.5.9/package.json rename to static/css/pico-1.5.9/package.json diff --git a/AHC_app/static/css/pico-1.5.9/scss/_functions.scss b/static/css/pico-1.5.9/scss/_functions.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/_functions.scss rename to static/css/pico-1.5.9/scss/_functions.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/_variables.scss b/static/css/pico-1.5.9/scss/_variables.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/_variables.scss rename to static/css/pico-1.5.9/scss/_variables.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_accordion.scss b/static/css/pico-1.5.9/scss/components/_accordion.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_accordion.scss rename to static/css/pico-1.5.9/scss/components/_accordion.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_card.scss b/static/css/pico-1.5.9/scss/components/_card.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_card.scss rename to static/css/pico-1.5.9/scss/components/_card.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_dropdown.scss b/static/css/pico-1.5.9/scss/components/_dropdown.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_dropdown.scss rename to static/css/pico-1.5.9/scss/components/_dropdown.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_modal.scss b/static/css/pico-1.5.9/scss/components/_modal.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_modal.scss rename to static/css/pico-1.5.9/scss/components/_modal.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_nav.scss b/static/css/pico-1.5.9/scss/components/_nav.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_nav.scss rename to static/css/pico-1.5.9/scss/components/_nav.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/components/_progress.scss b/static/css/pico-1.5.9/scss/components/_progress.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/components/_progress.scss rename to static/css/pico-1.5.9/scss/components/_progress.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_button.scss b/static/css/pico-1.5.9/scss/content/_button.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_button.scss rename to static/css/pico-1.5.9/scss/content/_button.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_code.scss b/static/css/pico-1.5.9/scss/content/_code.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_code.scss rename to static/css/pico-1.5.9/scss/content/_code.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_embedded.scss b/static/css/pico-1.5.9/scss/content/_embedded.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_embedded.scss rename to static/css/pico-1.5.9/scss/content/_embedded.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_form-alt-input-types.scss b/static/css/pico-1.5.9/scss/content/_form-alt-input-types.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_form-alt-input-types.scss rename to static/css/pico-1.5.9/scss/content/_form-alt-input-types.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss b/static/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss rename to static/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_form.scss b/static/css/pico-1.5.9/scss/content/_form.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_form.scss rename to static/css/pico-1.5.9/scss/content/_form.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_miscs.scss b/static/css/pico-1.5.9/scss/content/_miscs.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_miscs.scss rename to static/css/pico-1.5.9/scss/content/_miscs.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_table.scss b/static/css/pico-1.5.9/scss/content/_table.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_table.scss rename to static/css/pico-1.5.9/scss/content/_table.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/content/_typography.scss b/static/css/pico-1.5.9/scss/content/_typography.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/content/_typography.scss rename to static/css/pico-1.5.9/scss/content/_typography.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_container.scss b/static/css/pico-1.5.9/scss/layout/_container.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_container.scss rename to static/css/pico-1.5.9/scss/layout/_container.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_document.scss b/static/css/pico-1.5.9/scss/layout/_document.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_document.scss rename to static/css/pico-1.5.9/scss/layout/_document.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_grid.scss b/static/css/pico-1.5.9/scss/layout/_grid.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_grid.scss rename to static/css/pico-1.5.9/scss/layout/_grid.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_scroller.scss b/static/css/pico-1.5.9/scss/layout/_scroller.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_scroller.scss rename to static/css/pico-1.5.9/scss/layout/_scroller.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_section.scss b/static/css/pico-1.5.9/scss/layout/_section.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_section.scss rename to static/css/pico-1.5.9/scss/layout/_section.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/layout/_sectioning.scss b/static/css/pico-1.5.9/scss/layout/_sectioning.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/layout/_sectioning.scss rename to static/css/pico-1.5.9/scss/layout/_sectioning.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/pico.classless.scss b/static/css/pico-1.5.9/scss/pico.classless.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/pico.classless.scss rename to static/css/pico-1.5.9/scss/pico.classless.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/pico.fluid.classless.scss b/static/css/pico-1.5.9/scss/pico.fluid.classless.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/pico.fluid.classless.scss rename to static/css/pico-1.5.9/scss/pico.fluid.classless.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/pico.scss b/static/css/pico-1.5.9/scss/pico.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/pico.scss rename to static/css/pico-1.5.9/scss/pico.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/pico.slim.scss b/static/css/pico-1.5.9/scss/pico.slim.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/pico.slim.scss rename to static/css/pico-1.5.9/scss/pico.slim.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/postcss.config.js b/static/css/pico-1.5.9/scss/postcss.config.js similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/postcss.config.js rename to static/css/pico-1.5.9/scss/postcss.config.js diff --git a/AHC_app/static/css/pico-1.5.9/scss/themes/default.scss b/static/css/pico-1.5.9/scss/themes/default.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/themes/default.scss rename to static/css/pico-1.5.9/scss/themes/default.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/themes/default/_colors.scss b/static/css/pico-1.5.9/scss/themes/default/_colors.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/themes/default/_colors.scss rename to static/css/pico-1.5.9/scss/themes/default/_colors.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/themes/default/_dark.scss b/static/css/pico-1.5.9/scss/themes/default/_dark.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/themes/default/_dark.scss rename to static/css/pico-1.5.9/scss/themes/default/_dark.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/themes/default/_light.scss b/static/css/pico-1.5.9/scss/themes/default/_light.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/themes/default/_light.scss rename to static/css/pico-1.5.9/scss/themes/default/_light.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/themes/default/_styles.scss b/static/css/pico-1.5.9/scss/themes/default/_styles.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/themes/default/_styles.scss rename to static/css/pico-1.5.9/scss/themes/default/_styles.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/utilities/_accessibility.scss b/static/css/pico-1.5.9/scss/utilities/_accessibility.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/utilities/_accessibility.scss rename to static/css/pico-1.5.9/scss/utilities/_accessibility.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/utilities/_loading.scss b/static/css/pico-1.5.9/scss/utilities/_loading.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/utilities/_loading.scss rename to static/css/pico-1.5.9/scss/utilities/_loading.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/utilities/_reduce-motion.scss b/static/css/pico-1.5.9/scss/utilities/_reduce-motion.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/utilities/_reduce-motion.scss rename to static/css/pico-1.5.9/scss/utilities/_reduce-motion.scss diff --git a/AHC_app/static/css/pico-1.5.9/scss/utilities/_tooltip.scss b/static/css/pico-1.5.9/scss/utilities/_tooltip.scss similarity index 100% rename from AHC_app/static/css/pico-1.5.9/scss/utilities/_tooltip.scss rename to static/css/pico-1.5.9/scss/utilities/_tooltip.scss diff --git a/AHC_app/static/css/stable_grid.css b/static/css/stable_grid.css similarity index 100% rename from AHC_app/static/css/stable_grid.css rename to static/css/stable_grid.css diff --git a/AHC_app/static/css/timeline.css b/static/css/timeline.css similarity index 100% rename from AHC_app/static/css/timeline.css rename to static/css/timeline.css diff --git a/AHC_app/static/custom_pico.scss b/static/custom_pico.scss similarity index 100% rename from AHC_app/static/custom_pico.scss rename to static/custom_pico.scss diff --git a/AHC_app/static/js/expanding_sections.js b/static/js/expanding_sections.js similarity index 100% rename from AHC_app/static/js/expanding_sections.js rename to static/js/expanding_sections.js diff --git a/AHC_app/static/js/hiding_note_fields_in_form.js b/static/js/hiding_note_fields_in_form.js similarity index 100% rename from AHC_app/static/js/hiding_note_fields_in_form.js rename to static/js/hiding_note_fields_in_form.js diff --git a/AHC_app/static/js/hiding_note_fields_in_measurement_form.js b/static/js/hiding_note_fields_in_measurement_form.js similarity index 100% rename from AHC_app/static/js/hiding_note_fields_in_measurement_form.js rename to static/js/hiding_note_fields_in_measurement_form.js diff --git a/AHC_app/static/js/pin_animal.js b/static/js/pin_animal.js similarity index 100% rename from AHC_app/static/js/pin_animal.js rename to static/js/pin_animal.js diff --git a/AHC_app/static/js/timeline.js b/static/js/timeline.js similarity index 100% rename from AHC_app/static/js/timeline.js rename to static/js/timeline.js diff --git a/AHC_app/static/media/background/background-1169534_1920.png b/static/media/background/background-1169534_1920.png similarity index 100% rename from AHC_app/static/media/background/background-1169534_1920.png rename to static/media/background/background-1169534_1920.png diff --git a/AHC_app/static/media/background/black.jpg b/static/media/background/black.jpg similarity index 100% rename from AHC_app/static/media/background/black.jpg rename to static/media/background/black.jpg diff --git a/AHC_app/static/media/background/default_title.jpg b/static/media/background/default_title.jpg similarity index 100% rename from AHC_app/static/media/background/default_title.jpg rename to static/media/background/default_title.jpg diff --git a/AHC_app/static/media/background/lizard2.jpg b/static/media/background/lizard2.jpg similarity index 100% rename from AHC_app/static/media/background/lizard2.jpg rename to static/media/background/lizard2.jpg diff --git a/AHC_app/static/media/icons/chinchilla.png b/static/media/icons/chinchilla.png similarity index 100% rename from AHC_app/static/media/icons/chinchilla.png rename to static/media/icons/chinchilla.png diff --git a/AHC_app/static/media/profile_pics/pet-care-icon.png b/static/media/profile_pics/pet-care-icon.png similarity index 100% rename from AHC_app/static/media/profile_pics/pet-care-icon.png rename to static/media/profile_pics/pet-care-icon.png diff --git a/AHC_app/static/media/profile_pics/pet-care.png b/static/media/profile_pics/pet-care.png similarity index 100% rename from AHC_app/static/media/profile_pics/pet-care.png rename to static/media/profile_pics/pet-care.png diff --git a/AHC_app/static/media/profile_pics/signup.png b/static/media/profile_pics/signup.png similarity index 100% rename from AHC_app/static/media/profile_pics/signup.png rename to static/media/profile_pics/signup.png diff --git a/AHC_app/static/media/profile_pics/signup2.png b/static/media/profile_pics/signup2.png similarity index 100% rename from AHC_app/static/media/profile_pics/signup2.png rename to static/media/profile_pics/signup2.png diff --git a/AHC_app/static/media/readme_examples/Animal profile.png b/static/media/readme_examples/Animal profile.png similarity index 100% rename from AHC_app/static/media/readme_examples/Animal profile.png rename to static/media/readme_examples/Animal profile.png diff --git a/AHC_app/static/media/readme_examples/Diet note details.png b/static/media/readme_examples/Diet note details.png similarity index 100% rename from AHC_app/static/media/readme_examples/Diet note details.png rename to static/media/readme_examples/Diet note details.png diff --git a/AHC_app/static/media/readme_examples/Full timeline of notes.png b/static/media/readme_examples/Full timeline of notes.png similarity index 100% rename from AHC_app/static/media/readme_examples/Full timeline of notes.png rename to static/media/readme_examples/Full timeline of notes.png diff --git a/AHC_app/static/media/readme_examples/User registration.png b/static/media/readme_examples/User registration.png similarity index 100% rename from AHC_app/static/media/readme_examples/User registration.png rename to static/media/readme_examples/User registration.png diff --git a/AHC_app/templates/partials/animal_card.html b/templates/partials/animal_card.html similarity index 100% rename from AHC_app/templates/partials/animal_card.html rename to templates/partials/animal_card.html From d93179208a5009db6a98e5eabefc497a9df59757 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 12:02:11 +0200 Subject: [PATCH 20/31] feat(security): remove hardcoded SECRET_KEY and DEBUG from settings --- .env.template | 5 +++++ .github/workflows/django.yml | 1 + src/ahc/settings.py | 16 ++++++++-------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.env.template b/.env.template index 6fe5166..fcf11b7 100644 --- a/.env.template +++ b/.env.template @@ -1,3 +1,8 @@ +# Django core +SECRET_KEY= +DEBUG=True +ALLOWED_HOSTS=localhost,127.0.0.1 + # PostgresDB for docker-compose.yml - dev POSTGRES_DB= POSTGRES_USER= diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 65986f7..d210048 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -34,6 +34,7 @@ jobs: EMAIL_HOST_PASSWORD: ${{ secrets.EMAIL_HOST_PASSWORD }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} PYTHONUNBUFFERED: "1" + SECRET_KEY: ${{ secrets.SECRET_KEY }} steps: - name: Checkout diff --git a/src/ahc/settings.py b/src/ahc/settings.py index 077c99b..ee12b9c 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -14,7 +14,7 @@ from pathlib import Path import pycouchdb -from decouple import config +from decouple import Csv, config _OFFLINE_COMMANDS = { "check", @@ -50,13 +50,13 @@ def _skip_external_services() -> bool: # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-x#q0@altnjw2yrhh)edi)co2)n3p8q&0qmz7m8oxu-*jhd8d9-" # nosec B105 - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] +if _is_test_run(): + SECRET_KEY = config("SECRET_KEY", default="django-insecure-test-only-not-for-production") # nosec B105 + DEBUG = True +else: + SECRET_KEY = config("SECRET_KEY") + DEBUG = config("DEBUG", default=False, cast=bool) +ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost,127.0.0.1", cast=Csv()) # Application definition From d5b212b5d0590118bf9e0920a6cc62b300b52997 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 12:04:45 +0200 Subject: [PATCH 21/31] fix(cd): rename hook alias --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4cfcfde..c3e1185 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.14 hooks: - - id: ruff + - id: ruff-check args: [--fix] - id: ruff-format From 0332817560104d83252294a81d53116c1f4560f9 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 12:22:08 +0200 Subject: [PATCH 22/31] chore(toolchain): rename ruff hook, add just help --- justfile | 4 ++++ src/ahc/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index ccaa8dc..7cebea3 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,9 @@ set shell := ["pwsh", "-NoLogo", "-Command"] +# List all available recipes +help: + @just --list + # Install all dependencies including dev group install: uv sync diff --git a/src/ahc/settings.py b/src/ahc/settings.py index ee12b9c..c5664ea 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -51,7 +51,7 @@ def _skip_external_services() -> bool: # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ if _is_test_run(): - SECRET_KEY = config("SECRET_KEY", default="django-insecure-test-only-not-for-production") # nosec B105 + SECRET_KEY = config("SECRET_KEY", default="django-insecure-test-only-not-for-production") DEBUG = True else: SECRET_KEY = config("SECRET_KEY") From 4cfa477e8ed85388fda9fe253903c00850eafa2f Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 12:25:12 +0200 Subject: [PATCH 23/31] refactor(homepage): drop orphaned CronJob model --- src/ahc/apps/homepage/admin.py | 24 +------------------ .../migrations/0004_delete_cronjob.py | 11 +++++++++ src/ahc/apps/homepage/models.py | 12 ---------- 3 files changed, 12 insertions(+), 35 deletions(-) create mode 100644 src/ahc/apps/homepage/migrations/0004_delete_cronjob.py diff --git a/src/ahc/apps/homepage/admin.py b/src/ahc/apps/homepage/admin.py index 1307303..73a2081 100644 --- a/src/ahc/apps/homepage/admin.py +++ b/src/ahc/apps/homepage/admin.py @@ -1,27 +1,5 @@ from django.contrib import admin -from ahc.apps.homepage.models import AnimalTitle, CronJob +from ahc.apps.homepage.models import AnimalTitle admin.site.register(AnimalTitle) - - -class CronJobAdmin(admin.ModelAdmin): - list_display = ("name", "schedule", "last_execution", "next_execution") - readonly_fields = ( - "name", - "command", - "schedule", - "last_execution", - "next_execution", - ) - - def changelist_view(self, request, extra_context=None): - extra_context = extra_context or {} - extra_context["cronjobs"] = CronJob.objects.all() - return super().changelist_view(request, extra_context=extra_context) - - def has_add_permission(self, request): - return False - - -admin.site.register(CronJob, CronJobAdmin) diff --git a/src/ahc/apps/homepage/migrations/0004_delete_cronjob.py b/src/ahc/apps/homepage/migrations/0004_delete_cronjob.py new file mode 100644 index 0000000..200dd96 --- /dev/null +++ b/src/ahc/apps/homepage/migrations/0004_delete_cronjob.py @@ -0,0 +1,11 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("homepage", "0003_cronjob"), + ] + + operations = [ + migrations.DeleteModel(name="CronJob"), + ] diff --git a/src/ahc/apps/homepage/models.py b/src/ahc/apps/homepage/models.py index 43cc191..213acd7 100644 --- a/src/ahc/apps/homepage/models.py +++ b/src/ahc/apps/homepage/models.py @@ -42,15 +42,3 @@ def __str__(self): def get_absolute_url(self): return reverse("article-detail", kwargs={"pk": self.pk}) - - -# TODO: implement to allow set cronjobs from admin panel, must run shell command -class CronJob(models.Model): - name = models.CharField(max_length=255) - command = models.CharField(max_length=255) - schedule = models.CharField(max_length=255) - last_execution = models.DateTimeField(null=True, blank=True) - next_execution = models.DateTimeField(null=True, blank=True) - - def __str__(self): - return self.name From 715031b126225c3991393977ca86430bc2f42a6b Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 12:42:15 +0200 Subject: [PATCH 24/31] test(apps): add tests for animals, users, medical_notes; fix signal bug and migration --- .github/workflows/django.yml | 1 - conftest.py | 34 +++++++++++ src/ahc/apps/animals/tests.py | 58 ++++++++++++++++++- ...notification_last_modification_and_more.py | 6 +- .../signals/type_measurement_notes.py | 22 +++---- src/ahc/apps/medical_notes/tests.py | 55 +++++++++++++++++- src/ahc/apps/users/tests.py | 28 ++++++++- 7 files changed, 184 insertions(+), 20 deletions(-) diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index d210048..65986f7 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -34,7 +34,6 @@ jobs: EMAIL_HOST_PASSWORD: ${{ secrets.EMAIL_HOST_PASSWORD }} DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} PYTHONUNBUFFERED: "1" - SECRET_KEY: ${{ secrets.SECRET_KEY }} steps: - name: Checkout diff --git a/conftest.py b/conftest.py index e69de29..72922e2 100644 --- a/conftest.py +++ b/conftest.py @@ -0,0 +1,34 @@ +from unittest.mock import MagicMock, patch + +import pytest +from django.contrib.auth.models import User + + +@pytest.fixture +def user_profile(db): + """Create a User + Profile pair, mocking image processing in Profile.save().""" + from ahc.apps.users.models import Profile + + user = User.objects.create_user(username="testuser", password="testpass123") + with patch("ahc.apps.users.models.Image.open") as mock_open: + mock_img = MagicMock() + mock_img.height = 100 + mock_img.width = 100 + mock_open.return_value = mock_img + profile = Profile.objects.create(user=user) + return user, profile + + +@pytest.fixture +def second_user_profile(db): + """A second User + Profile for multi-user permission tests.""" + from ahc.apps.users.models import Profile + + user = User.objects.create_user(username="otheruser", password="testpass123") + with patch("ahc.apps.users.models.Image.open") as mock_open: + mock_img = MagicMock() + mock_img.height = 100 + mock_img.width = 100 + mock_open.return_value = mock_img + profile = Profile.objects.create(user=user) + return user, profile diff --git a/src/ahc/apps/animals/tests.py b/src/ahc/apps/animals/tests.py index a39b155..933a9c0 100644 --- a/src/ahc/apps/animals/tests.py +++ b/src/ahc/apps/animals/tests.py @@ -1 +1,57 @@ -# Create your tests here. +import pytest + +from ahc.apps.animals.models import Animal +from ahc.apps.animals.signals import update_allowed_users + + +@pytest.fixture +def animal(db, user_profile): + _, profile = user_profile + return Animal.objects.create(full_name="Whiskers", owner=profile) + + +@pytest.mark.integration +@pytest.mark.django_db +class TestAnimalModel: + def test_animal_is_created_with_uuid_pk(self, animal): + assert animal.id is not None + assert animal.full_name == "Whiskers" + + def test_owner_is_assigned(self, animal, user_profile): + _, profile = user_profile + assert animal.owner == profile + + def test_no_keepers_by_default(self, animal): + assert animal.allowed_users.count() == 0 + + def test_second_user_can_be_added_as_keeper(self, animal, second_user_profile): + _, other_profile = second_user_profile + animal.allowed_users.add(other_profile) + assert animal.allowed_users.filter(pk=other_profile.pk).exists() + + +@pytest.mark.integration +@pytest.mark.django_db +class TestUpdateAllowedUsersSignalHandler: + """update_allowed_users: owner must not appear in allowed_users.""" + + def test_owner_removed_when_present_in_allowed_users(self, animal, user_profile): + _, profile = user_profile + animal.allowed_users.add(profile) + assert animal.allowed_users.filter(pk=profile.pk).exists() + + update_allowed_users(sender=Animal, instance=animal) + + assert not animal.allowed_users.filter(pk=profile.pk).exists() + + def test_non_owner_keeper_not_affected(self, animal, second_user_profile): + _, other_profile = second_user_profile + animal.allowed_users.add(other_profile) + + update_allowed_users(sender=Animal, instance=animal) + + assert animal.allowed_users.filter(pk=other_profile.pk).exists() + + def test_no_op_when_allowed_users_is_empty(self, animal): + update_allowed_users(sender=Animal, instance=animal) + assert animal.allowed_users.count() == 0 diff --git a/src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py b/src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py index 29c4e8d..237bd01 100644 --- a/src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py +++ b/src/ahc/apps/medical_notes/migrations/0009_discordnotification_last_modification_and_more.py @@ -14,16 +14,16 @@ class Migration(migrations.Migration): migrations.AddField( model_name="discordnotification", name="last_modification", - field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514)), + field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514, tzinfo=datetime.UTC)), ), migrations.AddField( model_name="emailnotification", name="last_modification", - field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514)), + field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514, tzinfo=datetime.UTC)), ), migrations.AddField( model_name="smsnotification", name="last_modification", - field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514)), + field=models.DateTimeField(default=datetime.datetime(2024, 2, 2, 13, 46, 49, 356514, tzinfo=datetime.UTC)), ), ] diff --git a/src/ahc/apps/medical_notes/signals/type_measurement_notes.py b/src/ahc/apps/medical_notes/signals/type_measurement_notes.py index 38e3507..d3cddd6 100644 --- a/src/ahc/apps/medical_notes/signals/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/signals/type_measurement_notes.py @@ -10,20 +10,16 @@ @receiver(pre_save, sender=BiometricRecord) def validate_one_to_one_fields(sender, instance, **kwargs): - if ( - len( - { - instance - for instance in ( - instance.weight_biometric_record, - instance.height_biometric_record, - instance.custom_biometric_record, - ) - if instance is not None - } + assigned = sum( + 1 + for field in ( + instance.weight_biometric_record, + instance.height_biometric_record, + instance.custom_biometric_record, ) - > 1 - ): + if field is not None + ) + if assigned > 1: raise ValidationError("BiometricRecord can only have one of OneToOneFields assigned.") diff --git a/src/ahc/apps/medical_notes/tests.py b/src/ahc/apps/medical_notes/tests.py index a39b155..c18ca7e 100644 --- a/src/ahc/apps/medical_notes/tests.py +++ b/src/ahc/apps/medical_notes/tests.py @@ -1 +1,54 @@ -# Create your tests here. +from unittest.mock import MagicMock + +import pytest +from django.core.exceptions import ValidationError + +from ahc.apps.medical_notes.models.type_measurement_notes import ( + BiometricHeightRecords, + BiometricWeightRecords, +) +from ahc.apps.medical_notes.signals.type_measurement_notes import validate_one_to_one_fields + + +def _make_instance(weight=None, height=None, custom=None): + instance = MagicMock() + instance.weight_biometric_record = weight + instance.height_biometric_record = height + instance.custom_biometric_record = custom + return instance + + +@pytest.mark.unit +class TestBiometricRecordValidation: + """validate_one_to_one_fields: at most one measurement type per BiometricRecord.""" + + def test_two_types_raises_validation_error(self): + instance = _make_instance( + weight=BiometricWeightRecords(weight=5), + height=BiometricHeightRecords(height=30), + ) + with pytest.raises(ValidationError): + validate_one_to_one_fields(sender=None, instance=instance) + + def test_all_three_types_raises_validation_error(self): + from ahc.apps.medical_notes.models.type_measurement_notes import BiometricCustomRecords + + instance = _make_instance( + weight=BiometricWeightRecords(weight=5), + height=BiometricHeightRecords(height=30), + custom=BiometricCustomRecords(record_name="x", record_value="1", record_unit="cm"), + ) + with pytest.raises(ValidationError): + validate_one_to_one_fields(sender=None, instance=instance) + + def test_weight_only_is_valid(self): + instance = _make_instance(weight=BiometricWeightRecords(weight=5)) + validate_one_to_one_fields(sender=None, instance=instance) + + def test_height_only_is_valid(self): + instance = _make_instance(height=BiometricHeightRecords(height=30)) + validate_one_to_one_fields(sender=None, instance=instance) + + def test_all_none_is_valid(self): + instance = _make_instance() + validate_one_to_one_fields(sender=None, instance=instance) diff --git a/src/ahc/apps/users/tests.py b/src/ahc/apps/users/tests.py index a39b155..7984f3e 100644 --- a/src/ahc/apps/users/tests.py +++ b/src/ahc/apps/users/tests.py @@ -1 +1,27 @@ -# Create your tests here. +import pytest + +from ahc.apps.users.models import Profile + + +@pytest.mark.integration +@pytest.mark.django_db +class TestProfileModel: + def test_str_returns_username(self, user_profile): + _, profile = user_profile + assert str(profile) == "testuser" + + def test_profile_linked_to_user(self, user_profile): + user, profile = user_profile + assert profile.user == user + + def test_allow_recent_animals_list_defaults_to_true(self, user_profile): + _, profile = user_profile + assert profile.allow_recennt_animals_list is True + + def test_pinned_animals_empty_by_default(self, user_profile): + _, profile = user_profile + assert profile.pinned_animals.count() == 0 + + def test_profile_is_unique_per_user(self, user_profile): + user, _ = user_profile + assert Profile.objects.filter(user=user).count() == 1 From 8ce2841b4041ef2d0633b8006c514f26f562c2e4 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 17:12:48 +0200 Subject: [PATCH 25/31] fix(animals): use MEDIA_ROOT in signals, guard against missing media dir --- src/ahc/apps/animals/signals.py | 41 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ahc/apps/animals/signals.py b/src/ahc/apps/animals/signals.py index daddc0f..6ccd793 100644 --- a/src/ahc/apps/animals/signals.py +++ b/src/ahc/apps/animals/signals.py @@ -1,46 +1,45 @@ import os +from pathlib import Path +from django.conf import settings from django.db.models.signals import post_delete, post_save, pre_delete from django.dispatch import receiver from ahc.apps.animals.models import Animal from ahc.apps.users.models import Profile +_ANIMALS_MEDIA_DIR = Path(settings.MEDIA_ROOT) / "profile_pics" / "animals" + @receiver(post_save, sender=Animal) def remove_old_pictures_after_change(sender, instance, **kwargs): - animals_with_profile_images = Animal.objects.exclude(profile_image="").values_list("profile_image", flat=True) - unused_images = os.listdir("static/media/profile_pics/animals/") - - animals_with_profile_images = [str(picture).split("/")[-1] for picture in animals_with_profile_images] - - for image_name in unused_images: + if not _ANIMALS_MEDIA_DIR.is_dir(): + return + animals_with_profile_images = { + str(p).split("/")[-1] for p in Animal.objects.exclude(profile_image="").values_list("profile_image", flat=True) + } + for image_name in os.listdir(_ANIMALS_MEDIA_DIR): if image_name not in animals_with_profile_images: - image_path = os.path.join("static/media/profile_pics/animals/", image_name) - os.remove(image_path) + (_ANIMALS_MEDIA_DIR / image_name).unlink(missing_ok=True) -# is necessary to write tests for this one! @receiver(pre_delete, sender=Animal) def remove_old_pictures_after_animal_delete(sender, instance, **kwargs): if instance.profile_image: - image_name = instance.profile_image.name - image_path = os.path.join("static/media/profile_pics/animals/", image_name) - if os.path.exists(image_path): - os.remove(image_path) + image_path = _ANIMALS_MEDIA_DIR / Path(instance.profile_image.name).name + image_path.unlink(missing_ok=True) @receiver(post_delete, sender=Profile) def remove_old_pictures_after_user_delete(sender, instance, **kwargs): - animals_with_profile_images = Animal.objects.exclude(profile_image="").values_list("profile_image", flat=True) - unused_images = os.listdir("static/media/profile_pics/animals/") - - animals_with_profile_images = [str(picture).split("/")[-1] for picture in animals_with_profile_images] - - for image_name in unused_images: + if not _ANIMALS_MEDIA_DIR.is_dir(): + return + animals_with_profile_images = { + str(p).split("/")[-1] for p in Animal.objects.exclude(profile_image="").values_list("profile_image", flat=True) + } + for image_name in os.listdir(_ANIMALS_MEDIA_DIR): if image_name not in animals_with_profile_images: - image_path = os.path.join("static/media/profile_pics/animals/", image_name) - os.remove(image_path) + (_ANIMALS_MEDIA_DIR / image_name).unlink(missing_ok=True) @receiver(post_save, sender=Animal) From 0150da75b00dda819809071dc76bb310ef97f198 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 17:17:46 +0200 Subject: [PATCH 26/31] cd: remove format form precommit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3e1185..8afd159 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,8 +21,8 @@ repos: rev: v0.15.14 hooks: - id: ruff-check - args: [--fix] - id: ruff-format + args: [--check] - repo: https://github.com/codespell-project/codespell rev: v2.4.2 From 7de838cbb169ec538f94170aa24932a5f1aa151f Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 17:19:41 +0200 Subject: [PATCH 27/31] fix(settings): use BASE_DIR / static_collected for STATIC_ROOT --- src/ahc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ahc/settings.py b/src/ahc/settings.py index c5664ea..2db41ef 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -191,7 +191,7 @@ def _skip_external_services() -> bool: # https://docs.djangoproject.com/en/4.2/howto/static-files/ STATIC_URL = "/static/" -STATIC_ROOT = "static_collected" +STATIC_ROOT = BASE_DIR / "static_collected" STATICFILES_DIRS = [ BASE_DIR / "static", ] From e83f235ee5e0069b22284bb003f4af60ae9b3933 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 17:32:52 +0200 Subject: [PATCH 28/31] docs: add TODO.md with remaining work for next sessions --- TODO.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..86ec135 --- /dev/null +++ b/TODO.md @@ -0,0 +1,65 @@ +# Remaining work — next sessions + +## 1. Dead signals (HIGH — silent production bug) + +All `apps.py` have `ready(): pass` — signal handlers are never registered with +Django's dispatch system. As a result, the following logic **does not run in +production**: + +| Handler | File | Effect when dead | +|---------|------|-----------------| +| `remove_old_pictures_after_change` | `animals/signals.py` | Orphaned animal images accumulate | +| `remove_old_pictures_after_animal_delete` | `animals/signals.py` | Profile image not removed on delete | +| `remove_old_pictures_after_user_delete` | `animals/signals.py` | Same, on user delete | +| `update_allowed_users` | `animals/signals.py` | Owner can remain in allowed_users | +| `validate_one_to_one_fields` | `medical_notes/signals/` | Invalid BiometricRecords can be saved | +| `clean_orphaned_metric_records` | `medical_notes/signals/` | Orphaned BiometricRecord rows accumulate | +| `clean_orphaned_diet_records` | `medical_notes/signals/` | Orphaned FeedingNote rows accumulate | +| `create_profile` / `save_profile` | `users/signals.py` | Profile not created on User.create | +| `create_basic_privilege` / `create_background` | `users/signals.py` | Privilege/Background not set on Profile.save | + +**Decision required:** register each handler in `ready()` OR consciously delete it. +**Warning:** do NOT move signal logic into services "in passing" without a deliberate +decision — it would change current production behaviour (currently: nothing runs). + +## 2. FeedingNote missing `author` field (BUG) + +`medical_notes/signals/type_feeding_notes.py` references `instance.related_note.author` +but `FeedingNote` has no `author` field — it only has `related_note` (FK to `MedicalRecord`), +and `MedicalRecord` has `author`. The signal traversal chain is: + +``` +FeedingNote → related_note (MedicalRecord) → author (Profile) +``` + +Check whether this traversal is actually correct or whether it is a latent +`AttributeError` waiting to surface when the signal is finally connected. + +## 3. Form validation with DB queries in `utils_owner/forms.py` + +`ChangeOwnerForm.clean_new_owner()` and `ManageKeepersForm.clean_input_user()` +issue `Profile.objects.filter(...)` / `Profile.objects.get(...)` queries inside +`clean_*` methods. This is acceptable for now but can be extracted to selectors +(query layer) in a later refactor. **Do not move in the same PR as signal work** — +risk of changing form validation error messages. + +## 4. Test coverage gaps + +- Views (`animals/views.py`, `medical_notes/views/`) have zero test coverage. +- `users/signals.py` handlers untested (currently dead anyway, see §1). +- Fat views refactor (in progress) is the natural trigger for adding view tests. + +## 5. Fat views — in progress + +`animals/views.py` and `medical_notes/views/` contain business logic. +Extraction to a service layer is already started. Keep signal decisions (§1) +in sync with this work to avoid duplicating logic. + +## Already fixed in `refactor/django-5x-prep` + +- `STATIC_ROOT` → `BASE_DIR / "static_collected"` ✅ +- `SECRET_KEY` / `DEBUG` from env via python-decouple ✅ +- `homepage.CronJob` orphaned model dropped ✅ +- `validate_one_to_one_fields` set-comprehension bug fixed ✅ +- `animals/signals.py` hardcoded paths → `settings.MEDIA_ROOT` ✅ +- Migration 0009 naive datetime default → UTC-aware ✅ From b8bd3db6f818582e447846dcef01106f62c251fa Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sat, 30 May 2026 23:46:43 +0200 Subject: [PATCH 29/31] refactor: domain logic separation --- TODO.md | 65 +++ conftest.py | 4 +- .../mixins/animal_owner_permissions.py | 7 +- src/ahc/apps/animals/selectors.py | 34 ++ src/ahc/apps/animals/services.py | 68 ++++ src/ahc/apps/animals/tests.py | 261 ++++++++++++ src/ahc/apps/animals/utils_owner/forms.py | 1 - src/ahc/apps/animals/utils_owner/views.py | 54 +-- src/ahc/apps/animals/views.py | 71 ++-- src/ahc/apps/medical_notes/selectors.py | 80 ++++ .../apps/medical_notes/services/__init__.py | 0 .../medical_notes/services/attachments.py | 83 ++++ .../apps/medical_notes/services/biometrics.py | 50 +++ src/ahc/apps/medical_notes/services/couch.py | 43 ++ src/ahc/apps/medical_notes/services/notes.py | 41 ++ .../medical_notes/services/notifications.py | 36 ++ src/ahc/apps/medical_notes/tests.py | 377 +++++++++++++++++- .../views/mixins/user_animal_permisions.py | 56 +++ .../medical_notes/views/type_basic_note.py | 308 ++++---------- .../medical_notes/views/type_feeding_notes.py | 135 ++----- .../views/type_measurement_notes.py | 55 +-- 21 files changed, 1366 insertions(+), 463 deletions(-) create mode 100644 src/ahc/apps/animals/selectors.py create mode 100644 src/ahc/apps/animals/services.py create mode 100644 src/ahc/apps/medical_notes/selectors.py create mode 100644 src/ahc/apps/medical_notes/services/__init__.py create mode 100644 src/ahc/apps/medical_notes/services/attachments.py create mode 100644 src/ahc/apps/medical_notes/services/biometrics.py create mode 100644 src/ahc/apps/medical_notes/services/couch.py create mode 100644 src/ahc/apps/medical_notes/services/notes.py create mode 100644 src/ahc/apps/medical_notes/services/notifications.py diff --git a/TODO.md b/TODO.md index 86ec135..153baab 100644 --- a/TODO.md +++ b/TODO.md @@ -55,6 +55,71 @@ risk of changing form validation error messages. Extraction to a service layer is already started. Keep signal decisions (§1) in sync with this work to avoid duplicating logic. +## 6. Dependency migrations + +### 6a. Remove `httplib2` (no-risk, easy) + +`httplib2` is declared as a direct dependency but is **never imported** anywhere in `src/`. +It is a legacy Python 2-era HTTP library superseded by `requests` (already in use). + +Action: remove from `pyproject.toml`, run `uv lock`. + +### 6b. Move `icecream` to dev dependencies + +`icecream` is in the main (production) dependency group but the only usage is a +**lazy import inside a debug branch** in `src/celery_notifications/cron.py:121`. + +Action: move to `[dependency-groups] dev` in `pyproject.toml`. + +### 6c. Replace `pycouchdb` with maintained HTTP client + +`pycouchdb v1.16.0` has not been maintained since ~2019 and has no Python 3.13+ +test coverage. The entire interaction is already isolated in one adapter: + +- `src/ahc/settings.py` — `pycouchdb.Server(...)` connection init +- `src/ahc/apps/medical_notes/services/couch.py` — single thin adapter class + +Migration options (pick one): +- **Raw `requests`** — CouchDB exposes a plain HTTP/JSON API; no client lib needed. + Simplest: replaces `pycouchdb.Server` + `db[key]` with `requests.get/put` calls. +- **`httpx`** — async-capable drop-in; useful if the app ever adopts ASGI fully. + +Risk is low because ADR-08 scopes CouchDB to file/attachment storage only (no +complex queries). The adapter in `couch.py` is the single change surface. + +### 6d. Remove `django-libsass` + `libsass` by precompiling SCSS + +`django-libsass v0.9` (2021) drives `COMPRESS_PRECOMPILERS` to compile +`static/custom_pico.scss` at request time via `SassCompiler`. + +The SCSS file is small (17 lines — PicoCSS variable overrides + one `@import`). +Precompiling it once at build/deploy time removes two C-extension deps (`libsass`, +`cffi`) from the runtime and the runtime compilation overhead. + +Action: +1. Run `sass static/custom_pico.scss static/css/custom_pico.css` once (dart-sass + or node-sass; **not** a Python dep — run as a build step or commit the output). +2. Replace the `` in `base.html` from the compressor tag to a plain CSS link. +3. Remove `django-libsass`, `libsass`, `django-compressor`, `django-appconf` from + `pyproject.toml` (if compressor is not used for anything else). +4. Remove `COMPRESS_PRECOMPILERS` block from `settings.py`. + +Check `settings.py` for other `COMPRESS_*` entries before removing `django-compressor`. + +### 6e. Monitor: transitive Python-2-era deps + +These cannot be removed directly — they are pulled in by maintained packages: + +| Package | Via | Concern | +|---------|-----|---------| +| `six v1.17.0` | `python-dateutil` → `celery` | Python 2 compat; harmless but signals old dep chain | +| `audioop-lts v0.2.2` | `discord.py` | Shim for `audioop` removed in Python 3.13; watch discord.py changelog | +| `pytz v2026.2` | `flower` | Django 4.0+ prefers `zoneinfo`; not actionable until Flower drops pytz | + +No action needed now; re-check when upgrading Celery or discord.py. + +--- + ## Already fixed in `refactor/django-5x-prep` - `STATIC_ROOT` → `BASE_DIR / "static_collected"` ✅ diff --git a/conftest.py b/conftest.py index 72922e2..e6e8c00 100644 --- a/conftest.py +++ b/conftest.py @@ -9,7 +9,7 @@ def user_profile(db): """Create a User + Profile pair, mocking image processing in Profile.save().""" from ahc.apps.users.models import Profile - user = User.objects.create_user(username="testuser", password="testpass123") + user = User.objects.create_user(username="testuser", password="testpass123") # nosec B106 with patch("ahc.apps.users.models.Image.open") as mock_open: mock_img = MagicMock() mock_img.height = 100 @@ -24,7 +24,7 @@ def second_user_profile(db): """A second User + Profile for multi-user permission tests.""" from ahc.apps.users.models import Profile - user = User.objects.create_user(username="otheruser", password="testpass123") + user = User.objects.create_user(username="otheruser", password="testpass123") # nosec B106 with patch("ahc.apps.users.models.Image.open") as mock_open: mock_img = MagicMock() mock_img.height = 100 diff --git a/src/ahc/apps/animals/mixins/animal_owner_permissions.py b/src/ahc/apps/animals/mixins/animal_owner_permissions.py index e62efdb..292679a 100644 --- a/src/ahc/apps/animals/mixins/animal_owner_permissions.py +++ b/src/ahc/apps/animals/mixins/animal_owner_permissions.py @@ -1,11 +1,10 @@ from django.contrib.auth.mixins import UserPassesTestMixin from ahc.apps.animals.models import Animal +from ahc.apps.animals.selectors import is_animal_owner class UserPassesOwnershipTestMixin(UserPassesTestMixin): def test_func(self): - owner = Animal.objects.get(pk=self.kwargs["pk"]).owner - user = self.request.user.profile - - return user == owner + animal = Animal.objects.get(pk=self.kwargs["pk"]) + return is_animal_owner(self.request.user.profile, animal) diff --git a/src/ahc/apps/animals/selectors.py b/src/ahc/apps/animals/selectors.py new file mode 100644 index 0000000..306ee4e --- /dev/null +++ b/src/ahc/apps/animals/selectors.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from django.db.models import Q, QuerySet + +from ahc.apps.animals.models import Animal + + +def animals_visible_to(profile) -> QuerySet[Animal]: + """Return all animals accessible to the given profile (owner or keeper).""" + return Animal.objects.filter(Q(owner=profile) | Q(allowed_users=profile)).order_by("-creation_date") + + +def user_can_access_animal(profile, animal: Animal) -> bool: + """Return True if the profile is the owner or one of the allowed_users of the animal.""" + if animal.owner == profile: + return True + return animal.allowed_users.filter(pk=profile.pk).exists() + + +def is_animal_owner(profile, animal: Animal) -> bool: + """Return True if the profile is the owner of the animal.""" + return animal.owner == profile + + +def is_pinned(profile, animal: Animal) -> bool: + """Return True if the animal is currently pinned by the given profile.""" + return profile.pinned_animals.filter(pk=animal.pk).exists() + + +def recent_records_for(animal: Animal, limit: int = 5) -> QuerySet: + """Return the most recent MedicalRecords for the animal, newest first.""" + from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord + + return MedicalRecord.objects.filter(animal=animal).order_by("-date_creation")[:limit] diff --git a/src/ahc/apps/animals/services.py b/src/ahc/apps/animals/services.py new file mode 100644 index 0000000..3f7f188 --- /dev/null +++ b/src/ahc/apps/animals/services.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from django.shortcuts import get_object_or_404 +from PIL import Image + +from ahc.apps.animals.models import Animal +from ahc.apps.animals.selectors import user_can_access_animal + + +def create_animal(owner_profile, form) -> Animal: + """Create a new animal owned by owner_profile from a validated form instance.""" + animal = form.save(commit=False) + animal.owner = owner_profile + animal.save() + return animal + + +def pin_animal(profile, animal_id) -> None: + """Pin an animal for the given profile. + + Raises PermissionError when the profile has no read access to the animal. + """ + animal = get_object_or_404(Animal, id=animal_id) + if not user_can_access_animal(profile, animal): + raise PermissionError("You do not have access to this animal.") + profile.pinned_animals.add(animal) + + +def unpin_animal(profile, animal_id) -> None: + """Remove an animal from the profile's pinned list.""" + profile.pinned_animals.remove(animal_id) + + +def process_profile_image(animal: Animal) -> None: + """Resize the animal's profile image to at most 448x448 pixels.""" + img = Image.open(animal.profile_image.path) + if img.height > 448 or img.width > 448: + img.thumbnail((448, 448)) + img.save(animal.profile_image.path) + + +def transfer_ownership(animal: Animal, new_owner, set_keeper: bool, requesting_profile) -> None: + """Transfer animal ownership to new_owner. + + If set_keeper is True, the previous owner (requesting_profile) is added to allowed_users. + """ + animal.owner = new_owner + animal.save() + if set_keeper: + animal.allowed_users.add(requesting_profile) + + +def add_keeper(animal: Animal, keeper_id) -> None: + """Add a keeper to the animal's allowed_users list by Profile PK.""" + animal.allowed_users.add(keeper_id) + + +def set_birthday(animal: Animal, birthdate) -> None: + """Update the animal's birthdate.""" + animal.birthdate = birthdate + animal.save() + + +def set_first_contact(animal: Animal, vet: str, place: str) -> None: + """Update the animal's first-contact vet name and medical place.""" + animal.first_contact_vet = vet + animal.first_contact_medical_place = place + animal.save() diff --git a/src/ahc/apps/animals/tests.py b/src/ahc/apps/animals/tests.py index 933a9c0..93d5b15 100644 --- a/src/ahc/apps/animals/tests.py +++ b/src/ahc/apps/animals/tests.py @@ -1,6 +1,26 @@ +from datetime import date +from unittest.mock import MagicMock, patch + import pytest from ahc.apps.animals.models import Animal +from ahc.apps.animals.selectors import ( + animals_visible_to, + is_animal_owner, + is_pinned, + recent_records_for, + user_can_access_animal, +) +from ahc.apps.animals.services import ( + add_keeper, + create_animal, + pin_animal, + process_profile_image, + set_birthday, + set_first_contact, + transfer_ownership, + unpin_animal, +) from ahc.apps.animals.signals import update_allowed_users @@ -55,3 +75,244 @@ def test_non_owner_keeper_not_affected(self, animal, second_user_profile): def test_no_op_when_allowed_users_is_empty(self, animal): update_allowed_users(sender=Animal, instance=animal) assert animal.allowed_users.count() == 0 + + +@pytest.mark.unit +class TestIsAnimalOwnerSelector: + """is_animal_owner: pure predicate — no DB; uses MagicMock.""" + + def test_returns_true_for_owner(self): + profile = MagicMock() + animal = MagicMock() + animal.owner = profile + assert is_animal_owner(profile, animal) is True + + def test_returns_false_for_non_owner(self): + owner = MagicMock() + other = MagicMock() + animal = MagicMock() + animal.owner = owner + assert is_animal_owner(other, animal) is False + + +@pytest.mark.unit +class TestUserCanAccessAnimalSelector: + """user_can_access_animal: short-circuits on owner; queries allowed_users otherwise.""" + + def test_owner_can_access(self): + profile = MagicMock() + animal = MagicMock() + animal.owner = profile + assert user_can_access_animal(profile, animal) is True + animal.allowed_users.filter.assert_not_called() + + def test_keeper_can_access(self): + profile = MagicMock() + animal = MagicMock() + animal.owner = MagicMock() + animal.allowed_users.filter.return_value.exists.return_value = True + assert user_can_access_animal(profile, animal) is True + animal.allowed_users.filter.assert_called_once_with(pk=profile.pk) + + def test_stranger_cannot_access(self): + profile = MagicMock() + animal = MagicMock() + animal.owner = MagicMock() + animal.allowed_users.filter.return_value.exists.return_value = False + assert user_can_access_animal(profile, animal) is False + + +@pytest.mark.unit +class TestIsPinnedSelector: + """is_pinned: delegates to profile.pinned_animals.filter().exists().""" + + def test_pinned_returns_true(self): + profile = MagicMock() + animal = MagicMock() + profile.pinned_animals.filter.return_value.exists.return_value = True + assert is_pinned(profile, animal) is True + profile.pinned_animals.filter.assert_called_once_with(pk=animal.pk) + + def test_not_pinned_returns_false(self): + profile = MagicMock() + animal = MagicMock() + profile.pinned_animals.filter.return_value.exists.return_value = False + assert is_pinned(profile, animal) is False + + +@pytest.mark.integration +@pytest.mark.django_db +class TestAnimalsVisibleToSelector: + """animals_visible_to: ORM query — returns owner + keeper animals.""" + + def test_owner_sees_own_animal(self, animal, user_profile): + _, profile = user_profile + assert animal in animals_visible_to(profile) + + def test_keeper_sees_allowed_animal(self, animal, second_user_profile): + _, other_profile = second_user_profile + animal.allowed_users.add(other_profile) + assert animal in animals_visible_to(other_profile) + + def test_stranger_cannot_see_animal(self, animal, second_user_profile): + _, other_profile = second_user_profile + assert animal not in animals_visible_to(other_profile) + + +@pytest.mark.integration +@pytest.mark.django_db +class TestRecentRecordsForSelector: + """recent_records_for: returns MedicalRecords for the animal, newest first.""" + + def test_returns_empty_when_no_records(self, animal): + records = list(recent_records_for(animal)) + assert records == [] + + def test_respects_limit(self, animal, user_profile): + from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord + + _, profile = user_profile + for i in range(7): + MedicalRecord.objects.create( + animal=animal, + author=profile, + short_description=f"Note {i}", + type_of_event="general", + ) + records = list(recent_records_for(animal, limit=5)) + assert len(records) == 5 + + +@pytest.mark.unit +class TestCreateAnimalService: + """create_animal: assigns owner from profile, saves, and returns the new animal.""" + + def test_assigns_owner_saves_and_returns(self): + profile = MagicMock() + form = MagicMock() + animal_mock = MagicMock() + form.save.return_value = animal_mock + + result = create_animal(profile, form) + + form.save.assert_called_once_with(commit=False) + assert animal_mock.owner == profile + animal_mock.save.assert_called_once() + assert result is animal_mock + + +@pytest.mark.unit +class TestPinAnimalService: + """pin_animal / unpin_animal: ownership-checked M2M operations on profile.""" + + def test_pin_raises_when_no_access(self): + profile = MagicMock() + with ( + patch("ahc.apps.animals.services.get_object_or_404", return_value=MagicMock()), + patch("ahc.apps.animals.services.user_can_access_animal", return_value=False), + pytest.raises(PermissionError), + ): + pin_animal(profile, "some-uuid") + + def test_pin_adds_animal_when_access_granted(self): + profile = MagicMock() + animal = MagicMock() + with ( + patch("ahc.apps.animals.services.get_object_or_404", return_value=animal), + patch("ahc.apps.animals.services.user_can_access_animal", return_value=True), + ): + pin_animal(profile, "some-uuid") + + profile.pinned_animals.add.assert_called_once_with(animal) + + def test_unpin_removes_by_id(self): + profile = MagicMock() + unpin_animal(profile, "some-uuid") + profile.pinned_animals.remove.assert_called_once_with("some-uuid") + + +@pytest.mark.unit +class TestProcessProfileImageService: + """process_profile_image: thumbnail only when image exceeds 448x448.""" + + def test_thumbnails_when_image_too_large(self): + animal = MagicMock() + img = MagicMock() + img.height = 600 + img.width = 800 + with patch("ahc.apps.animals.services.Image.open", return_value=img): + process_profile_image(animal) + img.thumbnail.assert_called_once_with((448, 448)) + img.save.assert_called_once() + + def test_no_thumbnail_when_within_limit(self): + animal = MagicMock() + img = MagicMock() + img.height = 200 + img.width = 200 + with patch("ahc.apps.animals.services.Image.open", return_value=img): + process_profile_image(animal) + img.thumbnail.assert_not_called() + + def test_thumbnail_when_exactly_one_dimension_exceeds(self): + animal = MagicMock() + img = MagicMock() + img.height = 100 + img.width = 449 + with patch("ahc.apps.animals.services.Image.open", return_value=img): + process_profile_image(animal) + img.thumbnail.assert_called_once_with((448, 448)) + + +@pytest.mark.unit +class TestTransferOwnershipService: + """transfer_ownership: reassigns owner; optionally makes requester a keeper.""" + + def test_assigns_new_owner_and_saves(self): + animal = MagicMock() + new_owner = MagicMock() + requesting = MagicMock() + + transfer_ownership(animal, new_owner, set_keeper=False, requesting_profile=requesting) + + assert animal.owner == new_owner + animal.save.assert_called_once() + animal.allowed_users.add.assert_not_called() + + def test_adds_requesting_as_keeper_when_flag_is_set(self): + animal = MagicMock() + new_owner = MagicMock() + requesting = MagicMock() + + transfer_ownership(animal, new_owner, set_keeper=True, requesting_profile=requesting) + + animal.allowed_users.add.assert_called_once_with(requesting) + + +@pytest.mark.unit +class TestAddKeeperService: + """add_keeper: delegates to M2M.add with the provided keeper id.""" + + def test_adds_keeper_by_id(self): + animal = MagicMock() + add_keeper(animal, 42) + animal.allowed_users.add.assert_called_once_with(42) + + +@pytest.mark.unit +class TestAnimalFieldUpdateServices: + """set_birthday / set_first_contact: update specific fields and call save.""" + + def test_set_birthday_assigns_date_and_saves(self): + animal = MagicMock() + bd = date(2020, 6, 15) + set_birthday(animal, bd) + assert animal.birthdate == bd + animal.save.assert_called_once() + + def test_set_first_contact_assigns_both_fields_and_saves(self): + animal = MagicMock() + set_first_contact(animal, vet="Dr Smith", place="City Clinic") + assert animal.first_contact_vet == "Dr Smith" + assert animal.first_contact_medical_place == "City Clinic" + animal.save.assert_called_once() diff --git a/src/ahc/apps/animals/utils_owner/forms.py b/src/ahc/apps/animals/utils_owner/forms.py index fb7cf7f..6b75907 100644 --- a/src/ahc/apps/animals/utils_owner/forms.py +++ b/src/ahc/apps/animals/utils_owner/forms.py @@ -46,7 +46,6 @@ def __init__(self, *args, **kwargs): def clean_new_owner(self): new_owner = self.cleaned_data.get("new_owner") - print(self.cleaned_data.keys()) if new_owner == self.instance.owner.user.username: raise forms.ValidationError("You are already the owner.") diff --git a/src/ahc/apps/animals/utils_owner/views.py b/src/ahc/apps/animals/utils_owner/views.py index fe72dad..349a652 100644 --- a/src/ahc/apps/animals/utils_owner/views.py +++ b/src/ahc/apps/animals/utils_owner/views.py @@ -3,10 +3,16 @@ from django.urls import reverse, reverse_lazy from django.views.generic import DeleteView from django.views.generic.edit import FormView -from PIL import Image from ahc.apps.animals.mixins.animal_owner_permissions import UserPassesOwnershipTestMixin from ahc.apps.animals.models import Animal +from ahc.apps.animals.services import ( + add_keeper, + process_profile_image, + set_birthday, + set_first_contact, + transfer_ownership, +) from ahc.apps.animals.utils_owner.forms import ( ChangeBirthdayForm, ChangeFirstContactForm, @@ -44,16 +50,8 @@ def get_form_kwargs(self): def form_valid(self, form): form.save() - - animal_instance = form.instance - img = Image.open(animal_instance.profile_image.path) - - if img.height > 448 or img.width > 448: - output_size = (448, 448) - img.thumbnail(output_size) - img.save(animal_instance.profile_image.path) - - success_url = reverse("animal_profile", kwargs={"pk": animal_instance.pk}) + process_profile_image(form.instance) + success_url = reverse("animal_profile", kwargs={"pk": form.instance.pk}) return redirect(success_url) @@ -74,15 +72,12 @@ def get_form_kwargs(self): return kwargs def form_valid(self, form): - animal = form.instance - new_owner = form.cleaned_data["new_owner"] - set_keeper = form.cleaned_data["set_keeper"] - animal.owner = new_owner - animal.save() - - if set_keeper: - animal.allowed_users.add(self.request.user.profile) - + transfer_ownership( + animal=form.instance, + new_owner=form.cleaned_data["new_owner"], + set_keeper=form.cleaned_data["set_keeper"], + requesting_profile=self.request.user.profile, + ) return super().form_valid(form) def get_success_url(self): @@ -107,10 +102,7 @@ def get_form_kwargs(self): return kwargs def form_valid(self, form): - animal = form.instance - new_keeper = form.cleaned_data["input_user"] - - animal.allowed_users.add(new_keeper) + add_keeper(form.instance, form.cleaned_data["input_user"]) return super().form_valid(form) def get_success_url(self): @@ -133,10 +125,7 @@ def get_context_data(self, **kwargs): return context def form_valid(self, form): - animal = get_object_or_404(Animal, pk=self.kwargs["pk"]) - animal.birthdate = form.cleaned_data["birthdate"] - animal.save() - + set_birthday(get_object_or_404(Animal, pk=self.kwargs["pk"]), form.cleaned_data["birthdate"]) success_url = reverse("animal_profile", kwargs={"pk": self.kwargs["pk"]}) return redirect(success_url) @@ -154,10 +143,11 @@ def get_context_data(self, **kwargs): return context def form_valid(self, form): - animal = get_object_or_404(Animal, pk=self.kwargs["pk"]) - animal.first_contact_vet = form.cleaned_data["first_contact_vet"] - animal.first_contact_medical_place = form.cleaned_data["first_contact_medical_place"] - animal.save() + set_first_contact( + get_object_or_404(Animal, pk=self.kwargs["pk"]), + vet=form.cleaned_data["first_contact_vet"], + place=form.cleaned_data["first_contact_medical_place"], + ) return super().form_valid(form) def get_success_url(self): diff --git a/src/ahc/apps/animals/views.py b/src/ahc/apps/animals/views.py index f3eed53..5ecc377 100644 --- a/src/ahc/apps/animals/views.py +++ b/src/ahc/apps/animals/views.py @@ -1,5 +1,4 @@ from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin -from django.db.models import Q from django.http import JsonResponse from django.urls import reverse from django.utils import timezone @@ -9,9 +8,14 @@ from ahc.apps.animals.forms import AnimalRegisterForm, PinAnimalForm from ahc.apps.animals.models import Animal -from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord - -# from users.models import Profile as UserProfile +from ahc.apps.animals.selectors import ( + animals_visible_to, + is_animal_owner, + is_pinned, + recent_records_for, + user_can_access_animal, +) +from ahc.apps.animals.services import create_animal, pin_animal, unpin_animal class CreateAnimalView(LoginRequiredMixin, FormView): @@ -20,12 +24,8 @@ class CreateAnimalView(LoginRequiredMixin, FormView): success_url = "/animals/" def form_valid(self, form): - new_animal = form.save(commit=False) - new_animal.owner = self.request.user.profile - new_animal.save() - + new_animal = create_animal(self.request.user.profile, form) self.success_url = reverse("animal_profile", kwargs={"pk": new_animal.id}) - return super().form_valid(form) @@ -36,64 +36,43 @@ class AnimalProfileDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailVie def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + profile = self.request.user.profile context["now"] = timezone.now().date() - - # only for visibility of buttons, do not use as authentication - context["is_owner"] = self.object.owner == self.request.user.profile - - context["is_pinned"] = context["is_pinned"] = self.request.user.profile.pinned_animals.filter( - pk=self.object.pk - ).exists() - - recent_records = MedicalRecord.objects.filter(animal=self.object).order_by("-date_creation")[:5] - - context["recent_records"] = recent_records - + # only for button visibility, do not use as authentication + context["is_owner"] = is_animal_owner(profile, self.object) + context["is_pinned"] = is_pinned(profile, self.object) + context["recent_records"] = recent_records_for(self.object) return context def test_func(self): - all_users = set(self.get_object().allowed_users.all()) - all_users.add(self.get_object().owner) + animal = self.get_object() + return user_can_access_animal(self.request.user.profile, animal) - user = self.request.user.profile - return user in all_users - - -class StableView(TemplateView, LoginRequiredMixin): +class StableView(LoginRequiredMixin, TemplateView): template_name = "animals/all_animals_stable.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - query = Animal.objects.filter( - Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile) - ).order_by("-creation_date") - - context["animals"] = query - + context["animals"] = animals_visible_to(self.request.user.profile) return context -class ToPinAnimalsView(View): +class ToPinAnimalsView(LoginRequiredMixin, View): def post(self, request, *args, **kwargs): - print("DUPA1") - form = PinAnimalForm(request.POST) - print(form) - - current_user = request.user + profile = request.user.profile if form.is_valid(): animal_id = form.cleaned_data["animal_id"] action = form.cleaned_data["action"] if action == "add": - animal = Animal.objects.get(id=animal_id) - current_user.pinned_animals.add(animal) - current_user.save() + try: + pin_animal(profile, animal_id) + except PermissionError: + return JsonResponse({"status": "forbidden"}, status=403) elif action == "remove": - current_user.pinned_animals.remove(animal_id) - current_user.save() + unpin_animal(profile, animal_id) return JsonResponse({"status": "success"}) diff --git a/src/ahc/apps/medical_notes/selectors.py b/src/ahc/apps/medical_notes/selectors.py new file mode 100644 index 0000000..fa913b5 --- /dev/null +++ b/src/ahc/apps/medical_notes/selectors.py @@ -0,0 +1,80 @@ +"""Read-only selectors for the medical_notes app. + +All functions are side-effect free: they only query the database and return +values. Views, services, and mixins should call these instead of writing +inline ORM queries. +""" + +from __future__ import annotations + +from django.db.models import QuerySet + +from ahc.apps.animals.selectors import animals_visible_to, user_can_access_animal +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment + + +def animal_choices_for(profile, exclude_id=None) -> list[tuple]: + """Return (id, full_name) pairs for animals accessible to the profile. + + Optionally excludes one animal by UUID (used when the animal is already + the primary animal on a note). + """ + queryset = animals_visible_to(profile) + if exclude_id is not None: + queryset = queryset.exclude(id=exclude_id) + return [(animal.id, animal.full_name) for animal in queryset] + + +def timeline_for( + animal, + type_of_event: str | None = None, + tag_name: str | None = None, +) -> QuerySet[MedicalRecord]: + """Return MedicalRecords for an animal, optionally filtered by type or tag. + + Results are prefetch_related for attachments. Ordering and timezone + localisation remain the caller's responsibility (presentation logic). + """ + queryset = MedicalRecord.objects.filter(animal=animal).prefetch_related("attachments") + if type_of_event: + queryset = queryset.filter(type_of_event=type_of_event) + if tag_name: + queryset = queryset.filter(note_tags__slug=tag_name) + return queryset + + +def feeding_notes_for(medical_record) -> QuerySet: + """Return all FeedingNotes linked to the given MedicalRecord.""" + from ahc.apps.medical_notes.models.type_feeding_notes import FeedingNote + + return FeedingNote.objects.filter(related_note=medical_record) + + +def notifications_for_feednote(feednote_pk) -> QuerySet: + """Return EmailNotifications linked to a specific FeedingNote PK.""" + from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification + + return EmailNotification.objects.filter(related_note=feednote_pk) + + +def notifications_for_mednote(mednote_uuid) -> QuerySet: + """Return EmailNotifications reachable through FeedingNotes of a MedicalRecord.""" + from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote + + feednotes = FeedingNote.objects.filter(related_note=mednote_uuid) + return EmailNotification.objects.filter(related_note__in=feednotes).order_by("-last_modification") + + +def is_note_author(profile, note: MedicalRecord) -> bool: + """Return True if the profile is the author of the note.""" + return note.author == profile + + +def is_attachment_author(profile, attachment: MedicalRecordAttachment) -> bool: + """Return True if the profile authored the note that owns this attachment.""" + return attachment.medical_record.author == profile + + +def can_access_note_animal(profile, note: MedicalRecord) -> bool: + """Return True if the profile is owner or keeper of the animal linked to the note.""" + return user_can_access_animal(profile, note.animal) diff --git a/src/ahc/apps/medical_notes/services/__init__.py b/src/ahc/apps/medical_notes/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ahc/apps/medical_notes/services/attachments.py b/src/ahc/apps/medical_notes/services/attachments.py new file mode 100644 index 0000000..924d223 --- /dev/null +++ b/src/ahc/apps/medical_notes/services/attachments.py @@ -0,0 +1,83 @@ +"""Services for MedicalRecordAttachment: upload, delete, download via CouchDB.""" + +from __future__ import annotations + +from django.conf import settings +from django.db import transaction + +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment +from ahc.apps.medical_notes.services.couch import CouchAttachmentClient + + +class AttachmentLimitExceeded(Exception): + """Raised when the per-note attachment limit (COUCH_DB_LIMIT_PER_NOTE) is reached.""" + + +def upload_attachment( + medical_record: MedicalRecord, + attachment_instance: MedicalRecordAttachment, + uploaded_file, + couch_client: CouchAttachmentClient | None = None, +) -> MedicalRecordAttachment: + """Upload a file to CouchDB and persist the attachment metadata to PostgreSQL. + + Raises AttachmentLimitExceeded when the per-note limit is already reached. + The caller is responsible for form validation; this function operates on + already-validated data. + """ + if couch_client is None: + couch_client = CouchAttachmentClient() + + limit = settings.COUCH_DB_LIMIT_PER_NOTE + current_count = MedicalRecordAttachment.objects.filter(medical_record=medical_record).count() + if current_count >= limit: + raise AttachmentLimitExceeded(f"Maximum of {limit} attachments per note already reached.") + + reference_uuid = str(attachment_instance.id) + file_name = uploaded_file.name + uploaded_file.seek(0) + blob = uploaded_file.read() + + with transaction.atomic(): + couch_client.save_attachment(reference_uuid, file_name, blob) + attachment_instance.medical_record = medical_record + attachment_instance.file_name = file_name + attachment_instance.couch_id = reference_uuid + attachment_instance.file = None + attachment_instance.save() + + return attachment_instance + + +def delete_attachment( + attachment: MedicalRecordAttachment, + couch_client: CouchAttachmentClient | None = None, +) -> None: + """Remove the attachment from CouchDB and delete its PostgreSQL row.""" + if couch_client is None: + couch_client = CouchAttachmentClient() + + couch_client.delete_attachment(str(attachment.couch_id)) + attachment.delete() + + +def download_attachment( + couch_id: str, + couch_client: CouchAttachmentClient | None = None, +) -> tuple[bytes, str]: + """Retrieve attachment bytes and file name from CouchDB. + + Returns (file_bytes, file_name). + Raises django.http.Http404 when the document or its blob is missing. + """ + from django.http import Http404 + + if couch_client is None: + couch_client = CouchAttachmentClient() + + result = couch_client.get_attachment(couch_id) + if result is None: + raise Http404("Attachment not found.") + + doc, file_data = result + return file_data, doc.get("name", "") diff --git a/src/ahc/apps/medical_notes/services/biometrics.py b/src/ahc/apps/medical_notes/services/biometrics.py new file mode 100644 index 0000000..0b03652 --- /dev/null +++ b/src/ahc/apps/medical_notes/services/biometrics.py @@ -0,0 +1,50 @@ +"""Service for creating BiometricRecord entries.""" + +from __future__ import annotations + +from ahc.apps.medical_notes.models.type_measurement_notes import ( + BiometricCustomRecords, + BiometricHeightRecords, + BiometricRecord, + BiometricWeightRecords, +) + + +def create_biometric_record(animal, related_note, record_type: str, data: dict) -> BiometricRecord: + """Create the appropriate sub-record and link it to a parent BiometricRecord. + + record_type must be one of: "weight", "height", or a custom type. + data is the cleaned_data dict from BiometricRecordForm. + """ + if record_type == "weight": + sub_record = BiometricWeightRecords.objects.create( + weight=data["weight"], + weight_unit_to_present=data["weight_unit_to_present"], + ) + return BiometricRecord.objects.create( + animal=animal, + related_note=related_note, + weight_biometric_record=sub_record, + ) + + if record_type == "height": + sub_record = BiometricHeightRecords.objects.create( + height=data["height"], + height_unit_to_present=data["height_unit_to_present"], + ) + return BiometricRecord.objects.create( + animal=animal, + related_note=related_note, + height_biometric_record=sub_record, + ) + + sub_record = BiometricCustomRecords.objects.create( + record_name=data["custom_name"], + record_value=data["custom_value"], + record_unit=data["custom_unit"], + ) + return BiometricRecord.objects.create( + animal=animal, + related_note=related_note, + custom_biometric_record=sub_record, + ) diff --git a/src/ahc/apps/medical_notes/services/couch.py b/src/ahc/apps/medical_notes/services/couch.py new file mode 100644 index 0000000..52b65f3 --- /dev/null +++ b/src/ahc/apps/medical_notes/services/couch.py @@ -0,0 +1,43 @@ +"""CouchDB attachment client (ADR-08: CouchDB is attachment/file storage only). + +All raw pycouchdb interaction is isolated here. Views and other services +interact with CouchDB exclusively through this client. +""" + +from __future__ import annotations + +from django.conf import settings + + +class CouchAttachmentClient: + """Thin adapter over the pycouchdb database object stored in settings.COUCH_DB.""" + + def __init__(self, db=None): + self._db = db or settings.COUCH_DB + + def save_attachment(self, reference_uuid: str, file_name: str, blob: bytes) -> str: + """Upload a file blob to CouchDB and return the reference UUID (couch_id). + + Creates a CouchDB document keyed by reference_uuid, then attaches the blob. + """ + self._db.save({"_id": reference_uuid, "name": file_name}) + doc = self._db.get(reference_uuid) + self._db.put_attachment(doc, blob, filename=file_name) + return reference_uuid + + def delete_attachment(self, couch_id: str) -> None: + """Delete the CouchDB document (and its attachment) identified by couch_id.""" + self._db.delete(couch_id) + + def get_attachment(self, reference_id: str) -> tuple[dict, bytes] | None: + """Retrieve a CouchDB document and its attached file bytes. + + Returns (doc_dict, file_bytes) or None if the document does not exist. + """ + doc = self._db.get(reference_id) + if not doc: + return None + file_data = self._db.get_attachment(doc, filename=doc.get("name")) + if not file_data: + return None + return doc, file_data diff --git a/src/ahc/apps/medical_notes/services/notes.py b/src/ahc/apps/medical_notes/services/notes.py new file mode 100644 index 0000000..5a8983d --- /dev/null +++ b/src/ahc/apps/medical_notes/services/notes.py @@ -0,0 +1,41 @@ +"""Services for MedicalRecord creation and update.""" + +from __future__ import annotations + +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord + + +def create_note(author_profile, animal, form) -> MedicalRecord: + """Create and persist a MedicalRecord from a validated form. + + Sets the animal and author before saving. Handles M2M relationships + (additional_animals) via save_m2m(). + """ + note = form.save(commit=False) + note.animal = animal + note.author = author_profile + note.save() + form.save_m2m() + return note + + +def next_route_for(note: MedicalRecord, animal_id) -> tuple[str, dict]: + """Return the (url_name, kwargs) pair describing where to redirect after note creation. + + The caller is responsible for performing the redirect. + """ + event = note.type_of_event + if event == "biometric_record": + return "biometric_create", {"pk": animal_id, "note_id": note.id} + if event == "diet_note": + return "feeding_create", {"pk": note.id} + return "full_timeline_of_notes", {"pk": animal_id} + + +def update_note(note: MedicalRecord, form) -> MedicalRecord: + """Apply form data to an existing MedicalRecord and save.""" + if "animal" in form.cleaned_data: + note.animal = form.cleaned_data["animal"] + note.save() + note.additional_animals.set(form.cleaned_data.get("additional_animals") or []) + return note diff --git a/src/ahc/apps/medical_notes/services/notifications.py b/src/ahc/apps/medical_notes/services/notifications.py new file mode 100644 index 0000000..a38c720 --- /dev/null +++ b/src/ahc/apps/medical_notes/services/notifications.py @@ -0,0 +1,36 @@ +"""Services for feeding notification management.""" + +from __future__ import annotations + +from django.shortcuts import get_object_or_404 + +from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote + + +def create_email_notification(related_note: FeedingNote, form_instance, days_of_week_raw: list) -> EmailNotification: + """Build a 7-element bool array from user-submitted day indices and create an EmailNotification. + + days_of_week_raw is a list of integer strings (e.g. ["0", "2", "5"]) as received from POST. + """ + processed_days = [False] * 7 + for i in [int(day) for day in days_of_week_raw]: + processed_days[i] = True + + form_instance.days_of_week = processed_days + form_instance.related_note = related_note + + notify_kwargs = {key: value for key, value in form_instance.__dict__.items() if not key.startswith("_")} + return EmailNotification.objects.create_notification(**notify_kwargs) + + +def toggle_notification(pk) -> EmailNotification: + """Toggle the is_active flag of an EmailNotification and save.""" + notification = get_object_or_404(EmailNotification, pk=pk) + notification.is_active = not notification.is_active + notification.save() + return notification + + +def delete_notification(pk) -> None: + """Delete an EmailNotification by PK.""" + get_object_or_404(EmailNotification, pk=pk).delete() diff --git a/src/ahc/apps/medical_notes/tests.py b/src/ahc/apps/medical_notes/tests.py index c18ca7e..2258565 100644 --- a/src/ahc/apps/medical_notes/tests.py +++ b/src/ahc/apps/medical_notes/tests.py @@ -1,4 +1,6 @@ -from unittest.mock import MagicMock +from io import BytesIO +from types import SimpleNamespace +from unittest.mock import MagicMock, patch import pytest from django.core.exceptions import ValidationError @@ -7,6 +9,19 @@ BiometricHeightRecords, BiometricWeightRecords, ) +from ahc.apps.medical_notes.selectors import ( + can_access_note_animal, + is_attachment_author, + is_note_author, +) +from ahc.apps.medical_notes.services.attachments import ( + AttachmentLimitExceeded, + delete_attachment, + download_attachment, + upload_attachment, +) +from ahc.apps.medical_notes.services.notes import create_note, next_route_for, update_note +from ahc.apps.medical_notes.services.notifications import create_email_notification from ahc.apps.medical_notes.signals.type_measurement_notes import validate_one_to_one_fields @@ -52,3 +67,363 @@ def test_height_only_is_valid(self): def test_all_none_is_valid(self): instance = _make_instance() validate_one_to_one_fields(sender=None, instance=instance) + + +def _make_couch_client(save_ok=True, get_result=None, delete_ok=True): + """Build a mock CouchAttachmentClient for attachment service tests.""" + client = MagicMock() + client.save_attachment.return_value = "ref-uuid" + client.delete_attachment.return_value = None + client.get_attachment.return_value = get_result + return client + + +@pytest.mark.unit +class TestUploadAttachmentService: + """upload_attachment: orchestrates limit check, CouchDB write, and model save.""" + + def test_raises_when_limit_reached(self): + medical_record = MagicMock() + attachment = MagicMock() + uploaded_file = BytesIO(b"data") + uploaded_file.name = "photo.jpg" + client = _make_couch_client() + + with ( + patch("ahc.apps.medical_notes.services.attachments.MedicalRecordAttachment.objects.filter") as mock_filter, + patch("ahc.apps.medical_notes.services.attachments.settings") as mock_settings, + ): + mock_settings.COUCH_DB_LIMIT_PER_NOTE = 5 + mock_filter.return_value.count.return_value = 5 + + with pytest.raises(AttachmentLimitExceeded): + upload_attachment(medical_record, attachment, uploaded_file, couch_client=client) + + client.save_attachment.assert_not_called() + + def test_saves_to_couch_and_model_when_under_limit(self): + medical_record = MagicMock() + attachment = MagicMock() + attachment.id = "test-uuid" + uploaded_file = BytesIO(b"image-data") + uploaded_file.name = "photo.jpg" + client = _make_couch_client() + + with ( + patch("ahc.apps.medical_notes.services.attachments.MedicalRecordAttachment.objects.filter") as mock_filter, + patch("ahc.apps.medical_notes.services.attachments.settings") as mock_settings, + patch("ahc.apps.medical_notes.services.attachments.transaction"), + ): + mock_settings.COUCH_DB_LIMIT_PER_NOTE = 5 + mock_filter.return_value.count.return_value = 2 + + result = upload_attachment(medical_record, attachment, uploaded_file, couch_client=client) + + client.save_attachment.assert_called_once_with("test-uuid", "photo.jpg", b"image-data") + assert attachment.couch_id == "test-uuid" + assert attachment.file_name == "photo.jpg" + assert attachment.file is None + assert result is attachment + + +@pytest.mark.unit +class TestDeleteAttachmentService: + """delete_attachment: calls couch client then deletes the model row.""" + + def test_deletes_from_couch_and_db(self): + attachment = MagicMock() + attachment.couch_id = "some-couch-id" + client = _make_couch_client() + + delete_attachment(attachment, couch_client=client) + + client.delete_attachment.assert_called_once_with("some-couch-id") + attachment.delete.assert_called_once() + + +@pytest.mark.unit +class TestDownloadAttachmentService: + """download_attachment: retrieves bytes from couch client; raises Http404 on missing.""" + + def test_returns_bytes_and_name(self): + client = _make_couch_client(get_result=({"name": "report.pdf"}, b"pdf-bytes")) + + file_data, file_name = download_attachment("ref-uuid", couch_client=client) + + assert file_data == b"pdf-bytes" + assert file_name == "report.pdf" + client.get_attachment.assert_called_once_with("ref-uuid") + + def test_raises_http404_when_not_found(self): + from django.http import Http404 + + client = _make_couch_client(get_result=None) + + with pytest.raises(Http404): + download_attachment("missing-id", couch_client=client) + + +# --------------------------------------------------------------------------- +# Selectors +# --------------------------------------------------------------------------- + + +@pytest.mark.unit +class TestMedicalNoteSelectors: + """Pure predicate selectors — no DB, MagicMock only.""" + + def test_is_note_author_true_for_author(self): + profile = MagicMock() + note = MagicMock() + note.author = profile + assert is_note_author(profile, note) is True + + def test_is_note_author_false_for_non_author(self): + note = MagicMock() + note.author = MagicMock() + assert is_note_author(MagicMock(), note) is False + + def test_is_attachment_author_true_when_note_author_matches(self): + profile = MagicMock() + attachment = MagicMock() + attachment.medical_record.author = profile + assert is_attachment_author(profile, attachment) is True + + def test_is_attachment_author_false_for_other_profile(self): + attachment = MagicMock() + attachment.medical_record.author = MagicMock() + assert is_attachment_author(MagicMock(), attachment) is False + + def test_can_access_note_animal_delegates_to_animals_selector(self): + profile = MagicMock() + note = MagicMock() + with patch("ahc.apps.medical_notes.selectors.user_can_access_animal", return_value=True) as mock_selector: + result = can_access_note_animal(profile, note) + + mock_selector.assert_called_once_with(profile, note.animal) + assert result is True + + def test_can_access_note_animal_returns_false_when_denied(self): + profile = MagicMock() + note = MagicMock() + with patch("ahc.apps.medical_notes.selectors.user_can_access_animal", return_value=False): + assert can_access_note_animal(profile, note) is False + + +# --------------------------------------------------------------------------- +# services/notes.py +# --------------------------------------------------------------------------- + + +@pytest.mark.unit +class TestNextRouteForService: + """next_route_for: pure routing logic — no DB, no mocks needed.""" + + def test_biometric_event_routes_to_biometric_create(self): + note = MagicMock() + note.type_of_event = "biometric_record" + note.id = "note-uuid" + + url_name, kwargs = next_route_for(note, "animal-uuid") + + assert url_name == "biometric_create" + assert kwargs == {"pk": "animal-uuid", "note_id": "note-uuid"} + + def test_diet_note_routes_to_feeding_create(self): + note = MagicMock() + note.type_of_event = "diet_note" + note.id = "note-uuid" + + url_name, kwargs = next_route_for(note, "animal-uuid") + + assert url_name == "feeding_create" + assert kwargs == {"pk": "note-uuid"} + + def test_any_other_type_routes_to_full_timeline(self): + note = MagicMock() + note.type_of_event = "general" + + url_name, kwargs = next_route_for(note, "animal-uuid") + + assert url_name == "full_timeline_of_notes" + assert kwargs == {"pk": "animal-uuid"} + + +@pytest.mark.unit +class TestCreateNoteService: + """create_note: sets author/animal on unsaved instance, saves, calls save_m2m.""" + + def test_assigns_fields_saves_and_returns_note(self): + author = MagicMock() + animal = MagicMock() + form = MagicMock() + note_mock = MagicMock() + form.save.return_value = note_mock + + result = create_note(author, animal, form) + + form.save.assert_called_once_with(commit=False) + assert note_mock.animal == animal + assert note_mock.author == author + note_mock.save.assert_called_once() + form.save_m2m.assert_called_once() + assert result is note_mock + + +@pytest.mark.unit +class TestUpdateNoteService: + """update_note: conditionally reassigns animal, saves, sets additional_animals.""" + + def test_reassigns_animal_when_present_in_cleaned_data(self): + note = MagicMock() + form = MagicMock() + new_animal = MagicMock() + form.cleaned_data = {"animal": new_animal, "additional_animals": []} + + update_note(note, form) + + assert note.animal == new_animal + note.save.assert_called_once() + + def test_skips_animal_reassignment_when_not_in_cleaned_data(self): + note = MagicMock() + original_animal = note.animal + form = MagicMock() + form.cleaned_data = {"additional_animals": []} + + update_note(note, form) + + assert note.animal == original_animal + + def test_sets_additional_animals_m2m(self): + note = MagicMock() + form = MagicMock() + extras = [MagicMock(), MagicMock()] + form.cleaned_data = {"additional_animals": extras} + + update_note(note, form) + + note.additional_animals.set.assert_called_once_with(extras) + + +# --------------------------------------------------------------------------- +# services/notifications.py +# --------------------------------------------------------------------------- + + +@pytest.mark.unit +class TestCreateEmailNotificationService: + """create_email_notification: builds a 7-element bool days array before delegating. + + Uses SimpleNamespace so attribute assignment works without MagicMock interference. + """ + + def _notification_mock(self): + return MagicMock() + + def test_selected_days_become_true_rest_false(self): + related_note = MagicMock() + form_instance = SimpleNamespace() + + with patch("ahc.apps.medical_notes.services.notifications.EmailNotification"): + create_email_notification(related_note, form_instance, ["0", "2", "6"]) + + assert form_instance.days_of_week == [True, False, True, False, False, False, True] + + def test_empty_selection_produces_all_false(self): + related_note = MagicMock() + form_instance = SimpleNamespace() + + with patch("ahc.apps.medical_notes.services.notifications.EmailNotification"): + create_email_notification(related_note, form_instance, []) + + assert form_instance.days_of_week == [False] * 7 + + def test_all_days_selected(self): + related_note = MagicMock() + form_instance = SimpleNamespace() + + with patch("ahc.apps.medical_notes.services.notifications.EmailNotification"): + create_email_notification(related_note, form_instance, ["0", "1", "2", "3", "4", "5", "6"]) + + assert form_instance.days_of_week == [True] * 7 + + def test_related_note_is_set_on_instance(self): + related_note = MagicMock() + form_instance = SimpleNamespace() + + with patch("ahc.apps.medical_notes.services.notifications.EmailNotification"): + create_email_notification(related_note, form_instance, []) + + assert form_instance.related_note is related_note + + +# --------------------------------------------------------------------------- +# services/biometrics.py — integration (requires DB) +# --------------------------------------------------------------------------- + + +@pytest.fixture +def medical_note(db, user_profile): + from ahc.apps.animals.models import Animal + from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord + + _, profile = user_profile + animal = Animal.objects.create(full_name="Tester", owner=profile) + return MedicalRecord.objects.create( + animal=animal, + author=profile, + short_description="biometric base note", + type_of_event="biometric_record", + ) + + +@pytest.mark.integration +@pytest.mark.django_db +class TestCreateBiometricRecordService: + """create_biometric_record: branches across weight / height / custom sub-records.""" + + def test_creates_weight_record(self, medical_note): + from ahc.apps.medical_notes.services.biometrics import create_biometric_record + + record = create_biometric_record( + medical_note.animal, + medical_note, + "weight", + {"weight": 4.5, "weight_unit_to_present": "kg"}, + ) + + assert record.animal == medical_note.animal + assert record.weight_biometric_record is not None + assert record.weight_biometric_record.weight == 4.5 + assert record.height_biometric_record is None + assert record.custom_biometric_record is None + + def test_creates_height_record(self, medical_note): + from ahc.apps.medical_notes.services.biometrics import create_biometric_record + + record = create_biometric_record( + medical_note.animal, + medical_note, + "height", + {"height": 30.0, "height_unit_to_present": "cm"}, + ) + + assert record.height_biometric_record is not None + assert record.height_biometric_record.height == 30.0 + assert record.weight_biometric_record is None + + def test_creates_custom_record(self, medical_note): + from ahc.apps.medical_notes.services.biometrics import create_biometric_record + + record = create_biometric_record( + medical_note.animal, + medical_note, + "custom", + {"custom_name": "Temperature", "custom_value": "38.5", "custom_unit": "°C"}, + ) + + assert record.custom_biometric_record is not None + assert record.custom_biometric_record.record_name == "Temperature" + assert record.weight_biometric_record is None + assert record.height_biometric_record is None diff --git a/src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py b/src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py index e69de29..7f2791a 100644 --- a/src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py +++ b/src/ahc/apps/medical_notes/views/mixins/user_animal_permisions.py @@ -0,0 +1,56 @@ +"""Permission mixins for medical_notes views. + +Each mixin implements test_func by delegating to a selector from +ahc.apps.medical_notes.selectors, keeping views free of inline permission logic. + +Two access-level patterns exist: +- AnimalDirectAccessRequiredMixin — pk in URL is an Animal UUID directly. +- AnimalAccessRequiredMixin — pk in URL is a MedicalRecord UUID; access is + checked on the linked animal. +- NoteAuthorRequiredMixin — pk is a MedicalRecord UUID; author-only access. +- AttachmentAuthorRequiredMixin — pk is a MedicalRecordAttachment UUID. +""" + +from django.contrib.auth.mixins import UserPassesTestMixin +from django.shortcuts import get_object_or_404 + +from ahc.apps.animals.models import Animal +from ahc.apps.animals.selectors import user_can_access_animal +from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment +from ahc.apps.medical_notes.selectors import ( + can_access_note_animal, + is_attachment_author, + is_note_author, +) + + +class AnimalDirectAccessRequiredMixin(UserPassesTestMixin): + """Allow access when pk in URL is an Animal UUID and the profile can access it.""" + + def test_func(self): + animal = get_object_or_404(Animal, id=self.kwargs.get("pk")) + return user_can_access_animal(self.request.user.profile, animal) + + +class AnimalAccessRequiredMixin(UserPassesTestMixin): + """Allow access when pk in URL is a MedicalRecord UUID and the profile can access its animal.""" + + def test_func(self): + note = get_object_or_404(MedicalRecord, id=self.kwargs.get("pk")) + return can_access_note_animal(self.request.user.profile, note) + + +class NoteAuthorRequiredMixin(UserPassesTestMixin): + """Allow access only to the author of the MedicalRecord (pk = note UUID).""" + + def test_func(self): + note = get_object_or_404(MedicalRecord, id=self.kwargs.get("pk")) + return is_note_author(self.request.user.profile, note) + + +class AttachmentAuthorRequiredMixin(UserPassesTestMixin): + """Allow access only to the author of the note that owns the attachment (pk = attachment UUID).""" + + def test_func(self): + attachment = get_object_or_404(MedicalRecordAttachment, pk=self.kwargs.get("pk")) + return is_attachment_author(self.request.user.profile, attachment) diff --git a/src/ahc/apps/medical_notes/views/type_basic_note.py b/src/ahc/apps/medical_notes/views/type_basic_note.py index 13110d8..b533130 100644 --- a/src/ahc/apps/medical_notes/views/type_basic_note.py +++ b/src/ahc/apps/medical_notes/views/type_basic_note.py @@ -1,10 +1,7 @@ -from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.core.paginator import Paginator -from django.db import transaction -from django.db.models import Q -from django.http import Http404, HttpResponse +from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect from django.urls import reverse from django.utils import timezone @@ -20,26 +17,33 @@ UploadAppendixForm, ) from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord, MedicalRecordAttachment - -# UploadAppendixFormSet = formset_factory(UploadAppendixForm, extra=0) +from ahc.apps.medical_notes.selectors import ( + animal_choices_for, + can_access_note_animal, + is_attachment_author, + timeline_for, +) +from ahc.apps.medical_notes.services.attachments import ( + AttachmentLimitExceeded, + delete_attachment, + download_attachment, + upload_attachment, +) +from ahc.apps.medical_notes.services.notes import create_note, next_route_for, update_note +from ahc.apps.medical_notes.views.mixins.user_animal_permisions import ( + AnimalDirectAccessRequiredMixin, + AttachmentAuthorRequiredMixin, + NoteAuthorRequiredMixin, +) -# append viewing other related animals on note_views and notelist -class CreateNoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class CreateNoteFormView(LoginRequiredMixin, AnimalDirectAccessRequiredMixin, FormView): template_name = "medical_notes/create.html" form_class = MedicalRecordForm def get_form_kwargs(self): kwargs = super().get_form_kwargs() - - query = ( - AnimalProfile.objects.filter(Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile)) - .exclude(id=self.kwargs.get("pk")) - .order_by("-creation_date") - ) - - animal_choices = [(animal.id, animal.full_name) for animal in query] - kwargs["animal_choices"] = animal_choices + kwargs["animal_choices"] = animal_choices_for(self.request.user.profile, exclude_id=self.kwargs.get("pk")) kwargs["type_of_event_param"] = self.request.GET.get("type_of_event") return kwargs @@ -51,43 +55,16 @@ def get_context_data(self, **kwargs): def form_valid(self, form): animal_id = self.kwargs.get("pk") animal = get_object_or_404(AnimalProfile, id=animal_id) - - new_note = form.save(commit=False) - new_note.animal = animal - new_note.author = self.request.user.profile - new_note.save() - form.save_m2m() - - # return super().form_valid(form) - type_of_event = form.cleaned_data.get("type_of_event") - - if type_of_event == "biometric_record": - medical_create_url = reverse("biometric_create", kwargs={"pk": animal_id, "note_id": new_note.id}) - return redirect(medical_create_url) - elif type_of_event == "diet_note": - medical_create_url = reverse("feeding_create", kwargs={"pk": new_note.id}) - return redirect(medical_create_url) - else: - full_timeline_url = reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) - return redirect(full_timeline_url) - - def test_func(self): - user = self.request.user.profile - - animal_id = self.kwargs.get("pk") - animal = get_object_or_404(AnimalProfile, id=animal_id) - - all_users = set(animal.allowed_users.all()) - all_users.add(animal.owner) - - return user in all_users + note = create_note(self.request.user.profile, animal, form) + url_name, kwargs = next_route_for(note, animal_id) + return redirect(reverse(url_name, kwargs=kwargs)) def get_success_url(self): animal_id = self.kwargs.get("pk") return reverse("animal_profile", kwargs={"pk": animal_id}) -class FullTimelineOfNotes(LoginRequiredMixin, UserPassesTestMixin, ListView): +class FullTimelineOfNotes(LoginRequiredMixin, AnimalDirectAccessRequiredMixin, ListView): model = MedicalRecord template_name = "medical_notes/full_timeline_of_notes.html" context_object_name = "notes" @@ -96,34 +73,23 @@ class FullTimelineOfNotes(LoginRequiredMixin, UserPassesTestMixin, ListView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - animal_id = self.kwargs.get("pk") - animal = get_object_or_404(AnimalProfile, id=animal_id) - query = MedicalRecord.objects.filter(animal=animal) - - type_of_event = self.request.GET.get("type_of_event") - if type_of_event: - query = query.filter(type_of_event=type_of_event) - - tag_name = self.request.GET.get("tag_name") - if tag_name: - query = query.filter(note_tags__slug=tag_name) - - query = query.prefetch_related("attachments") + animal = get_object_or_404(AnimalProfile, id=self.kwargs.get("pk")) + query = timeline_for( + animal, + type_of_event=self.request.GET.get("type_of_event"), + tag_name=self.request.GET.get("tag_name"), + ) - # view all auto-timestamps in user timezone + # Localise auto-timestamps to the user's current timezone (presentation layer) user_timezone = timezone.get_current_timezone() for record in query: record.date_creation = timezone.localtime(record.date_creation, user_timezone) record.date_updated = timezone.localtime(record.date_updated, user_timezone) - for attachment in record.attachments.all(): attachment.upload_date = timezone.localtime(attachment.upload_date, user_timezone) paginator = Paginator(list(query.order_by("-date_creation")), per_page=self.paginate_by) - page_number = self.request.GET.get("page") - - notes = paginator.get_page(page_number) - + notes = paginator.get_page(self.request.GET.get("page")) context["notes"] = notes upload_forms = [] @@ -132,9 +98,7 @@ def get_context_data(self, **kwargs): form.fields["medical_record_id"].initial = str(note.id) upload_forms.append(form) - notes_with_forms = zip(context["notes"], upload_forms, strict=False) - - context["notes"] = notes_with_forms + context["notes"] = zip(context["notes"], upload_forms, strict=False) return context @@ -143,44 +107,17 @@ def post(self, request, *args, **kwargs): if form.is_valid(): medical_record_id = form["medical_record_id"].value() medical_record = get_object_or_404(MedicalRecord, id=medical_record_id) - - attachments_count = MedicalRecordAttachment.objects.filter(medical_record=medical_record_id).count() - attanents_limit_per_note = settings.COUCH_DB_LIMIT_PER_NOTE - if attachments_count >= attanents_limit_per_note: - messages.error( - request, - f"Failed to upload. Maximum limit {attanents_limit_per_note} attachments per note is already reached.", + form.save(commit=False) + try: + upload_attachment( + medical_record=medical_record, + attachment_instance=form.instance, + uploaded_file=request.FILES["file"], ) - return redirect(request.path) - - with transaction.atomic(): - form.instance.medical_record = medical_record - form.save(commit=False) - - couch_connector = settings.COUCH_DB - - uploaded_file = request.FILES["file"] - file_reference_uuid = str(form.instance.id) - uploaded_file_name = uploaded_file.name - uploaded_file.seek(0) - blop_file = uploaded_file.read() - - couch_connector.save({"_id": file_reference_uuid, "name": uploaded_file_name}) - - doc_dict = couch_connector.get(file_reference_uuid) - couch_connector.put_attachment(doc_dict, blop_file, filename=uploaded_file_name) - - uploaded_file_name = uploaded_file.name - - form.instance.file_name = uploaded_file_name - form.instance.couch_id = file_reference_uuid - form.instance.file = None - form.save() - messages.success(request, "Attachment uploaded successfully.") - + except AttachmentLimitExceeded as exc: + messages.error(request, f"Failed to upload. {exc}") else: - print(form.errors) for _field, errors in form.errors.items(): messages.error(request, f"Failed to upload: {', '.join(errors)}") @@ -189,19 +126,8 @@ def post(self, request, *args, **kwargs): def render_to_response(self, context, **response_kwargs): return super().render_to_response(context, **response_kwargs) - def test_func(self): - user = self.request.user.profile - - animal_id = self.kwargs.get("pk") - animal = get_object_or_404(AnimalProfile, id=animal_id) - - all_users = set(animal.allowed_users.all()) - all_users.add(animal.owner) - - return user in all_users - -class EditNoteView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): +class EditNoteView(LoginRequiredMixin, NoteAuthorRequiredMixin, UpdateView): model = MedicalRecord form_class = MedicalRecordEditForm template_name = "medical_notes/edit.html" @@ -209,69 +135,31 @@ class EditNoteView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() + kwargs["animal_choices"] = animal_choices_for(self.request.user.profile) - query = AnimalProfile.objects.filter( - Q(owner=self.request.user.profile) | Q(allowed_users=self.request.user.profile) - ).order_by("-creation_date") - - animal_choices = [(animal.id, animal.full_name) for animal in query] - kwargs["animal_choices"] = animal_choices - - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, pk=note_id) - animal_id = note.animal.id - animal = get_object_or_404(AnimalProfile, id=animal_id) - kwargs["animal"] = animal + note = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + kwargs["animal"] = get_object_or_404(AnimalProfile, id=note.animal.id) return kwargs def form_valid(self, form): - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, id=note_id) - - if "animal" in form.cleaned_data: - note.animal = form.cleaned_data["animal"] - - note.save() - - additional_animals = form.cleaned_data.get("additional_animals") - note.additional_animals.set(additional_animals) - + note = get_object_or_404(MedicalRecord, id=self.kwargs.get("pk")) + update_note(note, form) return super().form_valid(form) def get_success_url(self): - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, pk=note_id) - animal_id = note.animal.id - return reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) - - def test_func(self): - user = self.request.user.profile + note = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + return reverse("full_timeline_of_notes", kwargs={"pk": note.animal.id}) - note_id = self.kwargs.get("pk") - note_author = get_object_or_404(MedicalRecord, id=note_id).author - return user == note_author - -class EditMedicalRecordAttachmentDescription(LoginRequiredMixin, UserPassesTestMixin, UpdateView): +class EditMedicalRecordAttachmentDescription(LoginRequiredMixin, AttachmentAuthorRequiredMixin, UpdateView): model = MedicalRecordAttachment fields = ["description"] template_name = "medical_notes/edit.html" def get_success_url(self): - attachment_id = self.kwargs.get("pk") - attachment = get_object_or_404(MedicalRecordAttachment, pk=attachment_id) - note = attachment.medical_record - animal_id = note.animal.id - return reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) - - def test_func(self): - user = self.request.user.profile - - attachment_id = self.kwargs.get("pk") - attachment = get_object_or_404(MedicalRecordAttachment, pk=attachment_id) - note_author = attachment.medical_record.author - return user == note_author + attachment = get_object_or_404(MedicalRecordAttachment, pk=self.kwargs.get("pk")) + return reverse("full_timeline_of_notes", kwargs={"pk": attachment.medical_record.animal.id}) class EditRelatedAnimalsView(EditNoteView): @@ -282,107 +170,51 @@ class EditRelatedAnimalsView(EditNoteView): def get_form_kwargs(self): kwargs = super().get_form_kwargs() - user = self.request.user.profile - author = MedicalRecord.objects.filter(author=user) - - kwargs["is_author"] = author.exists() - + kwargs["is_author"] = MedicalRecord.objects.filter(author=user).exists() return kwargs def test_func(self): - user = self.request.user.profile - - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, pk=note_id) - animal_id = note.animal.id - animal = get_object_or_404(AnimalProfile, id=animal_id) + note = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + return can_access_note_animal(self.request.user.profile, note) - all_users = set(animal.allowed_users.all()) - all_users.add(animal.owner) - return user in all_users - - -class DeleteMedicalRecordAttachment(LoginRequiredMixin, UserPassesTestMixin, DeleteView): +class DeleteMedicalRecordAttachment(LoginRequiredMixin, AttachmentAuthorRequiredMixin, DeleteView): model = MedicalRecordAttachment template_name = "medical_notes/delete_confirm.html" context_object_name = "note" def get_success_url(self): - attachment_id = self.kwargs.get("pk") - attachment = get_object_or_404(MedicalRecordAttachment, pk=attachment_id) - note = attachment.medical_record - animal_id = note.animal.id - return reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) + attachment = get_object_or_404(MedicalRecordAttachment, pk=self.kwargs.get("pk")) + return reverse("full_timeline_of_notes", kwargs={"pk": attachment.medical_record.animal.id}) - def form_valid(self, request, *args, **kwargs): + def form_valid(self, form): self.object = self.get_object() success_url = self.get_success_url() - couch_connector = settings.COUCH_DB - - couch_attachment_id = str(self.object.couch_id) - couch_connector.delete(couch_attachment_id) - - self.object.delete() - - # success_url = self.get_success_url() + delete_attachment(self.object) return redirect(success_url) - def test_func(self): - user = self.request.user.profile - - attachment_id = self.kwargs.get("pk") - attachment = get_object_or_404(MedicalRecordAttachment, pk=attachment_id) - note_author = attachment.medical_record.author - return user == note_author - -class DeleteNoteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): +class DeleteNoteView(LoginRequiredMixin, NoteAuthorRequiredMixin, DeleteView): model = MedicalRecord template_name = "medical_notes/delete_confirm.html" context_object_name = "note" - def test_func(self): - user = self.request.user.profile - - note_id = self.kwargs.get("pk") - note_author = get_object_or_404(MedicalRecord, id=note_id).author - - return user == note_author - def get_success_url(self): - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, pk=note_id) - animal_id = note.animal.id - return reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) + note = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + return reverse("full_timeline_of_notes", kwargs={"pk": note.animal.id}) class DownloadAttachmentView(LoginRequiredMixin, UserPassesTestMixin, View): - def test_func(self): - user = self.request.user.profile + """Download attachment by CouchDB reference id (URL kwarg: id, not pk).""" - attachment_couch_id = self.kwargs.get("id") - attachment = get_object_or_404(MedicalRecordAttachment, couch_id=attachment_couch_id) - note_author = attachment.medical_record.author - return user == note_author + def test_func(self): + attachment = get_object_or_404(MedicalRecordAttachment, couch_id=self.kwargs.get("id")) + return is_attachment_author(self.request.user.profile, attachment) def get(self, request, *args, **kwargs): - couch_connector = settings.COUCH_DB reference_id = self.kwargs.get("id") - _filename = self.kwargs.get("name") - - attachment = couch_connector.get(reference_id) - if not attachment: - print("Attachment not found") - raise Http404("Attachment not found") - - file_data = couch_connector.get_attachment(attachment, filename=attachment.get("name")) - if not file_data: - print("Attachment file not found") - raise Http404("Attachment file not found") - + file_data, file_name = download_attachment(reference_id) response = HttpResponse(file_data, content_type="application/octet-stream") - response["Content-Disposition"] = f'attachment; filename="{attachment.get("name")}"' - + response["Content-Disposition"] = f'attachment; filename="{file_name}"' return response diff --git a/src/ahc/apps/medical_notes/views/type_feeding_notes.py b/src/ahc/apps/medical_notes/views/type_feeding_notes.py index 13a4fc1..c585571 100644 --- a/src/ahc/apps/medical_notes/views/type_feeding_notes.py +++ b/src/ahc/apps/medical_notes/views/type_feeding_notes.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, reverse @@ -6,16 +5,27 @@ from django.views.generic.edit import FormView, UpdateView from django.views.generic.list import ListView -from ahc.apps.animals.models import Animal as AnimalProfile from ahc.apps.medical_notes.forms.type_feeding_notes import ( DietRecordForm, NotificationRecordForm, ) from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord from ahc.apps.medical_notes.models.type_feeding_notes import EmailNotification, FeedingNote +from ahc.apps.medical_notes.selectors import ( + feeding_notes_for, + is_note_author, + notifications_for_feednote, + notifications_for_mednote, +) +from ahc.apps.medical_notes.services.notifications import ( + create_email_notification, + delete_notification, + toggle_notification, +) +from ahc.apps.medical_notes.views.mixins.user_animal_permisions import AnimalAccessRequiredMixin -class DietRecordCreateView(LoginRequiredMixin, UserPassesTestMixin, FormView): +class DietRecordCreateView(LoginRequiredMixin, AnimalAccessRequiredMixin, FormView): template_name = "medical_notes/create.html" form_class = DietRecordForm @@ -26,9 +36,7 @@ def get_context_data(self, **kwargs): def form_valid(self, form): note_id = self.kwargs.get("pk") - related_note = get_object_or_404(MedicalRecord, id=note_id) - _animal = related_note.animal feeding_note = form.save(commit=False) feeding_note.related_note = related_note @@ -37,18 +45,6 @@ def form_valid(self, form): success_url = reverse("note_related_diets", kwargs={"pk": note_id}) return redirect(success_url) - def test_func(self): - user = self.request.user.profile - - note_id = self.kwargs.get("pk") - related_note = get_object_or_404(MedicalRecord, id=note_id) - animal = get_object_or_404(AnimalProfile, id=related_note.animal.id) - - all_users = set(animal.allowed_users.all()) - all_users.add(animal.owner) - - return user in all_users - class EditDietRecordView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = FeedingNote @@ -64,8 +60,7 @@ def get_context_data(self, **kwargs): def form_valid(self, form): form.save(commit=True) - email_notification_id = self.kwargs.get("pk") - email_notification = get_object_or_404(EmailNotification, id=email_notification_id) + email_notification = get_object_or_404(EmailNotification, id=self.kwargs.get("pk")) feeding_note = email_notification.related_note medical_record = feeding_note.related_note @@ -73,17 +68,12 @@ def form_valid(self, form): return redirect(success_url) def get_success_url(self): - note_id = self.kwargs.get("pk") - note = get_object_or_404(MedicalRecord, pk=note_id) - animal_id = note.animal.id - return reverse("full_timeline_of_notes", kwargs={"pk": animal_id}) + note = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + return reverse("full_timeline_of_notes", kwargs={"pk": note.animal.id}) def test_func(self): - user = self.request.user.profile - - note_id = self.kwargs.get("pk") - note_author = get_object_or_404(FeedingNote, id=note_id).related_note.author - return user == note_author + feeding_note = get_object_or_404(FeedingNote, id=self.kwargs.get("pk")) + return is_note_author(self.request.user.profile, feeding_note.related_note) class FeedingNoteListView(LoginRequiredMixin, UserPassesTestMixin, ListView): @@ -92,11 +82,8 @@ class FeedingNoteListView(LoginRequiredMixin, UserPassesTestMixin, ListView): context_object_name = "feeding_notes" def get_queryset(self): - record_id = self.kwargs.get("pk") - medical_record = get_object_or_404(MedicalRecord, pk=record_id) - queryset = FeedingNote.objects.filter(related_note=medical_record.id) - - return queryset + medical_record = get_object_or_404(MedicalRecord, pk=self.kwargs.get("pk")) + return feeding_notes_for(medical_record) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -105,11 +92,8 @@ def get_context_data(self, **kwargs): return context def test_func(self): - user = self.request.user.profile - - note_id = self.kwargs.get("pk") - note_author = get_object_or_404(MedicalRecord, id=note_id).author - return user == note_author + note = get_object_or_404(MedicalRecord, id=self.kwargs.get("pk")) + return is_note_author(self.request.user.profile, note) class CreateNotificationView(LoginRequiredMixin, UserPassesTestMixin, FormView): @@ -118,71 +102,38 @@ class CreateNotificationView(LoginRequiredMixin, UserPassesTestMixin, FormView): success_url = "/" def get_object(self): - note_id = self.kwargs.get("pk") - return get_object_or_404(FeedingNote, id=note_id) + return get_object_or_404(FeedingNote, id=self.kwargs.get("pk")) def form_valid(self, form): - note_id = self.kwargs.get("pk") - related_note = get_object_or_404(FeedingNote, id=note_id) - - set_by_user_timezone = form.instance.timezone - user_timezone_timestamp = form.instance.daily_timestamp - - server_timezone = settings.TIME_ZONE - + related_note = get_object_or_404(FeedingNote, id=self.kwargs.get("pk")) notify = form.save(commit=False) - days_of_week = [int(day) for day in self.request.POST.getlist("days_of_week")] - - processed_days_of_week = [False] * 7 - for i in days_of_week: - processed_days_of_week[i] = True - - notify.days_of_week = processed_days_of_week - notify.related_note = related_note - - notify_kwargs = {key: value for key, value in notify.__dict__.items() if not key.startswith("_")} - - EmailNotification.objects.create_notification(**notify_kwargs) - - print(type(set_by_user_timezone)) - print(type(user_timezone_timestamp)) - print(type(server_timezone)) - + create_email_notification( + related_note=related_note, + form_instance=notify, + days_of_week_raw=self.request.POST.getlist("days_of_week"), + ) return super().form_valid(form) def test_func(self): - return True + feeding_note = get_object_or_404(FeedingNote, id=self.kwargs.get("pk")) + from ahc.apps.medical_notes.selectors import can_access_note_animal + return can_access_note_animal(self.request.user.profile, feeding_note.related_note) -class NotificationListView(ListView): + +class NotificationListView(LoginRequiredMixin, ListView): template_name = "medical_notes/notification_list.html" context_object_name = "notifications" def get_queryset(self): feednote_pk = self.request.GET.get("feednote_pk") mednote_uuid = self.request.GET.get("mednote_uuid") - animal_uuid = self.request.GET.get("animal_uuid") - # TODO set url and test view with feednote_pk if feednote_pk: - email_notifications = EmailNotification.objects.filter(related_note=feednote_pk) - flattened_email_notifications = list(email_notifications) - - return flattened_email_notifications - - elif mednote_uuid: - feednotes_pk = FeedingNote.objects.filter(related_note=mednote_uuid) - email_notifications = EmailNotification.objects.filter(related_note__in=feednotes_pk).order_by( - "-last_modification" - ) - - email_notifications = email_notifications.order_by("-last_modification") - print([i.__str__() for i in email_notifications]) - - return email_notifications - - elif animal_uuid: - pass + return list(notifications_for_feednote(feednote_pk)) + if mednote_uuid: + return notifications_for_mednote(mednote_uuid) + return EmailNotification.objects.none() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -193,16 +144,10 @@ def get_context_data(self, **kwargs): def post(self, request, *args, **kwargs): pk = kwargs.get("pk") - notify = get_object_or_404(EmailNotification, pk=pk) - - notify.is_active = not notify.is_active - notify.save() - + toggle_notification(pk) return HttpResponseRedirect(request.META.get("HTTP_REFERER")) def delete(self, *args, **kwargs): pk = kwargs.get("pk") - notify = get_object_or_404(self.model, pk=pk) - notify.delete() - + delete_notification(pk) return HttpResponseRedirect(reverse_lazy("note_related_notifications")) diff --git a/src/ahc/apps/medical_notes/views/type_measurement_notes.py b/src/ahc/apps/medical_notes/views/type_measurement_notes.py index 0e48e33..90dfeca 100644 --- a/src/ahc/apps/medical_notes/views/type_measurement_notes.py +++ b/src/ahc/apps/medical_notes/views/type_measurement_notes.py @@ -1,18 +1,15 @@ +from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import get_object_or_404, redirect, reverse from django.views.generic.edit import FormView from ahc.apps.animals.models import Animal as AnimalProfile from ahc.apps.medical_notes.forms.type_measurement_notes import BiometricRecordForm from ahc.apps.medical_notes.models.type_basic_note import MedicalRecord -from ahc.apps.medical_notes.models.type_measurement_notes import ( - BiometricCustomRecords, - BiometricHeightRecords, - BiometricRecord, - BiometricWeightRecords, -) +from ahc.apps.medical_notes.services.biometrics import create_biometric_record +from ahc.apps.medical_notes.views.mixins.user_animal_permisions import AnimalDirectAccessRequiredMixin -class BiometricRecordCreateView(FormView): +class BiometricRecordCreateView(LoginRequiredMixin, AnimalDirectAccessRequiredMixin, FormView): template_name = "medical_notes/create.html" form_class = BiometricRecordForm @@ -22,47 +19,17 @@ def get_context_data(self, **kwargs): return context def form_valid(self, form): - record_type = form.cleaned_data["record_type"] animal_id = self.kwargs.get("pk") note_id = self.kwargs.get("note_id") animal = get_object_or_404(AnimalProfile, id=animal_id) - # print(f'{note_id=}') related_note = get_object_or_404(MedicalRecord, id=note_id) - # print(f'{related_note=}') - if record_type == "weight": - weight = form.cleaned_data["weight"] - unit = form.cleaned_data["weight_unit_to_present"] - weight_record = BiometricWeightRecords.objects.create(weight=weight, weight_unit_to_present=unit) - _biometric_record = BiometricRecord.objects.create( - animal=animal, - related_note=related_note, - weight_biometric_record=weight_record, - ) - elif record_type == "height": - height = form.cleaned_data["height"] - unit = form.cleaned_data["height_unit_to_present"] - height_record = BiometricHeightRecords.objects.create(height=height, height_unit_to_present=unit) - _biometric_record = BiometricRecord.objects.create( - animal=animal, - related_note=related_note, - height_biometric_record=height_record, - ) - else: - custom_name = form.cleaned_data["custom_name"] - custom_value = form.cleaned_data["custom_value"] - custom_unit = form.cleaned_data["custom_unit"] - custom_record = BiometricCustomRecords.objects.create( - record_name=custom_name, - record_value=custom_value, - record_unit=custom_unit, - ) - _biometric_record = BiometricRecord.objects.create( - animal=animal, - related_note=related_note, - custom_biometric_record=custom_record, - ) + create_biometric_record( + animal=animal, + related_note=related_note, + record_type=form.cleaned_data["record_type"], + data=form.cleaned_data, + ) - success_url = reverse("animal_profile", kwargs={"pk": animal_id}) - return redirect(success_url) + return redirect(reverse("animal_profile", kwargs={"pk": animal_id})) From 0c7a1d673b40e8685681ecc02c3f81b3fad36028 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 31 May 2026 00:23:27 +0200 Subject: [PATCH 30/31] chore(deps): remove unmaintained and misplaced dependencies --- TODO.md | 65 +++----- pyproject.toml | 8 +- .../homepage/templates/homepage/base.html | 5 +- src/ahc/apps/medical_notes/services/couch.py | 70 ++++++-- src/ahc/settings.py | 26 +-- static/css/custom_pico.css | 4 + static/css/custom_pico.css.map | 1 + uv.lock | 152 +----------------- 8 files changed, 93 insertions(+), 238 deletions(-) create mode 100644 static/css/custom_pico.css create mode 100644 static/css/custom_pico.css.map diff --git a/TODO.md b/TODO.md index 153baab..50a52e2 100644 --- a/TODO.md +++ b/TODO.md @@ -55,56 +55,39 @@ risk of changing form validation error messages. Extraction to a service layer is already started. Keep signal decisions (§1) in sync with this work to avoid duplicating logic. -## 6. Dependency migrations +## 6. Dependency migrations ✅ -### 6a. Remove `httplib2` (no-risk, easy) +### 6a. Remove `httplib2` ✅ -`httplib2` is declared as a direct dependency but is **never imported** anywhere in `src/`. -It is a legacy Python 2-era HTTP library superseded by `requests` (already in use). +Removed via `uv remove httplib2`. Pulled `pyparsing` with it (sole dependent). -Action: remove from `pyproject.toml`, run `uv lock`. +### 6b. Move `icecream` to dev dependencies ✅ -### 6b. Move `icecream` to dev dependencies +Moved to `[dependency-groups].dev`. Only consumer (`send_email_example` in +`celery_notifications/cron.py`) is dead code — never called. Production images +built with `uv sync --no-group dev` no longer install it. -`icecream` is in the main (production) dependency group but the only usage is a -**lazy import inside a debug branch** in `src/celery_notifications/cron.py:121`. +### 6c. Replace `pycouchdb` with `requests` ✅ -Action: move to `[dependency-groups] dev` in `pyproject.toml`. +- `src/ahc/settings.py` — replaced `pycouchdb.Server(...)` with plain config + vars (`COUCHDB_BASE_URL`, `COUCHDB_DB_NAME`, `COUCHDB_HOST`; host defaults to + `appendixes-db` for docker/k8s compatibility). +- `src/ahc/apps/medical_notes/services/couch.py` — adapter rewritten on + `requests.Session` + `_rev` handling; public method signatures unchanged. +- `uv remove pycouchdb` also dropped `chardet`. -### 6c. Replace `pycouchdb` with maintained HTTP client +### 6d. Remove `django-libsass` + `libsass` ✅ -`pycouchdb v1.16.0` has not been maintained since ~2019 and has no Python 3.13+ -test coverage. The entire interaction is already isolated in one adapter: +- `static/css/custom_pico.css` compiled once via `npx sass` and committed. +- `base.html` — `{% compress css %}` block replaced with a plain ``. +- `settings.py` — removed `COMPRESS_*`, `LIBSASS_*`, `compressor` from + `INSTALLED_APPS` and `STATICFILES_FINDERS`. +- `uv remove django-compressor django-libsass libsass django-appconf` also + dropped `rcssmin`, `rjsmin`. +- `cffi` kept — still required by `cryptography`. -- `src/ahc/settings.py` — `pycouchdb.Server(...)` connection init -- `src/ahc/apps/medical_notes/services/couch.py` — single thin adapter class - -Migration options (pick one): -- **Raw `requests`** — CouchDB exposes a plain HTTP/JSON API; no client lib needed. - Simplest: replaces `pycouchdb.Server` + `db[key]` with `requests.get/put` calls. -- **`httpx`** — async-capable drop-in; useful if the app ever adopts ASGI fully. - -Risk is low because ADR-08 scopes CouchDB to file/attachment storage only (no -complex queries). The adapter in `couch.py` is the single change surface. - -### 6d. Remove `django-libsass` + `libsass` by precompiling SCSS - -`django-libsass v0.9` (2021) drives `COMPRESS_PRECOMPILERS` to compile -`static/custom_pico.scss` at request time via `SassCompiler`. - -The SCSS file is small (17 lines — PicoCSS variable overrides + one `@import`). -Precompiling it once at build/deploy time removes two C-extension deps (`libsass`, -`cffi`) from the runtime and the runtime compilation overhead. - -Action: -1. Run `sass static/custom_pico.scss static/css/custom_pico.css` once (dart-sass - or node-sass; **not** a Python dep — run as a build step or commit the output). -2. Replace the `` in `base.html` from the compressor tag to a plain CSS link. -3. Remove `django-libsass`, `libsass`, `django-compressor`, `django-appconf` from - `pyproject.toml` (if compressor is not used for anything else). -4. Remove `COMPRESS_PRECOMPILERS` block from `settings.py`. - -Check `settings.py` for other `COMPRESS_*` entries before removing `django-compressor`. +**Follow-up:** upgrade PicoCSS and bring it in as a git submodule (the 6d SCSS +deprecation warnings originate in pico 1.5.9 internals). ### 6e. Monitor: transitive Python-2-era deps diff --git a/pyproject.toml b/pyproject.toml index f2bdd19..248bda0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ dependencies = [ "celery>=5.4", "redis>=5.0", "flower", - "pycouchdb", "discord", "python-decouple", "pillow>=11.0", @@ -20,21 +19,15 @@ dependencies = [ "django-crispy-forms", "crispy-bootstrap4", "django-bootstrap-modal-forms", - "django-compressor", - "django-libsass", - "libsass", - "django-appconf", "django-taggit", "django-timezone-field>=6.1", "tzdata", "python3-openid", "requests", "requests-oauthlib", - "httplib2", "python-dateutil", "pyjwt", "defusedxml", - "icecream", ] [dependency-groups] @@ -47,6 +40,7 @@ dev = [ "pytest", "pytest-django", "pytest-cov", + "icecream", ] [tool.uv] diff --git a/src/ahc/apps/homepage/templates/homepage/base.html b/src/ahc/apps/homepage/templates/homepage/base.html index a1d1cb3..0b85c7e 100644 --- a/src/ahc/apps/homepage/templates/homepage/base.html +++ b/src/ahc/apps/homepage/templates/homepage/base.html @@ -14,10 +14,7 @@ --> - {% load compress %} - {% compress css %} - - {% endcompress %} + {% if title %} diff --git a/src/ahc/apps/medical_notes/services/couch.py b/src/ahc/apps/medical_notes/services/couch.py index 52b65f3..7d76ced 100644 --- a/src/ahc/apps/medical_notes/services/couch.py +++ b/src/ahc/apps/medical_notes/services/couch.py @@ -1,43 +1,87 @@ """CouchDB attachment client (ADR-08: CouchDB is attachment/file storage only). -All raw pycouchdb interaction is isolated here. Views and other services +All raw HTTP interaction with CouchDB is isolated here. Views and other services interact with CouchDB exclusively through this client. """ from __future__ import annotations +import urllib.parse + +import requests from django.conf import settings class CouchAttachmentClient: - """Thin adapter over the pycouchdb database object stored in settings.COUCH_DB.""" + """Thin HTTP adapter over the CouchDB REST API. + + Reads connection parameters from ``settings.COUCHDB_*`` at instantiation + time. Pass explicit constructor arguments in tests to avoid touching Django + settings. + """ + + def __init__( + self, + base_url: str | None = None, + db_name: str | None = None, + auth: tuple[str, str] | None = None, + session: requests.Session | None = None, + ) -> None: + self._base_url = (base_url or settings.COUCHDB_BASE_URL).rstrip("/") + self._db_name = db_name or settings.COUCHDB_DB_NAME + self._auth = auth or (settings.COUCHDB_USER, settings.COUCHDB_PASSWORD) + self._session = session or requests.Session() - def __init__(self, db=None): - self._db = db or settings.COUCH_DB + def _doc_url(self, doc_id: str) -> str: + return f"{self._base_url}/{self._db_name}/{urllib.parse.quote(doc_id, safe='')}" def save_attachment(self, reference_uuid: str, file_name: str, blob: bytes) -> str: """Upload a file blob to CouchDB and return the reference UUID (couch_id). Creates a CouchDB document keyed by reference_uuid, then attaches the blob. """ - self._db.save({"_id": reference_uuid, "name": file_name}) - doc = self._db.get(reference_uuid) - self._db.put_attachment(doc, blob, filename=file_name) + r = self._session.put(self._doc_url(reference_uuid), json={"name": file_name}, auth=self._auth) + r.raise_for_status() + rev = r.json()["rev"] + + att_url = f"{self._doc_url(reference_uuid)}/{urllib.parse.quote(file_name, safe='')}?rev={rev}" + r2 = self._session.put( + att_url, + data=blob, + auth=self._auth, + headers={"Content-Type": "application/octet-stream"}, + ) + r2.raise_for_status() return reference_uuid def delete_attachment(self, couch_id: str) -> None: """Delete the CouchDB document (and its attachment) identified by couch_id.""" - self._db.delete(couch_id) + r = self._session.get(self._doc_url(couch_id), auth=self._auth) + if r.status_code == 404: + return + r.raise_for_status() + rev = r.json()["_rev"] + + d = self._session.delete(f"{self._doc_url(couch_id)}?rev={rev}", auth=self._auth) + d.raise_for_status() def get_attachment(self, reference_id: str) -> tuple[dict, bytes] | None: """Retrieve a CouchDB document and its attached file bytes. Returns (doc_dict, file_bytes) or None if the document does not exist. """ - doc = self._db.get(reference_id) - if not doc: + r = self._session.get(self._doc_url(reference_id), auth=self._auth) + if r.status_code == 404: return None - file_data = self._db.get_attachment(doc, filename=doc.get("name")) - if not file_data: + r.raise_for_status() + doc = r.json() + + name = doc.get("name") + a = self._session.get( + f"{self._doc_url(reference_id)}/{urllib.parse.quote(name, safe='')}", + auth=self._auth, + ) + if a.status_code == 404: return None - return doc, file_data + a.raise_for_status() + return doc, a.content diff --git a/src/ahc/settings.py b/src/ahc/settings.py index 2db41ef..ab6ef02 100644 --- a/src/ahc/settings.py +++ b/src/ahc/settings.py @@ -13,13 +13,11 @@ import sys from pathlib import Path -import pycouchdb from decouple import Csv, config _OFFLINE_COMMANDS = { "check", "collectstatic", - "compress", "crontab", "makemigrations", "migrate", @@ -72,7 +70,6 @@ def _skip_external_services() -> bool: "crispy_forms", "crispy_bootstrap4", "bootstrap_modal_forms", - "compressor", "taggit", "ahc.apps.homepage.apps.HomepageConfig", "ahc.apps.users.apps.UsersConfig", @@ -146,11 +143,9 @@ def _skip_external_services() -> bool: COUCHDB_USER = config("COUCHDB_USER") COUCHDB_PASSWORD = config("COUCHDB_PASSWORD") COUCHDB_PORT = config("COUCHDB_PORT") - - COUCH_SERVER = pycouchdb.Server( - f"http://{COUCHDB_USER}:{COUCHDB_PASSWORD}@appendixes-db:{COUCHDB_PORT}/", authmethod="basic" - ) - COUCH_DB = COUCH_SERVER.database("appendixes") + COUCHDB_HOST = config("COUCHDB_HOST", default="appendixes-db") + COUCHDB_DB_NAME = config("COUCHDB_DB_NAME", default="appendixes") + COUCHDB_BASE_URL = f"http://{COUCHDB_HOST}:{COUCHDB_PORT}" COUCH_DB_LIMIT_PER_NOTE = 5 @@ -199,14 +194,8 @@ def _skip_external_services() -> bool: STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", - "compressor.finders.CompressorFinder", ] -COMPRESS_ROOT = STATIC_ROOT -COMPRESS_PRECOMPILERS = (("text/x-scss", "django_libsass.SassCompiler"),) -COMPRESS_OFFLINE = True -LIBSASS_OUTPUT_STYLE = "compressed" - _staticfiles_backend = ( "django.contrib.staticfiles.storage.StaticFilesStorage" if _is_test_run() @@ -221,15 +210,6 @@ def _skip_external_services() -> bool: }, } -""" DOCUMENTATION TO CUSTOM SCSS: -https://picocss.com/docs/customization.html -https://www.accordbox.com/blog/how-use-scss-sass-your-django-project-python-way/ - -commands: -python manage.py collectstatic -python manage.py compress --force -""" - MEDIA_URL = "/media/" MEDIA_ROOT = BASE_DIR / "static" / "media" diff --git a/static/css/custom_pico.css b/static/css/custom_pico.css new file mode 100644 index 0000000..7772820 --- /dev/null +++ b/static/css/custom_pico.css @@ -0,0 +1,4 @@ +/*! + * Pico CSS v1.5.9 (https://picocss.com) + * Copyright 2019-2023 - Licensed under MIT + */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px}@media(min-width: 576px){:root{--font-size: 17px}}@media(min-width: 768px){:root{--font-size: 18px}}@media(min-width: 992px){:root{--font-size: 19px}}@media(min-width: 1200px){:root{--font-size: 20px}}:root{--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--grid-spacing-vertical: 0;--grid-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media(min-width: 576px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 2.5)}}@media(min-width: 768px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3)}}@media(min-width: 992px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3.5)}}@media(min-width: 1200px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 4)}}@media(min-width: 576px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media(min-width: 768px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}@media(min-width: 992px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.75)}}@media(min-width: 1200px){article{--block-spacing-horizontal: calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing)}@media(min-width: 576px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2.5);--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media(min-width: 768px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 3);--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}a{--text-decoration: none}a.secondary,a.contrast{--text-decoration: underline}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type=checkbox],[type=radio]{--border-width: 2px}[type=checkbox][role=switch]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme=light],:root:not([data-theme=dark]){--background-color: #fff;--color: hsl(205, 20%, 32%);--h1-color: hsl(205, 30%, 15%);--h2-color: rgb(35.38125, 50.628125, 61.51875);--h3-color: hsl(205, 25%, 23%);--h4-color: rgb(54.63375, 72.706875, 85.61625);--h5-color: hsl(205, 20%, 32%);--h6-color: rgb(152.64, 128.66, 51.46);--muted-color: hsl(205, 10%, 50%);--muted-border-color: hsl(205, 20%, 94%);--primary: #fdd835;--primary-hover: #fbc02d;--primary-focus: rgba(253, 216, 53, 0.125);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 20%, 32%);--secondary-focus: rgba(240, 173, 5, 0.125);--secondary-inverse: #fff;--contrast: hsl(205, 30%, 15%);--contrast-hover: #000;--contrast-focus: rgba(240, 173, 5, 0.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: rgb(83.83125, 57.871875, 37.29375);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: hsl(205, 14%, 68%);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #fbc02d;--form-element-disabled-border-color: hsl(205, 14%, 68%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211, 47, 47, 0.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67, 160, 71, 0.125);--switch-background-color: hsl(205, 16%, 77%);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #fbc02d;--range-active-border-color: hsl(205, 16%, 77%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgb(245.82, 247.605, 248.88);--code-background-color: hsl(205, 20%, 94%);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 40%, 50%);--code-property-color: hsl(185, 40%, 40%);--code-value-color: hsl(40, 20%, 50%);--code-comment-color: hsl(205, 14%, 68%);--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(26.775, 40.1625, 49.725, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(26.775, 40.1625, 49.725, 0.024), 0.0625rem 0.125rem 0.75rem rgba(26.775, 40.1625, 49.725, 0.03), 0.1125rem 0.225rem 1.35rem rgba(26.775, 40.1625, 49.725, 0.036), 0.2085rem 0.417rem 2.502rem rgba(26.775, 40.1625, 49.725, 0.04302), 0.5rem 1rem 6rem rgba(26.775, 40.1625, 49.725, 0.06), 0 0 0 0.0625rem rgba(26.775, 40.1625, 49.725, 0.015);--card-sectionning-background-color: rgb(250.41, 251.3025, 251.94);--dropdown-background-color: rgb(250.41, 251.3025, 251.94);--dropdown-border-color: rgb(243.82, 216.105, 143.88);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: hsl(205, 20%, 94%);--modal-overlay-background-color: rgba(251, 192, 45, 0.7);--progress-background-color: #fbc02d;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: rgb(16.734375, 25.1015625, 31.078125);--color: hsl(205, 16%, 77%);--h1-color: hsl(205, 20%, 94%);--h2-color: rgb(243.82, 216.105, 143.88);--h3-color: #fbc02d;--h4-color: rgb(218.983, 194.957, 125.367);--h5-color: hsl(205, 16%, 77%);--h6-color: rgb(174.471, 186.609, 195.279);--muted-color: hsl(205, 10%, 50%);--muted-border-color: rgb(31.078125, 45.3953125, 55.621875);--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253, 216, 53, 0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 10%, 50%);--secondary-focus: rgba(114.75, 129.625, 140.25, 0.25);--secondary-inverse: #fff;--contrast: hsl(205, 20%, 94%);--contrast-hover: #fff;--contrast-focus: rgba(114.75, 129.625, 140.25, 0.25);--contrast-inverse: #000;--mark-background-color: rgb(208.488, 194.152, 131.912);--mark-color: rgb(16.734375, 25.1015625, 31.078125);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: rgb(16.734375, 25.1015625, 31.078125);--form-element-border-color: rgb(54.63375, 72.706875, 85.61625);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: hsl(205, 25%, 23%);--form-element-disabled-border-color: hsl(205, 20%, 32%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56, 142, 60, 0.25);--switch-background-color: rgb(54.63375, 72.706875, 85.61625);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: rgb(35.38125, 50.628125, 61.51875);--range-active-border-color: hsl(205, 25%, 23%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(114.75, 129.625, 140.25, 0.05);--code-background-color: rgb(23.428125, 35.1421875, 43.509375);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 30%, 50%);--code-property-color: hsl(185, 30%, 50%);--code-value-color: hsl(40, 10%, 50%);--code-comment-color: rgb(152.64, 128.66, 51.46);--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: rgb(20.08125, 30.121875, 37.29375);--card-border-color: var(--card-background-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color: rgb(23.428125, 35.1421875, 43.509375);--dropdown-background-color: hsl(205, 30%, 15%);--dropdown-border-color: rgb(35.38125, 50.628125, 61.51875);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35.38125, 50.628125, 61.51875, 0.75);--modal-overlay-background-color: rgba(35.38125, 50.628125, 61.51875, 0.8);--progress-background-color: rgb(35.38125, 50.628125, 61.51875);--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme=dark]{--background-color: rgb(16.734375, 25.1015625, 31.078125);--color: hsl(205, 16%, 77%);--h1-color: hsl(205, 20%, 94%);--h2-color: rgb(243.82, 216.105, 143.88);--h3-color: #fbc02d;--h4-color: rgb(218.983, 194.957, 125.367);--h5-color: hsl(205, 16%, 77%);--h6-color: rgb(174.471, 186.609, 195.279);--muted-color: hsl(205, 10%, 50%);--muted-border-color: rgb(31.078125, 45.3953125, 55.621875);--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253, 216, 53, 0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 10%, 50%);--secondary-focus: rgba(114.75, 129.625, 140.25, 0.25);--secondary-inverse: #fff;--contrast: hsl(205, 20%, 94%);--contrast-hover: #fff;--contrast-focus: rgba(114.75, 129.625, 140.25, 0.25);--contrast-inverse: #000;--mark-background-color: rgb(208.488, 194.152, 131.912);--mark-color: rgb(16.734375, 25.1015625, 31.078125);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: rgb(16.734375, 25.1015625, 31.078125);--form-element-border-color: rgb(54.63375, 72.706875, 85.61625);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: hsl(205, 25%, 23%);--form-element-disabled-border-color: hsl(205, 20%, 32%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56, 142, 60, 0.25);--switch-background-color: rgb(54.63375, 72.706875, 85.61625);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: rgb(35.38125, 50.628125, 61.51875);--range-active-border-color: hsl(205, 25%, 23%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(114.75, 129.625, 140.25, 0.05);--code-background-color: rgb(23.428125, 35.1421875, 43.509375);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 30%, 50%);--code-property-color: hsl(185, 30%, 50%);--code-value-color: hsl(40, 10%, 50%);--code-comment-color: rgb(152.64, 128.66, 51.46);--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: rgb(20.08125, 30.121875, 37.29375);--card-border-color: var(--card-background-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color: rgb(23.428125, 35.1421875, 43.509375);--dropdown-background-color: hsl(205, 30%, 15%);--dropdown-border-color: rgb(35.38125, 50.628125, 61.51875);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35.38125, 50.628125, 61.51875, 0.75);--modal-overlay-background-color: rgba(35.38125, 50.628125, 61.51875, 0.8);--progress-background-color: rgb(35.38125, 50.628125, 61.51875);--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type=checkbox],[type=radio],[type=range]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media(min-width: 576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media(min-width: 768px){.container{max-width:700px}}@media(min-width: 992px){.container{max-width:920px}}@media(min-width: 1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media(min-width: 992px){.grid{grid-template-columns:repeat(auto-fit, minmax(0%, 1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing)*.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role=link]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition)}a:is([aria-current],:hover,:active,:focus),[role=link]:is([aria-current],:hover,:active,:focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role=link]:focus{--background-color: var(--primary-focus)}a.secondary,[role=link].secondary{--color: var(--secondary)}a.secondary:is([aria-current],:hover,:active,:focus),[role=link].secondary:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}a.secondary:focus,[role=link].secondary:focus{--background-color: var(--secondary-focus)}a.contrast,[role=link].contrast{--color: var(--contrast)}a.contrast:is([aria-current],:hover,:active,:focus),[role=link].contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}a.contrast:focus,[role=link].contrast:focus{--background-color: var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}hgroup,.headings{margin-bottom:var(--typography-spacing-vertical)}hgroup>*,.headings>*{margin-bottom:0}hgroup>*:last-child,.headings>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical)*.25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical)*.25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);border-inline-start:.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical)*.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}button,input[type=submit],input[type=button],input[type=reset],[role=button]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}button:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),[role=button]:is([aria-current],:hover,:active,:focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type=submit]:focus,input[type=button]:focus,input[type=reset]:focus,[role=button]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--primary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).secondary,input[type=reset]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}:is(button,input[type=submit],input[type=button],[role=button]).secondary:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover);--color: var(--secondary-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).secondary:focus,input[type=reset]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--secondary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).contrast{--background-color: var(--contrast);--border-color: var(--contrast);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:is([aria-current],:hover,:active,:focus){--background-color: var(--contrast-hover);--border-color: var(--contrast-hover);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--contrast-focus)}:is(button,input[type=submit],input[type=button],[role=button]).outline,input[type=reset].outline{--background-color: transparent;--color: var(--primary)}:is(button,input[type=submit],input[type=button],[role=button]).outline:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--background-color: transparent;--color: var(--primary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary,input[type=reset].outline{--color: var(--secondary)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast{--color: var(--contrast)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing)*.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--background-color: var(--form-element-active-background-color)}input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--border-color: var(--form-element-active-border-color)}input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type=submit],[type=button],[type=reset])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal) !important;padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color: var(--form-element-valid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important}:where(input,select,textarea)[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color: var(--form-element-invalid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:rgba(0,0,0,0)}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}:where(input,select,textarea,.grid)+small{display:block;width:100%;margin-top:calc(var(--spacing)*-0.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing)*.25)}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-0.125em;margin-right:.375em;margin-left:0;margin-inline-start:0;margin-inline-end:.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-right:.375em;margin-bottom:0;cursor:pointer}[type=checkbox]:indeterminate{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color);--color: var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type=checkbox][role=switch]:focus{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color)}[type=checkbox][role=switch]:checked{--background-color: var(--switch-checked-background-color);--border-color: var(--switch-checked-background-color)}[type=checkbox][role=switch]:before{display:block;width:calc(1.25em - var(--border-width)*2);height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:checked{background-image:none}[type=checkbox][role=switch]:checked::before{margin-left:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type=checkbox][aria-invalid=false],[type=checkbox]:checked[aria-invalid=false],[type=radio][aria-invalid=false],[type=radio]:checked[aria-invalid=false],[type=checkbox][role=switch][aria-invalid=false],[type=checkbox][role=switch]:checked[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}[type=checkbox][aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=radio][aria-invalid=true],[type=radio]:checked[aria-invalid=true],[type=checkbox][role=switch][aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--icon-position: 0.75rem;--icon-width: 1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width)*-1);margin-left:var(--icon-position);opacity:0}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}[type=file]{--color: var(--muted-color);padding:calc(var(--form-element-spacing-vertical)*.5) 0;border:0;border-radius:0;background:none}[type=file]::file-selector-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::file-selector-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-webkit-file-upload-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-ms-browse{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-ms-browse:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}[type=range]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]:hover,[type=range]:focus{--range-border-color: var(--range-active-border-color);--range-thumb-color: var(--range-thumb-hover-color)}[type=range]:active{--range-thumb-color: var(--range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--icon-search),var(--icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--icon-search),var(--icon-invalid)}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing)/2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}pre,code,kbd,samp{font-size:.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}pre,code,kbd{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem .5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:none;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none !important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary{transition:color var(--transition)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--spacing, 1rem)*.5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:none}details summary:focus:not([role=button]){color:var(--accordion-active-summary-color)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem*var(--line-height, 1.5));background-image:var(--icon-chevron-button)}details summary[role=button]:not(.outline).contrast::after{background-image:var(--icon-chevron-button-inverse)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>header,article>footer{margin-right:calc(var(--block-spacing-horizontal)*-1);margin-left:calc(var(--block-spacing-horizontal)*-1);padding:calc(var(--block-spacing-vertical)*.66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical)*-1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical)*-1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width: 0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;backdrop-filter:var(--modal-overlay-backdrop-filter);background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing)*2);overflow:auto}@media(min-width: 576px){dialog article{max-width:510px}}@media(min-width: 768px){dialog article{max-width:700px}}dialog article>header,dialog article>footer{padding:calc(var(--block-spacing-vertical)*.5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role=button]{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type){margin-left:calc(var(--spacing)*.5)}dialog article p:last-of-type{margin:0}dialog article .close{display:block;width:1rem;height:1rem;margin-top:calc(var(--block-spacing-vertical)*-0.5);margin-bottom:var(--typography-spacing-vertical);margin-left:auto;background-image:var(--icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;opacity:.5;transition:opacity var(--transition)}dialog article .close:is([aria-current],:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--scrollbar-width, 0px);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{backdrop-filter:none;background-color:rgba(0,0,0,0)}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal)*-1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal)*-1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical)*-1) calc(var(--nav-link-spacing-horizontal)*-1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizontal)*2);margin-inline-start:calc(var(--nav-link-spacing-horizontal)/2);content:"/";color:var(--muted-color);text-align:center}nav[aria-label=breadcrumb] a[aria-current]{background-color:rgba(0,0,0,0);color:inherit;text-decoration:none;pointer-events:none}nav [role=button]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside nav,aside ol,aside ul,aside li{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical)*.5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--spacing)*.5);overflow:hidden;border:0;border-radius:var(--border-radius);background-color:var(--progress-background-color);color:var(--progress-color)}progress::-webkit-progress-bar{border-radius:var(--border-radius);background:none}progress[value]::-webkit-progress-value{background-color:var(--progress-color)}progress::-moz-progress-bar{background-color:var(--progress-color)}@media(prefers-reduced-motion: no-preference){progress:indeterminate{background:var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:rgba(0,0,0,0)}progress:indeterminate::-moz-progress-bar{background-color:rgba(0,0,0,0)}}@media(prefers-reduced-motion: no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}details[role=list],li[role=list]{position:relative}details[role=list] summary+ul,li[role=list]>ul{display:flex;z-index:99;position:absolute;top:auto;right:0;left:0;flex-direction:column;margin:0;padding:0;border:var(--border-width) solid var(--dropdown-border-color);border-radius:var(--border-radius);border-top-right-radius:0;border-top-left-radius:0;background-color:var(--dropdown-background-color);box-shadow:var(--card-box-shadow);color:var(--dropdown-color);white-space:nowrap}details[role=list] summary+ul li,li[role=list]>ul li{width:100%;margin-bottom:0;padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);list-style:none}details[role=list] summary+ul li:first-of-type,li[role=list]>ul li:first-of-type{margin-top:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li:last-of-type,li[role=list]>ul li:last-of-type{margin-bottom:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li a,li[role=list]>ul li a{display:block;margin:calc(var(--form-element-spacing-vertical)*-0.5) calc(var(--form-element-spacing-horizontal)*-1);padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);overflow:hidden;color:var(--dropdown-color);text-decoration:none;text-overflow:ellipsis}details[role=list] summary+ul li a:hover,li[role=list]>ul li a:hover{background-color:var(--dropdown-hover-background-color)}details[role=list] summary::after,li[role=list]>a::after{display:block;width:1rem;height:calc(1rem*var(--line-height, 1.5));margin-inline-start:.5rem;float:right;transform:rotate(0deg);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}details[role=list]{padding:0;border-bottom:none}details[role=list] summary{margin-bottom:0}details[role=list] summary:not([role]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2);padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--form-element-border-color);border-radius:var(--border-radius);background-color:var(--form-element-background-color);color:var(--form-element-placeholder-color);line-height:inherit;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}details[role=list] summary:not([role]):active,details[role=list] summary:not([role]):focus{border-color:var(--form-element-active-border-color);background-color:var(--form-element-active-background-color)}details[role=list] summary:not([role]):focus{box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}details[role=list][open] summary{border-bottom-right-radius:0;border-bottom-left-radius:0}details[role=list][open] summary::before{display:block;z-index:1;position:fixed;top:0;right:0;bottom:0;left:0;background:none;content:"";cursor:default}nav details[role=list] summary,nav li[role=list] a{display:flex;direction:ltr}nav details[role=list] summary+ul,nav li[role=list]>ul{min-width:fit-content;border-radius:var(--border-radius)}nav details[role=list] summary+ul li a,nav li[role=list]>ul li a{border-radius:0}nav details[role=list] summary,nav details[role=list] summary:not([role]){height:auto;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}nav details[role=list][open] summary{border-radius:var(--border-radius)}nav details[role=list] summary+ul{margin-top:var(--outline-width);margin-inline-start:0}nav details[role=list] summary[role=link]{margin-bottom:calc(var(--nav-link-spacing-vertical)*-1);line-height:var(--line-height)}nav details[role=list] summary[role=link]+ul{margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-link-spacing-horizontal)*-1)}li[role=list]:hover>ul,li[role=list] a:active~ul,li[role=list] a:focus~ul{display:flex}li[role=list]>ul{display:none;margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal))}li[role=list]>a::after{background-image:var(--icon-chevron)}label>details[role=list]{margin-top:calc(var(--spacing)*.25);margin-bottom:var(--spacing)}[aria-busy=true]{cursor:progress}[aria-busy=true]:not(input,select,textarea)::before{display:inline-block;width:1em;height:1em;border:.1875em solid currentColor;border-radius:1em;border-right-color:rgba(0,0,0,0);content:"";vertical-align:text-bottom;vertical-align:-0.125em;animation:spinner .75s linear infinite;opacity:var(--loading-spinner-opacity)}[aria-busy=true]:not(input,select,textarea):not(:empty)::before{margin-right:calc(var(--spacing)*.5);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)*.5)}[aria-busy=true]:not(input,select,textarea):empty{text-align:center}button[aria-busy=true],input[type=submit][aria-busy=true],input[type=button][aria-busy=true],input[type=reset][aria-busy=true],a[aria-busy=true]{pointer-events:none}@keyframes spinner{to{transform:rotate(360deg)}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip][data-placement=top]::before,[data-tooltip][data-placement=top]::after,[data-tooltip]::before,[data-tooltip]::after{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%, -0.25rem);border-radius:var(--border-radius);background:var(--tooltip-background-color);content:attr(data-tooltip);color:var(--tooltip-color);font-style:normal;font-weight:var(--font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip][data-placement=top]::after,[data-tooltip]::after{padding:0;transform:translate(-50%, 0rem);border-top:.3rem solid;border-right:.3rem solid rgba(0,0,0,0);border-left:.3rem solid rgba(0,0,0,0);border-radius:0;background-color:rgba(0,0,0,0);content:"";color:var(--tooltip-background-color)}[data-tooltip][data-placement=bottom]::before,[data-tooltip][data-placement=bottom]::after{top:100%;bottom:auto;transform:translate(-50%, 0.25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%, -0.3rem);border:.3rem solid rgba(0,0,0,0);border-bottom:.3rem solid}[data-tooltip][data-placement=left]::before,[data-tooltip][data-placement=left]::after{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-0.25rem, -50%)}[data-tooltip][data-placement=left]:after{transform:translate(0.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-left:.3rem solid}[data-tooltip][data-placement=right]::before,[data-tooltip][data-placement=right]::after{top:50%;right:auto;bottom:auto;left:100%;transform:translate(0.25rem, -50%)}[data-tooltip][data-placement=right]:after{transform:translate(-0.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-right:.3rem solid}[data-tooltip]:focus::before,[data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{opacity:1}@media(hover: hover)and (pointer: fine){[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::before,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-top}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::after{animation-name:tooltip-caret-slide-top}[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::before,[data-tooltip][data-placement=bottom]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-bottom}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{animation-name:tooltip-caret-slide-bottom}[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::before,[data-tooltip][data-placement=left]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-left}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{animation-name:tooltip-caret-slide-left}[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::before,[data-tooltip][data-placement=right]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-right}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{animation-name:tooltip-caret-slide-right}}@keyframes tooltip-slide-top{from{transform:translate(-50%, 0.75rem);opacity:0}to{transform:translate(-50%, -0.25rem);opacity:1}}@keyframes tooltip-caret-slide-top{from{opacity:0}50%{transform:translate(-50%, -0.25rem);opacity:0}to{transform:translate(-50%, 0rem);opacity:1}}@keyframes tooltip-slide-bottom{from{transform:translate(-50%, -0.75rem);opacity:0}to{transform:translate(-50%, 0.25rem);opacity:1}}@keyframes tooltip-caret-slide-bottom{from{opacity:0}50%{transform:translate(-50%, -0.5rem);opacity:0}to{transform:translate(-50%, -0.3rem);opacity:1}}@keyframes tooltip-slide-left{from{transform:translate(0.75rem, -50%);opacity:0}to{transform:translate(-0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-left{from{opacity:0}50%{transform:translate(0.05rem, -50%);opacity:0}to{transform:translate(0.3rem, -50%);opacity:1}}@keyframes tooltip-slide-right{from{transform:translate(-0.75rem, -50%);opacity:0}to{transform:translate(0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-right{from{opacity:0}50%{transform:translate(-0.05rem, -50%);opacity:0}to{transform:translate(-0.3rem, -50%);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0, 0, 0, 0);position:absolute}a,area,button,input,label,select,summary,textarea,[tabindex]{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media(prefers-reduced-motion: reduce){*:not([aria-busy=true]),:not([aria-busy=true])::before,:not([aria-busy=true])::after{background-attachment:initial !important;animation-duration:1ms !important;animation-delay:-1ms !important;animation-iteration-count:1 !important;scroll-behavior:auto !important;transition-delay:0s !important;transition-duration:0s !important}}/*# sourceMappingURL=custom_pico.css.map */ diff --git a/static/css/custom_pico.css.map b/static/css/custom_pico.css.map new file mode 100644 index 0000000..4a98dc8 --- /dev/null +++ b/static/css/custom_pico.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["pico-1.5.9/scss/pico.scss","pico-1.5.9/scss/themes/default/_styles.scss","pico-1.5.9/scss/themes/default/_light.scss","pico-1.5.9/scss/themes/default.scss","pico-1.5.9/scss/themes/default/_dark.scss","pico-1.5.9/scss/layout/_document.scss","pico-1.5.9/scss/layout/_sectioning.scss","pico-1.5.9/scss/layout/_container.scss","pico-1.5.9/scss/layout/_section.scss","pico-1.5.9/scss/layout/_grid.scss","pico-1.5.9/scss/layout/_scroller.scss","pico-1.5.9/scss/content/_typography.scss","pico-1.5.9/scss/content/_embedded.scss","pico-1.5.9/scss/content/_button.scss","pico-1.5.9/scss/content/_form.scss","pico-1.5.9/scss/content/_form-checkbox-radio.scss","pico-1.5.9/scss/content/_form-alt-input-types.scss","pico-1.5.9/scss/content/_table.scss","pico-1.5.9/scss/content/_code.scss","pico-1.5.9/scss/content/_miscs.scss","pico-1.5.9/scss/components/_accordion.scss","pico-1.5.9/scss/components/_card.scss","pico-1.5.9/scss/components/_modal.scss","pico-1.5.9/scss/components/_nav.scss","pico-1.5.9/scss/components/_progress.scss","pico-1.5.9/scss/components/_dropdown.scss","pico-1.5.9/scss/utilities/_loading.scss","pico-1.5.9/scss/utilities/_tooltip.scss","pico-1.5.9/scss/utilities/_accessibility.scss","pico-1.5.9/scss/utilities/_reduce-motion.scss"],"names":[],"mappings":"CAAA;AAAA;AAAA;AAAA,GCCA,MAEE,4LAGA,mBACA,mBACA,kBAKI,yBAZN,MAaQ,mBAKF,yBAlBN,MAmBQ,mBAKF,yBAxBN,MAyBQ,mBAKF,0BA9BN,MA+BQ,mBA/BR,MAqCE,yBACA,oBACA,qBAGA,gBAGA,sCAGA,mDACA,2CAGE,2BACA,0CAIF,yCACA,wCAGA,qCACA,yCACA,oCACA,sCAGA,6CAGA,+BAGA,+CAWI,yBALJ,0CAMM,sDAKF,yBAXJ,0CAYM,oDAKF,yBAjBJ,0CAkBM,sDAKF,0BAvBJ,0CAwBM,oDAQF,yBAFJ,QAGM,yDAKF,yBARJ,QASM,wDAKF,yBAdJ,QAeM,yDAKF,0BApBJ,QAqBM,sDAMN,eAEE,mDACA,2CAGE,yBANJ,eAOM,qDACA,yDAKF,yBAbJ,eAcM,mDACA,wDAOR,EACE,wBAIE,uBAEE,6BAMN,MACE,qBAIF,kBAME,mBAGF,GACE,kBACA,oCAGF,GACE,qBACA,wCAGF,GACE,oBACA,uCAGF,GACE,qBACA,wCAGF,GACE,sBACA,yCAIF,6BAEE,oBAGF,6BACE,oBAMA,oCAEE,oBAIJ,uBACE,qBAIF,kBAIE,8MAKF,IACE,sBClPF,gDAEE,yBAGA,4BACA,+BACA,+CACA,+BACA,+CACA,+BACA,uCAGA,kCACA,yCAGA,mBACA,yBACA,2CACA,wBAGA,qBACA,sCACA,4CACA,0BAGA,+BACA,uBACA,2CACA,yBAGA,iCACA,iDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,6CACA,gDACA,mCACA,qDACA,oDACA,mDACA,iDACA,kDACA,yDACA,qCACA,6CACA,oDACA,6DACA,2CACA,kDACA,2DAGA,8CACA,uCACA,kDAGA,8BACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,oEAGA,4CACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,yCAGA,oDACA,8CACA,mDAMA,iDACA,+CACA,ycAQA,mEAGA,2DACA,sDACA,8CACA,+BACA,sDAGA,0DAGA,qCACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,sSACA,gTACA,wTACA,wVACA,4cACA,8YACA,sSACA,0VACA,qVACA,qSAGA,mBC3IF,oDACE,wBCfA,0DAGA,4BACA,+BACA,yCACA,oBACA,2CACA,+BACA,2CAGA,kCACA,4DAGA,mBACA,yBACA,0CACA,wBAGA,qBACA,sCACA,uDACA,0BAGA,+BACA,uBACA,sDACA,yBAGA,wDACA,oDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,uEACA,gEACA,mCACA,qDACA,6EACA,mDACA,iDACA,6DACA,yDACA,qCACA,6CACA,oDACA,4DACA,2CACA,kDACA,0DAGA,8DACA,uCACA,kDAGA,yDACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,2EAGA,+DACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,iDAGA,oDACA,iDACA,8CACA,mDAMA,4DACA,kDACA,yVAQA,2EAGA,gDACA,4DACA,8CACA,+BACA,6EAGA,2EAGA,gEACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,ySACA,gTACA,kTACA,wVACA,+cACA,8YACA,sSACA,6VACA,wVACA,qSAGA,mBDnIF,kBCtBE,0DAGA,4BACA,+BACA,yCACA,oBACA,2CACA,+BACA,2CAGA,kCACA,4DAGA,mBACA,yBACA,0CACA,wBAGA,qBACA,sCACA,uDACA,0BAGA,+BACA,uBACA,sDACA,yBAGA,wDACA,oDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,uEACA,gEACA,mCACA,qDACA,6EACA,mDACA,iDACA,6DACA,yDACA,qCACA,6CACA,oDACA,4DACA,2CACA,kDACA,0DAGA,8DACA,uCACA,kDAGA,yDACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,2EAGA,+DACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,iDAGA,oDACA,iDACA,8CACA,mDAMA,4DACA,kDACA,yVAQA,2EAGA,gDACA,4DACA,8CACA,+BACA,6EAGA,2EAGA,gEACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,ySACA,gTACA,kTACA,wVACA,+cACA,8YACA,sSACA,6VACA,wVACA,qSAGA,kBD9HF,mDAIE,4BEvBF,qBAGE,sBACA,4BAKF,iBAEE,wBACA,uBASF,cACE,0CACA,8BACA,sBACA,yCACA,mBACA,+BACA,2BACA,+BACA,+BACA,kCACA,yBACA,eACA,WCnCF,KACE,cAOF,KACE,WACA,SAEA,kCAGE,WACA,kBACA,iBAsCE,wCC7DJ,4BAEE,WACA,kBACA,iBACA,6BACA,4BAKE,yBAFJ,WAGM,gBACA,gBACA,gBAKF,yBAVJ,WAWM,iBAKF,yBAhBJ,WAiBM,iBAKF,0BAtBJ,WAuBM,kBChCR,QACE,kDCCE,+CACA,0CACA,aACA,0BACA,SAGE,yBARJ,MASM,yDAIJ,QACE,YCfN,OACE,cACA,SACA,UACA,gBAEA,kBACE,kCACA,yBCHJ,SAEE,mBAIF,QAEE,kBACA,gBACA,cACA,wBAEF,IACE,eAEF,IACE,WAMF,oDAUE,aACA,iDACA,mBACA,kBACA,+BACA,2BAKF,cAEE,wBACA,gCACA,aACA,yCACA,mBACA,uCAGE,qIAIF,gGACE,8BACA,6BAGF,0BACE,yCAKA,kCACE,0BAEA,oHACE,gCAGF,8CACE,2CAKJ,gCACE,yBAEA,kHACE,+BAGF,4CACE,0CAOR,kBAME,aACA,iDACA,mBACA,+BACA,2BACA,+BAGF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAKA,mFACE,8CAuBF,iBAEE,iDAEA,qBACE,gBAGF,2CACE,4BACA,qBACA,eACA,kBAMN,EACE,iDAIF,MACE,2BAIF,iBACE,gBACA,4BACA,oCACA,qBAEA,oBACE,2DAOF,+BACE,SACA,wDAIJ,MACE,kBAIF,KACE,uBACA,8CACA,wBACA,wBAIF,WACE,cACA,4CACA,uBACA,kBACA,wDACA,gEACA,uBAEA,kBACE,uDACA,qCAMJ,YACE,yBACA,qBACA,YAIF,IACE,uBACA,qBAIF,IACE,uBAIF,YACE,sCC5PF,0CACE,sBAIF,YAEE,qBAIF,sBACE,aACA,SAIF,eACE,kBAKF,IACE,eACA,YACA,kBAIF,wBACE,kBAIF,eACE,gBClCF,OACE,SACA,iBACA,oBACA,oBAIF,gDAIE,0BAMF,OACE,cACA,WACA,6BAGF,cACE,qBACA,qBAGF,6EAKE,mCACA,+BACA,gCACA,+DACA,oFAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,0RACE,yCACA,qCACA,qEACA,gCAGF,2GACE,sHASF,4FAEE,qCACA,iCACA,kCACA,eAEA,8KACE,2CACA,uCACA,kCAGF,wGACE,wHAMJ,yEACE,oCACA,gCACA,iCAEA,kHACE,0CACA,sCACA,iCAGF,+EACE,uHAMJ,kGAEE,gCACA,wBAEA,oLACE,gCACA,8BAKJ,4GAEE,0BAEA,8LACE,gCAKJ,iFACE,yBAEA,0HACE,+BA0BN,yMAGE,WACA,oBC1KF,+BAIE,SACA,eACA,+BACA,oBACA,uBAIF,MACE,iBAIF,OACE,oBAOF,OACE,eACA,UACA,cACA,mBAIF,SACE,cAIF,6BAEE,UAIF,wDAEE,YAKF,cACE,6BACA,oBAIF,yCACE,wBAKF,6BACE,0BACA,aAIF,mBACE,UACA,kBAIF,gBACE,aAIF,iBACE,gBAIF,aACE,aAIF,yBAEE,UACA,eAOF,qDACE,sGAOF,SACE,SACA,6BACA,UACA,SAIF,sBAEE,cACA,uCACA,8DAIF,wDAGE,WAIF,iFAGE,gBACA,oFAKF,sBAGE,yDACA,iDACA,mCACA,mBACA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BAGE,kIASF,0JACE,gEAOF,2IACE,wDAQF,0HACE,yEAKJ,sMAIE,kEACA,0DACA,6CACA,oBAME,qJAEI,+EAGA,oDACA,uEACA,oFAUF,wCACA,0BACA,4BAGF,2JACE,mCAGF,0JACE,qCAIJ,kDACE,uDAEA,qEAEI,yEACA,0FASN,iDACE,yDAEA,oEAEI,2EACA,4FAaF,sIACE,uCAOR,6HAKE,4CACA,UAIF,wDAGE,6BAMA,mBACE,SACA,+BAGF,8BACE,oEACA,oDACA,4DACA,yEACA,qCACA,wCACA,0BACA,4BAMA,wCACE,uCAaJ,0CACE,cACA,WACA,sCACA,6BACA,yBAMF,oCACE,oCCxVJ,6BAEE,wBACA,qBACA,gBACA,aACA,cACA,oBACA,oBACA,cACA,sBACA,yBACA,iCACA,kBACA,sBACA,eAEA,mDACE,aAGF,iKAGE,mCACA,+BACA,sCACA,2BACA,2BACA,4BAGF,yCACE,qBACA,oBACA,gBACA,eAMF,8BACE,mCACA,+BACA,mCACA,2BACA,2BACA,4BAKJ,aACE,kBAEA,4EAGE,2CACA,mBACA,sBAKJ,6BACE,mDACA,+CACA,6BAQA,MAJe,OAKf,OANgB,OAOhB,qDACA,cARgB,OAShB,yCACA,YAVgB,OAYhB,mCACE,mDACA,+CAGF,qCACE,2DACA,uDAGF,oCACE,cACA,2CACA,YACA,kBACA,8BACA,WAGE,kCAIJ,qCACE,sBAEA,6CACE,gDACA,wDAaJ,oQACE,uDAGF,8PACE,yDC3HF,2CAHE,UAOF,+BAPE,UAiBF,mCAJE,SACA,4CAOF,gCARE,SACA,4CAeF,4IACE,yBACA,mBACA,6DACA,kCACA,sDACA,uCACA,4BAIF,4EACE,kCAUF,sPACE,wBACA,wCACA,iCACA,UAIJ,sFAEE,iBAIF,YACE,4BACA,wDACA,SACA,gBACA,gBAoCA,kCAjCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,4DACE,2CACA,uCAQJ,wCArCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,kEACE,2CACA,uCAYJ,wBAzCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,kDACE,2CACA,uCAkBN,aAOE,wBACA,qBACA,gBACA,WACA,OARe,QASf,gBAeA,4CAXE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EASJ,+BAfE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EAaJ,wBAnBE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EAiCJ,mCAdE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAQJ,+BAlBE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAYJ,wBAtBE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAgBJ,sCAEE,uDACA,oDAGF,oBACE,qDAGA,0CACE,sBAGF,sCACE,sBAGF,+BACE,sBAQJ,8EACE,4EACA,mBACA,oCACA,yCACA,0BACA,4BAEA,4FAEI,uFAKF,6DAGF,kGACE,sDAGF,iGACE,wDAOJ,4CACE,wBACA,aAOE,gGACE,0CAEA,8GACE,6DC/PV,cACE,WACA,yBACA,iBACA,cAOF,MAEE,8CACA,kEACA,mBACA,+BACA,2BACA,gBACA,iBAKA,kBAEE,+DACA,gBAOA,yCACE,4DCnCN,kBAIE,iBACA,+BAIF,IACE,6BACA,cAMF,aAGE,mCACA,wCACA,wBACA,+BACA,oBAGF,SAEE,qBACA,sBAGF,IACE,cACA,6BACA,gBAEA,SACE,cACA,uBACA,gBACA,eACA,+BAOF,OACE,4BACA,+BAIF,OACE,iCACA,kBAIF,OACE,8BACA,qBAIF,QACE,gCACA,kBAKJ,IACE,kDACA,4BACA,wBC9EF,GACE,SACA,SACA,+CACA,cAIF,kBAGI,wBAQJ,OACE,qBC3BF,QACE,cACA,6BACA,8BACA,sEAEA,gBACE,iBACA,qBACA,eAEA,4BACE,2CANJ,gBAUI,mCAIF,wCACE,aAGF,wBACE,aAGF,kCACE,qBAIF,uBACE,cACA,WACA,YACA,kDACA,YACA,yBACA,qCACA,iCACA,0BACA,4BACA,WAGE,uCAIJ,sBACE,aAEA,yCACE,4CAKJ,6BACE,WACA,gBAGA,oCACE,0CACA,4CAOE,2DACE,oDASR,sBACE,mCAGE,8CACE,0CAIJ,6BACE,oBAQJ,0BACE,iBAEA,iCACE,WACA,gCC3GR,QACE,uCACA,sEACA,mCACA,wCACA,kCAEA,8BAEE,sDACA,qDACA,gFAEA,0DAGF,eACE,kDACA,4CACA,iEACA,6CACA,4CAGF,eACE,yCACA,qDACA,8DACA,gDACA,+CC7BJ,MACE,uBAGF,OACE,aACA,YACA,eACA,MACA,QACA,SACA,OACA,mBACA,uBACA,cACA,eACA,eACA,gBACA,uBACA,SACA,qDACA,uDACA,mBAGA,eACE,0CACA,cAGE,yBALJ,eAMM,iBAKF,yBAXJ,eAYM,iBAIJ,4CAEE,+EAKA,6BACE,SACA,2BACA,YAIJ,sBACE,iBAEA,oCACE,gBAEA,wDACE,oCAMJ,8BACE,SAMF,sBACE,cACA,WACA,YACA,oDACA,iDACA,iBACA,mCACA,2BACA,0BACA,4BACA,WAGE,qCAGF,+DACE,UAOR,sCAEE,aAMF,eACE,0CACA,gBACA,oBACA,kBAEA,sBACE,oBAUF,8GAEE,mBALiB,IAMjB,sCACA,yBAGF,mDACE,uBACA,6BAEA,2DACE,gBAfe,IAgBf,qBAMJ,0DAEE,mBACA,4BAIJ,yBACE,KACE,qBACA,gCAIJ,iBACE,KACE,4BACA,WC7JN,uBACE,WACA,YAOF,WAEE,aAGF,IACE,8BAEA,cAEE,mBACA,gBACA,UACA,gBAEA,0CACE,2DAEF,wCACE,4DAIJ,OACE,qBACA,SACA,kFAIA,SACE,aAIJ,0BACE,qBACA,6FAEA,4EACA,mCACA,qBAEA,mEACE,qBAKJ,2BACE,mBACA,sBAGE,mDACE,uDAIA,0DACE,kBACA,iDACA,+DACA,YACA,yBACA,kBAKN,2CACE,+BACA,cACA,qBACA,oBAKJ,kBACE,qBACA,oBACA,4EAMF,qCAIE,cAGF,SACE,2FAGA,WACE,cAIF,uBACE,eAWI,oEACE,aC3HZ,SACE,qBACA,wBAMF,SAEE,wBACA,qBAGA,qBACA,gBACA,WACA,aACA,sCACA,gBAGA,SACA,mCACA,kDAGA,4BAEA,+BACE,mCACA,gBAEF,wCACE,uCAEF,4BACE,uCAIF,8CACE,uBACE,oKAOA,oDAEA,sDACE,+BAEF,0CACE,gCAON,8CACE,iCACE,6BAKN,kCACE,GACE,2BAEF,KACE,6BCjFJ,iCAEE,kBAGF,+CAEE,aACA,WACA,kBACA,SACA,QACA,OACA,sBACA,SACA,UACA,8DACA,mCACA,0BACA,yBACA,kDACA,kCACA,4BACA,mBAEA,qDACE,WACA,gBACA,6FAEA,gBAEA,iFACE,yDAGF,+EACE,4DAGF,yDACE,cACA,uGAEA,6FAEA,gBACA,4BACA,qBACA,uBAEA,qEACE,wDASN,yDACE,cACA,WACA,0CACA,0BACA,YACA,uBACA,iCACA,0BACA,4BACA,WAKJ,mBACE,UACA,mBAGA,2BACE,gBAEA,uCACE,sGAIA,oFAEA,kEACA,mCACA,sDACA,4CACA,oBACA,eAGE,kIAKF,2FAEE,qDACA,6DAGF,6CACE,sEAMN,iCACE,6BACA,4BAEA,yCACE,cACA,UACA,eACA,MACA,QACA,SACA,OACA,gBACA,WACA,eAMN,mDAEE,aACA,cAGF,uDAEE,sBACA,mCAEA,iEACE,gBAMF,0EAEE,YACA,4EAGF,qCACE,mCAGF,kCACE,gCACA,sBAGF,0CACE,wDACA,+BAEA,6CACE,yEACA,gEASJ,0EAGE,aAGF,iBACE,aACA,yEACA,qGAKF,uBACE,qCAIJ,yBACE,oCACA,6BC7MF,iBACE,gBAMA,oDACE,qBACA,UACA,WACA,kCACA,kBACA,iCACA,WACA,2BACA,wBACA,uCACA,uCAIA,gEACE,qCACA,cACA,sBACA,0CAIJ,kDACE,kBAUF,iJACE,oBAKJ,mBACE,GACE,0BCnDJ,eACE,kBAEA,mCACE,yBACA,qBACA,YAGF,kIAIE,cACA,WACA,kBACA,YACA,SACA,qBACA,gBACA,oCACA,mCACA,2CACA,2BACA,2BACA,kBACA,+BACA,kBACA,qBACA,uBACA,mBACA,UACA,oBAIF,gEAEE,UACA,gCACA,uBACA,uCACA,sCACA,gBACA,+BACA,WACA,sCAIA,2FAEE,SACA,YACA,mCAGF,4CACE,mCACA,iCACA,0BAKF,uFAEE,QACA,WACA,YACA,UACA,oCAGF,0CACE,kCACA,iCACA,wBAKF,yFAEE,QACA,WACA,YACA,UACA,mCAGF,2CACE,mCACA,iCACA,yBAOF,kHAEE,UAQF,wCAKI,iTAEE,uBACA,iCAGF,uJACE,uCAOA,8MAEE,uBACA,oCAGF,sGACE,0CAQF,sMAEE,uBACA,kCAGF,kGACE,wCAQF,0MAEE,uBACA,mCAGF,oGACE,0CAMR,6BACE,KACE,mCACA,UAEF,GACE,oCACA,WAIJ,mCACE,KACE,UAEF,IACE,oCACA,UAEF,GACE,gCACA,WAIJ,gCACE,KACE,oCACA,UAEF,GACE,mCACA,WAIJ,sCACE,KACE,UAEF,IACE,mCACA,UAEF,GACE,mCACA,WAIJ,8BACE,KACE,mCACA,UAEF,GACE,oCACA,WAIJ,oCACE,KACE,UAEF,IACE,mCACA,UAEF,GACE,kCACA,WAIJ,+BACE,KACE,oCACA,UAEF,GACE,mCACA,WAIJ,qCACE,KACE,UAEF,IACE,oCACA,UAEF,GACE,mCACA,WCrQR,gBACE,eAIF,gCAEE,mBAIF,4BACE,gBAGF,wCACE,sBACA,kBAKF,6DASE,8BAMF,UACE,cCrCA,uCACE,qFAGE,yCACA,kCACA,gCACA,uCACA,gCACA,+BACA","file":"custom_pico.css"} \ No newline at end of file diff --git a/uv.lock b/uv.lock index 1a6dab0..c3d6059 100644 --- a/uv.lock +++ b/uv.lock @@ -98,21 +98,14 @@ dependencies = [ { name = "defusedxml" }, { name = "discord" }, { name = "django" }, - { name = "django-appconf" }, { name = "django-bootstrap-modal-forms" }, - { name = "django-compressor" }, { name = "django-crispy-forms" }, - { name = "django-libsass" }, { name = "django-taggit" }, { name = "django-timezone-field" }, { name = "djangorestframework" }, { name = "flower" }, - { name = "httplib2" }, - { name = "icecream" }, - { name = "libsass" }, { name = "pillow" }, { name = "psycopg", extra = ["binary"] }, - { name = "pycouchdb" }, { name = "pyjwt" }, { name = "python-dateutil" }, { name = "python-decouple" }, @@ -127,6 +120,7 @@ dependencies = [ dev = [ { name = "bandit" }, { name = "codespell" }, + { name = "icecream" }, { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -144,21 +138,14 @@ requires-dist = [ { name = "defusedxml" }, { name = "discord" }, { name = "django", specifier = ">=6.0,<6.1" }, - { name = "django-appconf" }, { name = "django-bootstrap-modal-forms" }, - { name = "django-compressor" }, { name = "django-crispy-forms" }, - { name = "django-libsass" }, { name = "django-taggit" }, { name = "django-timezone-field", specifier = ">=6.1" }, { name = "djangorestframework", specifier = ">=3.14" }, { name = "flower" }, - { name = "httplib2" }, - { name = "icecream" }, - { name = "libsass" }, { name = "pillow", specifier = ">=11.0" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2" }, - { name = "pycouchdb" }, { name = "pyjwt" }, { name = "python-dateutil" }, { name = "python-decouple" }, @@ -173,6 +160,7 @@ requires-dist = [ dev = [ { name = "bandit", extras = ["toml"] }, { name = "codespell" }, + { name = "icecream" }, { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -343,15 +331,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] -[[package]] -name = "chardet" -version = "5.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, -] - [[package]] name = "charset-normalizer" version = "3.4.7" @@ -622,18 +601,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/5b/1328f8b84fce040c404f76822bf8c57d254e368e8cbd8bd67ec2b26d75f5/django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0", size = 8368680, upload-time = "2026-05-05T13:54:33.532Z" }, ] -[[package]] -name = "django-appconf" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/a2/e58bec8d7941b914af52a67c35b5709eceed2caa2848f28437f1666ed668/django_appconf-1.2.0.tar.gz", hash = "sha256:15a88d60dd942d6059f467412fe4581db632ef03018a3c183fb415d6fc9e5cec", size = 16127, upload-time = "2025-11-08T15:46:27.304Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/e6/4c34d94dfb74bbcbc489606e61f1924933de30d22c593dd1f429f35fbd7f/django_appconf-1.2.0-py3-none-any.whl", hash = "sha256:b81bce5ef0ceb9d84df48dfb623a32235d941c78cc5e45dbb6947f154ea277f4", size = 6500, upload-time = "2025-11-08T15:46:25.957Z" }, -] - [[package]] name = "django-bootstrap-modal-forms" version = "3.0.5" @@ -646,21 +613,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/e7/84a437e7d413e14627b82d6f3478960d01e88b5e927f673cee536e6907ba/django_bootstrap_modal_forms-3.0.5-py3-none-any.whl", hash = "sha256:e56bbe05fb29c5aa9e0f3c0277b0d8363b81cc6c4e4aaf152cedea883edae58a", size = 29560, upload-time = "2024-09-28T13:39:53.645Z" }, ] -[[package]] -name = "django-compressor" -version = "4.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django" }, - { name = "django-appconf" }, - { name = "rcssmin" }, - { name = "rjsmin" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/e4/c6d87b1341d744ceafa85eeceb2adabb1c62b795b8207cbc580fb70df8f4/django_compressor-4.6.0.tar.gz", hash = "sha256:c7478feab98f3368780591f9ee28a433350f5277dd28811f7f710f5bc6dff3c0", size = 99735, upload-time = "2025-11-10T13:12:11.439Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/9d/9a0ba39f33574994e5b33aea55a68e8fad72b8dd923a82300e4e91774f59/django_compressor-4.6.0-py3-none-any.whl", hash = "sha256:6e7b21020a0d86272c5e37000c33accc4ebeb77394a3dd86d775a09aae7aade4", size = 96828, upload-time = "2025-11-10T13:12:10.001Z" }, -] - [[package]] name = "django-crispy-forms" version = "2.6" @@ -673,19 +625,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/e3/4c5915a732d6ab54da8871400852b67529518eedfb6b78ecf10bbccfcabb/django_crispy_forms-2.6-py3-none-any.whl", hash = "sha256:8ee0ae28b6b0ac41ff48a65944480c049fe8d1b0047086874fd7efabf4ec1374", size = 31479, upload-time = "2026-03-01T09:03:36.048Z" }, ] -[[package]] -name = "django-libsass" -version = "0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "django-compressor" }, - { name = "libsass" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/6c/fe7c95536eed569960daf139726c8f83eaf8c4ae01d908c22d94d60f31c2/django-libsass-0.9.tar.gz", hash = "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5", size = 6754, upload-time = "2021-07-08T14:16:55.346Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/ee/65935acc5a36c418fa17d5190a4aeb339cfdf98b6a93ca1c59134cf1e6aa/django_libsass-0.9-py3-none-any.whl", hash = "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97", size = 6572, upload-time = "2021-07-08T14:16:53.501Z" }, -] - [[package]] name = "django-taggit" version = "6.1.0" @@ -797,18 +736,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] -[[package]] -name = "httplib2" -version = "0.31.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyparsing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz", hash = "sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", size = 250800, upload-time = "2026-01-23T11:04:44.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl", hash = "sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", size = 91099, upload-time = "2026-01-23T11:04:42.78Z" }, -] - [[package]] name = "humanize" version = "4.15.0" @@ -875,19 +802,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/0f/834427d8c03ff1d7e867d3db3d176470c64871753252b21b4f4897d1fa45/kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93", size = 214219, upload-time = "2025-12-29T20:30:05.74Z" }, ] -[[package]] -name = "libsass" -version = "0.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/b4/ab091585eaa77299558e3289ca206846aefc123fb320b5656ab2542c20ad/libsass-0.23.0.tar.gz", hash = "sha256:6f209955ede26684e76912caf329f4ccb57e4a043fd77fe0e7348dd9574f1880", size = 316068, upload-time = "2024-01-06T18:53:05.404Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/13/fc1bea1de880ca935137183727c7d4dd921c4128fc08b8ddc3698ba5a8a3/libsass-0.23.0-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:34cae047cbbfc4ffa832a61cbb110f3c95f5471c6170c842d3fed161e40814dc", size = 1086783, upload-time = "2024-01-06T19:02:38.903Z" }, - { url = "https://files.pythonhosted.org/packages/55/2f/6af938651ff3aec0a0b00742209df1172bc297fa73531f292801693b7315/libsass-0.23.0-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:ea97d1b45cdc2fc3590cb9d7b60f1d8915d3ce17a98c1f2d4dd47ee0d9c68ce6", size = 982759, upload-time = "2024-01-06T19:02:41.331Z" }, - { url = "https://files.pythonhosted.org/packages/fd/5a/eb5b62641df0459a3291fc206cf5bd669c0feed7814dded8edef4ade8512/libsass-0.23.0-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4a218406d605f325d234e4678bd57126a66a88841cb95bee2caeafdc6f138306", size = 9444543, upload-time = "2024-01-06T19:02:43.191Z" }, - { url = "https://files.pythonhosted.org/packages/e5/fc/275783f5120970d859ae37d04b6a60c13bdec2aa4294b9dfa8a37b5c2513/libsass-0.23.0-cp38-abi3-win32.whl", hash = "sha256:31e86d92a5c7a551df844b72d83fc2b5e50abc6fbbb31e296f7bebd6489ed1b4", size = 775481, upload-time = "2024-01-06T19:02:46.05Z" }, - { url = "https://files.pythonhosted.org/packages/ef/20/caf3c7cf2432d85263119798c45221ddf67bdd7dae8f626d14ff8db04040/libsass-0.23.0-cp38-abi3-win_amd64.whl", hash = "sha256:a2ec85d819f353cbe807432d7275d653710d12b08ec7ef61c124a580a8352f3c", size = 872914, upload-time = "2024-01-06T19:02:47.61Z" }, -] - [[package]] name = "markdown-it-py" version = "4.2.0" @@ -1147,19 +1061,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, ] -[[package]] -name = "pycouchdb" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "chardet" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c1/b4/4f699a686a2ce14ab31cb17902693f2cf201ba51c3a6fb7aba210725c154/pycouchdb-1.16.0.tar.gz", hash = "sha256:309d71c3ce3f98bbee5731db00f514753438b81e6e7adefbb8c134312200a4f9", size = 11351, upload-time = "2024-05-29T10:00:11.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/63/b4397a7215c089c7951afb258069cc58a06788224f1bb6a0d4f976f2d476/pycouchdb-1.16.0-py3-none-any.whl", hash = "sha256:e26ce58f626fcabbe2f5b15b3ad2b89cdd3f6d666da673632037476d1191ab67", size = 12560, upload-time = "2024-05-29T10:00:09.31Z" }, -] - [[package]] name = "pycparser" version = "3.0" @@ -1187,15 +1088,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, ] -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - [[package]] name = "pytest" version = "9.0.3" @@ -1319,26 +1211,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] -[[package]] -name = "rcssmin" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/af/c9654b4f9b054ec163ed7cb20d8db0e5ae05e2e9ce99a4c11d91a2180b3f/rcssmin-1.2.2.tar.gz", hash = "sha256:806986eaf7414545edc28a1d29523e9560e49e151ff4a337d9d1f0271d6e1cc4", size = 587012, upload-time = "2025-10-12T10:48:08.932Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/40/9c4cb3133f6d4ddfbeada76988a10ff2a974706fd6fcbb97edd8c0f4cc76/rcssmin-1.2.2-cp314-cp314-manylinux1_i686.whl", hash = "sha256:540dd3aa586b5f8f4c4b90db37e6a31c04718cdf90dbe9bec43c3b4dd50519e7", size = 49032, upload-time = "2025-10-12T10:48:53.014Z" }, - { url = "https://files.pythonhosted.org/packages/07/84/a411a48fd4179a88c68a2ad3649b408fa7887a421d3435c10ae6f5724e3a/rcssmin-1.2.2-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:6ea38a38eec263858b70bed6715478dcfed7fbc5d63333a8c512631ee22baad9", size = 49497, upload-time = "2025-10-12T10:48:54.009Z" }, - { url = "https://files.pythonhosted.org/packages/a1/32/5663a71a9304e0c9f33b765264508229d026359cfff746e1d0a593d809ea/rcssmin-1.2.2-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:07dc7d352e8eb08de82fc4c545dd04f9f487466c8370051e0bee4eb1e4dc85d0", size = 50382, upload-time = "2025-10-12T10:48:55.079Z" }, - { url = "https://files.pythonhosted.org/packages/d7/28/e411eb191ffff7bd712f2eb0f691cb7ca514b1876d6bff2f5ae61359b8db/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cdccb0e08281f0dd5d463c16ec61a06bd1534de50206dc72918be3c10dcb82e5", size = 50962, upload-time = "2025-10-12T10:48:56.494Z" }, - { url = "https://files.pythonhosted.org/packages/fb/3f/cdb99526d294c5dd4b919dc4ef492b7bd11e08b585d15ec641dfb9423493/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2b6d5e2e2fd65738d57ef65aaaed2cff2288eccff7f704bf3d579e6f451cb60a", size = 52504, upload-time = "2025-10-12T10:48:57.886Z" }, - { url = "https://files.pythonhosted.org/packages/e8/60/a8183401fa64e93e1d52b2cdf275a2c11e0993f5f3162c573a67872b535d/rcssmin-1.2.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7018d4197713c7797d1a67ed47ab53d4706c2e9ed134123c30a47d389dda5386", size = 50561, upload-time = "2025-10-12T10:48:58.935Z" }, - { url = "https://files.pythonhosted.org/packages/47/5e/496d6c9c309e2fe79e6a69f25f7a6d18f545edb4ea3584f461b9f84b0d60/rcssmin-1.2.2-cp314-cp314t-manylinux1_i686.whl", hash = "sha256:0162c32ce946978edc834d4fba705ac5f9422d7f556f3264cc4fc67c7ee39171", size = 51214, upload-time = "2025-10-12T10:49:00.021Z" }, - { url = "https://files.pythonhosted.org/packages/5e/78/87da6706d5856ceee71421ba831d2f5d93c3e6865acfbb56ace8d54587cc/rcssmin-1.2.2-cp314-cp314t-manylinux1_x86_64.whl", hash = "sha256:f17dc92553a46412c49f972f0ab31088032b9482a9c421bc2d39691a5d8842aa", size = 51608, upload-time = "2025-10-12T10:49:01.422Z" }, - { url = "https://files.pythonhosted.org/packages/cd/6c/204b0262c11ac2da2b8df2d8fed76f1959273fbc8376450d0ac022d754b7/rcssmin-1.2.2-cp314-cp314t-manylinux2014_aarch64.whl", hash = "sha256:40c7dfba098bbd129d8c35dd8b604275585f9dc0496e5d17dbe7fd6b873b0233", size = 53349, upload-time = "2025-10-12T10:49:02.512Z" }, - { url = "https://files.pythonhosted.org/packages/c3/7b/9aae16756d3f33cbc512760ba3e69c3856a51aa293e463f2ca97760d1b1b/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d0197fab78ebbe33f5df9caf2572ef2d44bbe243a9130881a0c5c53ba03641fa", size = 53066, upload-time = "2025-10-12T10:49:03.589Z" }, - { url = "https://files.pythonhosted.org/packages/4e/18/b06fadfa9b85e486bb1571050217cb539c062d1ae4cd32b1a31c36f67fd4/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:19e53c58768369366fdaef00da59f275f724f229994ea885309df6ca368ff3c8", size = 54271, upload-time = "2025-10-12T10:49:04.735Z" }, - { url = "https://files.pythonhosted.org/packages/79/55/f29ce21f8e5a1f3c19d43b67b907268d227b7edcda2ca200ca0028734a5e/rcssmin-1.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8d3de1a870e00d157f3a7b1797498fdc09a3774629079572350f75783bb94b9a", size = 52423, upload-time = "2025-10-12T10:49:06.04Z" }, -] - [[package]] name = "redis" version = "7.4.0" @@ -1389,26 +1261,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] -[[package]] -name = "rjsmin" -version = "1.2.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/59/16/14288d309d0f42c6586440c47bf6ec1a880218f698f30293fa3782db4008/rjsmin-1.2.5.tar.gz", hash = "sha256:a3f8040b0273dec773e0e807e86a4d0a9535516c0a0a35aa1bb6de6e15bb1f09", size = 427399, upload-time = "2025-10-12T10:50:27.422Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/ed/b472d5a3fd7d63c016893f7d438e677901fea28089b5d30cd1a115bcc887/rjsmin-1.2.5-cp314-cp314-manylinux1_i686.whl", hash = "sha256:7096357ed596fdfe0acb750f8cbfca338f3c845cc12def3861e23ed811589d15", size = 31983, upload-time = "2025-10-12T10:51:11.361Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e8/e76fa527fde17fd08288e4efef25c0aba7979ed5740eeab7bdff507bdeba/rjsmin-1.2.5-cp314-cp314-manylinux1_x86_64.whl", hash = "sha256:4e80b05803749502995fe33b6f5fd589b51dc46e50d873baf0b515c8f6e7b668", size = 32002, upload-time = "2025-10-12T10:51:12.257Z" }, - { url = "https://files.pythonhosted.org/packages/87/6c/ee395ef8ee117ba2d158a23a9502bc4a706e02f63bfdf6d01b802ae6ee9a/rjsmin-1.2.5-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:b6d0bc092acc3f54ea63ec1dcb808edaac5e956141d89fd0d038e80de5322052", size = 32435, upload-time = "2025-10-12T10:51:13.147Z" }, - { url = "https://files.pythonhosted.org/packages/1a/78/c157d33aa6148f0e8c57bb91a41969e1a4aab929f3bb0a8d9ff3b5e21556/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1e2943259be7beafdcb0847c2a901f223bf9044bdfa8105e1be1ad67d6c47795", size = 32877, upload-time = "2025-10-12T10:51:14.545Z" }, - { url = "https://files.pythonhosted.org/packages/e9/49/6252145bf85d87c815aaf441c5efdf1ce918db5ab6e915cf6d0d99ca3969/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e0387568c27fb49e55c1d0dfc27b54fc63d04b7756b1fed9743078130262907f", size = 32957, upload-time = "2025-10-12T10:51:15.964Z" }, - { url = "https://files.pythonhosted.org/packages/15/7e/c321c047b1a2fb7fa5ac818c37c1a15d348e1c12a1148de8ca5192a83b8f/rjsmin-1.2.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8196f1ecb0dff6c8647d4622e496869e94f1be92567ea2e941aa18d49a1a4347", size = 32456, upload-time = "2025-10-12T10:51:16.885Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d7/2d190ce5ad10832df62edd4d9b1ae7092fd259ca58b39a1e202337f511a9/rjsmin-1.2.5-cp314-cp314t-manylinux1_i686.whl", hash = "sha256:9dd9f66568be9c8676278f140aa54102fab9af7feb59adf0c7a85bef49fe70df", size = 34115, upload-time = "2025-10-12T10:51:17.911Z" }, - { url = "https://files.pythonhosted.org/packages/76/ab/e7bcf261ede4cef7a0693927d7dcd1612bb59ba6c05191f58a92deec9f01/rjsmin-1.2.5-cp314-cp314t-manylinux1_x86_64.whl", hash = "sha256:5b8f72f7d96e5e1d30a33182cb39d4eb4516ddcd9b2f984813a9eefe66f8e180", size = 33977, upload-time = "2025-10-12T10:51:18.996Z" }, - { url = "https://files.pythonhosted.org/packages/a7/75/f1ff5f2199437b534204b40aa46c55c703489063cf7806c948a1a665575e/rjsmin-1.2.5-cp314-cp314t-manylinux2014_aarch64.whl", hash = "sha256:8c5906bd8830f616e992ad5e7277d0ea12c530110da188b2b9da23e9524a7cbc", size = 34604, upload-time = "2025-10-12T10:51:20.031Z" }, - { url = "https://files.pythonhosted.org/packages/d2/dc/acd463d88c56476cc683f1c6cce893c590007dccd390747e824b8e923d63/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8207bac0d3bab7791fd667f0863b5f32e51047845179b94b28c716e6514a9234", size = 34775, upload-time = "2025-10-12T10:51:21.364Z" }, - { url = "https://files.pythonhosted.org/packages/ce/56/e6f61718d1c36e646aabe552ad1f8f77744a4c57524eaa782b5b44eba220/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1e3ab93a51d7581ba0a3b6a383df2929b86d9d55f9516764678f9b4e409826e8", size = 34682, upload-time = "2025-10-12T10:51:22.755Z" }, - { url = "https://files.pythonhosted.org/packages/00/f3/37a4672ddb1307eb57d9b54ba89a48f483a04a63cac4e1471fdb4cba76e6/rjsmin-1.2.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47dad1732a2c4779bdc76d5b3183fdf2ec27838f31071fa9dfcc79483d3480e2", size = 34161, upload-time = "2025-10-12T10:51:23.761Z" }, -] - [[package]] name = "ruff" version = "0.15.14" From 95ffef07582f74b19d3a5f2797a2c25d787310a0 Mon Sep 17 00:00:00 2001 From: Cybernetic-Ransomware <71835339+Cybernetic-Ransomware@users.noreply.github.com> Date: Sun, 31 May 2026 01:36:59 +0200 Subject: [PATCH 31/31] fix: resolve docker startup failures and Django template partial error --- .env.template | 5 + TODO.md | 8 +- docker/.dockerignore | 9 - docker/Dockerfile-couchdb | 7 +- docker/Dockerfile-couchdb.dockerignore | 5 + docker/Dockerfile-queue | 15 +- docker/Dockerfile-queue.dockerignore | 45 + docker/Dockerfile-web | 14 +- docker/Dockerfile-web.dockerignore | 44 + docker/docker-compose.yml | 72 +- justfile | 8 +- .../templates/animals/all_animals_stable.html | 2 +- .../homepage/templates/homepage/base.html | 7 +- .../homepage/templates/homepage/homepage.html | 4 +- static/css/custom_pico.css | 19 +- static/css/custom_pico.css.map | 1 - static/css/pico-1.5.9/.github/CONTRIBUTING.md | 22 - .../.github/ISSUE_TEMPLATE/bug_report.md | 21 - .../.github/ISSUE_TEMPLATE/config.yml | 8 - static/css/pico-1.5.9/.github/examples.jpg | Bin 133531 -> 0 bytes static/css/pico-1.5.9/.github/logo.svg | 1 - static/css/pico-1.5.9/.gitignore | 5 - static/css/pico-1.5.9/LICENSE.md | 21 - static/css/pico-1.5.9/README.md | 212 - static/css/pico-1.5.9/composer.json | 30 - static/css/pico-1.5.9/css/pico.classless.css | 2498 ----- .../css/pico-1.5.9/css/pico.classless.css.map | 1 - .../css/pico-1.5.9/css/pico.classless.min.css | 5 - .../pico-1.5.9/css/pico.classless.min.css.map | 1 - static/css/pico-1.5.9/css/pico.css | 2665 ------ static/css/pico-1.5.9/css/pico.css.map | 1 - .../pico-1.5.9/css/pico.fluid.classless.css | 2468 ----- .../css/pico.fluid.classless.css.map | 1 - .../css/pico.fluid.classless.min.css | 5 - .../css/pico.fluid.classless.min.css.map | 1 - static/css/pico-1.5.9/css/pico.min.css | 5 - static/css/pico-1.5.9/css/pico.min.css.map | 1 - static/css/pico-1.5.9/css/pico.slim.css | 1294 --- static/css/pico-1.5.9/css/pico.slim.css.map | 1 - static/css/pico-1.5.9/css/pico.slim.min.css | 8 - .../css/pico-1.5.9/css/pico.slim.min.css.map | 1 - static/css/pico-1.5.9/css/postcss.config.js | 12 - static/css/pico-1.5.9/css/themes/default.css | 526 -- .../css/pico-1.5.9/css/themes/default.css.map | 1 - .../css/pico-1.5.9/css/themes/default.min.css | 2 - .../pico-1.5.9/css/themes/default.min.css.map | 1 - static/css/pico-1.5.9/docs/accordions.html | 30 - static/css/pico-1.5.9/docs/buttons.html | 6 - static/css/pico-1.5.9/docs/cards.html | 5 - static/css/pico-1.5.9/docs/classless.html | 29 - static/css/pico-1.5.9/docs/containers.html | 3 - static/css/pico-1.5.9/docs/css/pico.docs.css | 508 - .../css/pico-1.5.9/docs/css/pico.docs.css.map | 1 - .../css/pico-1.5.9/docs/css/pico.docs.min.css | 4 - .../pico-1.5.9/docs/css/pico.docs.min.css.map | 1 - static/css/pico-1.5.9/docs/customization.html | 59 - static/css/pico-1.5.9/docs/dropdowns.html | 157 - static/css/pico-1.5.9/docs/forms.html | 108 - static/css/pico-1.5.9/docs/grid.html | 6 - static/css/pico-1.5.9/docs/index.html | 14 - static/css/pico-1.5.9/docs/js/commons.js | 21 - static/css/pico-1.5.9/docs/js/commons.min.js | 1 - .../css/pico-1.5.9/docs/js/customization.js | 14 - .../pico-1.5.9/docs/js/customization.min.js | 43 - static/css/pico-1.5.9/docs/js/grid.js | 100 - static/css/pico-1.5.9/docs/js/grid.min.js | 15 - static/css/pico-1.5.9/docs/js/modal.js | 95 - static/css/pico-1.5.9/docs/js/modal.min.js | 1 - .../pico-1.5.9/docs/js/src/color-picker.js | 173 - .../docs/js/src/material-design-colors.js | 303 - .../pico-1.5.9/docs/js/src/theme-switcher.js | 95 - .../docs/js/src/toggle-navigation.js | 42 - static/css/pico-1.5.9/docs/loading.html | 2 - static/css/pico-1.5.9/docs/modal.html | 87 - static/css/pico-1.5.9/docs/navs.html | 34 - static/css/pico-1.5.9/docs/progress.html | 1 - static/css/pico-1.5.9/docs/rtl.html | 4 - static/css/pico-1.5.9/docs/scroller.html | 5 - .../docs/scss/components/_modal.scss | 34 - .../pico-1.5.9/docs/scss/components/_nav.scss | 66 - .../docs/scss/components/_theme-switcher.scss | 71 - .../pico-1.5.9/docs/scss/content/_code.scss | 69 - .../docs/scss/content/_typography.scss | 19 - .../pico-1.5.9/docs/scss/layout/_aside.scss | 131 - .../docs/scss/layout/_document.scss | 7 - .../docs/scss/layout/_documentation.scss | 137 - .../pico-1.5.9/docs/scss/layout/_main.scss | 49 - .../css/pico-1.5.9/docs/scss/pico.docs.scss | 24 - .../pico-1.5.9/docs/scss/themes/_docs.scss | 25 - .../docs/scss/themes/docs/_dark.scss | 9 - .../docs/scss/themes/docs/_icons.scss | 9 - .../docs/scss/themes/docs/_light.scss | 11 - static/css/pico-1.5.9/docs/src/_footer.html | 4 - static/css/pico-1.5.9/docs/src/_head.html | 8 - static/css/pico-1.5.9/docs/src/_nav.html | 23 - static/css/pico-1.5.9/docs/src/_sidebar.html | 74 - .../css/pico-1.5.9/docs/src/accordions.html | 98 - static/css/pico-1.5.9/docs/src/buttons.html | 77 - static/css/pico-1.5.9/docs/src/cards.html | 48 - static/css/pico-1.5.9/docs/src/classless.html | 84 - .../css/pico-1.5.9/docs/src/containers.html | 74 - .../pico-1.5.9/docs/src/customization.html | 131 - static/css/pico-1.5.9/docs/src/dropdowns.html | 379 - static/css/pico-1.5.9/docs/src/forms.html | 272 - static/css/pico-1.5.9/docs/src/grid.html | 67 - static/css/pico-1.5.9/docs/src/index.html | 59 - static/css/pico-1.5.9/docs/src/loading.html | 51 - static/css/pico-1.5.9/docs/src/modal.html | 206 - static/css/pico-1.5.9/docs/src/navs.html | 157 - static/css/pico-1.5.9/docs/src/progress.html | 48 - static/css/pico-1.5.9/docs/src/rtl.html | 39 - static/css/pico-1.5.9/docs/src/scroller.html | 95 - static/css/pico-1.5.9/docs/src/tables.html | 177 - static/css/pico-1.5.9/docs/src/themes.html | 66 - static/css/pico-1.5.9/docs/src/tooltips.html | 65 - .../css/pico-1.5.9/docs/src/typography.html | 211 - .../pico-1.5.9/docs/src/we-love-classes.html | 38 - static/css/pico-1.5.9/docs/tables.html | 177 - static/css/pico-1.5.9/docs/themes.html | 66 - static/css/pico-1.5.9/docs/tooltips.html | 65 - static/css/pico-1.5.9/docs/typography.html | 211 - .../css/pico-1.5.9/docs/we-love-classes.html | 38 - static/css/pico-1.5.9/package-lock.json | 8167 ----------------- static/css/pico-1.5.9/package.json | 105 - static/css/pico-1.5.9/scss/_functions.scss | 4 - static/css/pico-1.5.9/scss/_variables.scss | 69 - .../scss/components/_accordion.scss | 116 - .../css/pico-1.5.9/scss/components/_card.scss | 36 - .../pico-1.5.9/scss/components/_dropdown.scss | 213 - .../pico-1.5.9/scss/components/_modal.scss | 169 - .../css/pico-1.5.9/scss/components/_nav.scss | 141 - .../pico-1.5.9/scss/components/_progress.scss | 89 - .../css/pico-1.5.9/scss/content/_button.scss | 183 - static/css/pico-1.5.9/scss/content/_code.scss | 91 - .../pico-1.5.9/scss/content/_embedded.scss | 48 - .../scss/content/_form-alt-input-types.scss | 272 - .../scss/content/_form-checkbox-radio.scss | 138 - static/css/pico-1.5.9/scss/content/_form.scss | 352 - .../css/pico-1.5.9/scss/content/_miscs.scss | 33 - .../css/pico-1.5.9/scss/content/_table.scss | 50 - .../pico-1.5.9/scss/content/_typography.scss | 264 - .../pico-1.5.9/scss/layout/_container.scss | 42 - .../css/pico-1.5.9/scss/layout/_document.scss | 48 - static/css/pico-1.5.9/scss/layout/_grid.scss | 24 - .../css/pico-1.5.9/scss/layout/_scroller.scss | 16 - .../css/pico-1.5.9/scss/layout/_section.scss | 8 - .../pico-1.5.9/scss/layout/_sectioning.scss | 70 - .../css/pico-1.5.9/scss/pico.classless.scss | 13 - .../pico-1.5.9/scss/pico.fluid.classless.scss | 16 - static/css/pico-1.5.9/scss/pico.scss | 43 - static/css/pico-1.5.9/scss/pico.slim.scss | 47 - static/css/pico-1.5.9/scss/postcss.config.js | 9 - .../css/pico-1.5.9/scss/themes/default.scss | 37 - .../scss/themes/default/_colors.scss | 65 - .../pico-1.5.9/scss/themes/default/_dark.scss | 159 - .../scss/themes/default/_light.scss | 159 - .../scss/themes/default/_styles.scss | 247 - .../scss/utilities/_accessibility.scss | 52 - .../pico-1.5.9/scss/utilities/_loading.scss | 58 - .../scss/utilities/_reduce-motion.scss | 27 - .../pico-1.5.9/scss/utilities/_tooltip.scss | 278 - static/css/pico-2.1.1/pico.yellow.min.css | 4 + static/custom_pico.scss | 16 - templates/partials/animal_card.html | 2 - 164 files changed, 209 insertions(+), 27590 deletions(-) delete mode 100644 docker/.dockerignore create mode 100644 docker/Dockerfile-couchdb.dockerignore create mode 100644 docker/Dockerfile-queue.dockerignore create mode 100644 docker/Dockerfile-web.dockerignore delete mode 100644 static/css/custom_pico.css.map delete mode 100644 static/css/pico-1.5.9/.github/CONTRIBUTING.md delete mode 100644 static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml delete mode 100644 static/css/pico-1.5.9/.github/examples.jpg delete mode 100644 static/css/pico-1.5.9/.github/logo.svg delete mode 100644 static/css/pico-1.5.9/.gitignore delete mode 100644 static/css/pico-1.5.9/LICENSE.md delete mode 100644 static/css/pico-1.5.9/README.md delete mode 100644 static/css/pico-1.5.9/composer.json delete mode 100644 static/css/pico-1.5.9/css/pico.classless.css delete mode 100644 static/css/pico-1.5.9/css/pico.classless.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.classless.min.css delete mode 100644 static/css/pico-1.5.9/css/pico.classless.min.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.css delete mode 100644 static/css/pico-1.5.9/css/pico.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.fluid.classless.css delete mode 100644 static/css/pico-1.5.9/css/pico.fluid.classless.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.fluid.classless.min.css delete mode 100644 static/css/pico-1.5.9/css/pico.fluid.classless.min.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.min.css delete mode 100644 static/css/pico-1.5.9/css/pico.min.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.slim.css delete mode 100644 static/css/pico-1.5.9/css/pico.slim.css.map delete mode 100644 static/css/pico-1.5.9/css/pico.slim.min.css delete mode 100644 static/css/pico-1.5.9/css/pico.slim.min.css.map delete mode 100644 static/css/pico-1.5.9/css/postcss.config.js delete mode 100644 static/css/pico-1.5.9/css/themes/default.css delete mode 100644 static/css/pico-1.5.9/css/themes/default.css.map delete mode 100644 static/css/pico-1.5.9/css/themes/default.min.css delete mode 100644 static/css/pico-1.5.9/css/themes/default.min.css.map delete mode 100644 static/css/pico-1.5.9/docs/accordions.html delete mode 100644 static/css/pico-1.5.9/docs/buttons.html delete mode 100644 static/css/pico-1.5.9/docs/cards.html delete mode 100644 static/css/pico-1.5.9/docs/classless.html delete mode 100644 static/css/pico-1.5.9/docs/containers.html delete mode 100644 static/css/pico-1.5.9/docs/css/pico.docs.css delete mode 100644 static/css/pico-1.5.9/docs/css/pico.docs.css.map delete mode 100644 static/css/pico-1.5.9/docs/css/pico.docs.min.css delete mode 100644 static/css/pico-1.5.9/docs/css/pico.docs.min.css.map delete mode 100644 static/css/pico-1.5.9/docs/customization.html delete mode 100644 static/css/pico-1.5.9/docs/dropdowns.html delete mode 100644 static/css/pico-1.5.9/docs/forms.html delete mode 100644 static/css/pico-1.5.9/docs/grid.html delete mode 100644 static/css/pico-1.5.9/docs/index.html delete mode 100644 static/css/pico-1.5.9/docs/js/commons.js delete mode 100644 static/css/pico-1.5.9/docs/js/commons.min.js delete mode 100644 static/css/pico-1.5.9/docs/js/customization.js delete mode 100644 static/css/pico-1.5.9/docs/js/customization.min.js delete mode 100644 static/css/pico-1.5.9/docs/js/grid.js delete mode 100644 static/css/pico-1.5.9/docs/js/grid.min.js delete mode 100644 static/css/pico-1.5.9/docs/js/modal.js delete mode 100644 static/css/pico-1.5.9/docs/js/modal.min.js delete mode 100644 static/css/pico-1.5.9/docs/js/src/color-picker.js delete mode 100644 static/css/pico-1.5.9/docs/js/src/material-design-colors.js delete mode 100644 static/css/pico-1.5.9/docs/js/src/theme-switcher.js delete mode 100644 static/css/pico-1.5.9/docs/js/src/toggle-navigation.js delete mode 100644 static/css/pico-1.5.9/docs/loading.html delete mode 100644 static/css/pico-1.5.9/docs/modal.html delete mode 100644 static/css/pico-1.5.9/docs/navs.html delete mode 100644 static/css/pico-1.5.9/docs/progress.html delete mode 100644 static/css/pico-1.5.9/docs/rtl.html delete mode 100644 static/css/pico-1.5.9/docs/scroller.html delete mode 100644 static/css/pico-1.5.9/docs/scss/components/_modal.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/components/_nav.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/components/_theme-switcher.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/content/_code.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/content/_typography.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/layout/_aside.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/layout/_document.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/layout/_documentation.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/layout/_main.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/pico.docs.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/themes/_docs.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/themes/docs/_dark.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/themes/docs/_icons.scss delete mode 100644 static/css/pico-1.5.9/docs/scss/themes/docs/_light.scss delete mode 100644 static/css/pico-1.5.9/docs/src/_footer.html delete mode 100644 static/css/pico-1.5.9/docs/src/_head.html delete mode 100644 static/css/pico-1.5.9/docs/src/_nav.html delete mode 100644 static/css/pico-1.5.9/docs/src/_sidebar.html delete mode 100644 static/css/pico-1.5.9/docs/src/accordions.html delete mode 100644 static/css/pico-1.5.9/docs/src/buttons.html delete mode 100644 static/css/pico-1.5.9/docs/src/cards.html delete mode 100644 static/css/pico-1.5.9/docs/src/classless.html delete mode 100644 static/css/pico-1.5.9/docs/src/containers.html delete mode 100644 static/css/pico-1.5.9/docs/src/customization.html delete mode 100644 static/css/pico-1.5.9/docs/src/dropdowns.html delete mode 100644 static/css/pico-1.5.9/docs/src/forms.html delete mode 100644 static/css/pico-1.5.9/docs/src/grid.html delete mode 100644 static/css/pico-1.5.9/docs/src/index.html delete mode 100644 static/css/pico-1.5.9/docs/src/loading.html delete mode 100644 static/css/pico-1.5.9/docs/src/modal.html delete mode 100644 static/css/pico-1.5.9/docs/src/navs.html delete mode 100644 static/css/pico-1.5.9/docs/src/progress.html delete mode 100644 static/css/pico-1.5.9/docs/src/rtl.html delete mode 100644 static/css/pico-1.5.9/docs/src/scroller.html delete mode 100644 static/css/pico-1.5.9/docs/src/tables.html delete mode 100644 static/css/pico-1.5.9/docs/src/themes.html delete mode 100644 static/css/pico-1.5.9/docs/src/tooltips.html delete mode 100644 static/css/pico-1.5.9/docs/src/typography.html delete mode 100644 static/css/pico-1.5.9/docs/src/we-love-classes.html delete mode 100644 static/css/pico-1.5.9/docs/tables.html delete mode 100644 static/css/pico-1.5.9/docs/themes.html delete mode 100644 static/css/pico-1.5.9/docs/tooltips.html delete mode 100644 static/css/pico-1.5.9/docs/typography.html delete mode 100644 static/css/pico-1.5.9/docs/we-love-classes.html delete mode 100644 static/css/pico-1.5.9/package-lock.json delete mode 100644 static/css/pico-1.5.9/package.json delete mode 100644 static/css/pico-1.5.9/scss/_functions.scss delete mode 100644 static/css/pico-1.5.9/scss/_variables.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_accordion.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_card.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_dropdown.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_modal.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_nav.scss delete mode 100644 static/css/pico-1.5.9/scss/components/_progress.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_button.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_code.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_embedded.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_form-alt-input-types.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_form-checkbox-radio.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_form.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_miscs.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_table.scss delete mode 100644 static/css/pico-1.5.9/scss/content/_typography.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_container.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_document.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_grid.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_scroller.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_section.scss delete mode 100644 static/css/pico-1.5.9/scss/layout/_sectioning.scss delete mode 100644 static/css/pico-1.5.9/scss/pico.classless.scss delete mode 100644 static/css/pico-1.5.9/scss/pico.fluid.classless.scss delete mode 100644 static/css/pico-1.5.9/scss/pico.scss delete mode 100644 static/css/pico-1.5.9/scss/pico.slim.scss delete mode 100644 static/css/pico-1.5.9/scss/postcss.config.js delete mode 100644 static/css/pico-1.5.9/scss/themes/default.scss delete mode 100644 static/css/pico-1.5.9/scss/themes/default/_colors.scss delete mode 100644 static/css/pico-1.5.9/scss/themes/default/_dark.scss delete mode 100644 static/css/pico-1.5.9/scss/themes/default/_light.scss delete mode 100644 static/css/pico-1.5.9/scss/themes/default/_styles.scss delete mode 100644 static/css/pico-1.5.9/scss/utilities/_accessibility.scss delete mode 100644 static/css/pico-1.5.9/scss/utilities/_loading.scss delete mode 100644 static/css/pico-1.5.9/scss/utilities/_reduce-motion.scss delete mode 100644 static/css/pico-1.5.9/scss/utilities/_tooltip.scss create mode 100644 static/css/pico-2.1.1/pico.yellow.min.css delete mode 100644 static/custom_pico.scss diff --git a/.env.template b/.env.template index fcf11b7..ae7a7c0 100644 --- a/.env.template +++ b/.env.template @@ -25,6 +25,11 @@ COUCHDB_USER= COUCHDB_PASSWORD= COUCHDB_PORT= +# Bind-mount volume paths on the host — use the db/ directory in the repo root +# Windows: H:/Dev_Mentoring/Animals Healthcare Application/db/postgres +DB_VOLUMEN_POSTGRES= +DB_VOLUMEN_COUCH= + # CouchDB for settings.py - dev COUCH_CONNECTOR=Server("") diff --git a/TODO.md b/TODO.md index 50a52e2..14424f9 100644 --- a/TODO.md +++ b/TODO.md @@ -86,8 +86,12 @@ built with `uv sync --no-group dev` no longer install it. dropped `rcssmin`, `rjsmin`. - `cffi` kept — still required by `cryptography`. -**Follow-up:** upgrade PicoCSS and bring it in as a git submodule (the 6d SCSS -deprecation warnings originate in pico 1.5.9 internals). +**Follow-up — PicoCSS upgrade:** ✅ Upgraded to **2.1.1** (vendored precompiled CSS). +`static/css/pico-2.1.1/pico.yellow.min.css` downloaded from jsDelivr; `pico-1.5.9/` +and `custom_pico.scss` removed; `custom_pico.css` rewritten to `--pico-*` variable +overrides only (secondary grey `#bbbbbb`). To update in the future: replace the +single file and bump the `` version in `base.html`. No Node/Sass/git-submodule +required. ### 6e. Monitor: transitive Python-2-era deps diff --git a/docker/.dockerignore b/docker/.dockerignore deleted file mode 100644 index 1f45f28..0000000 --- a/docker/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -./tars/* -*.tar -.venv/ -__pycache__/ -*.py[cod] -db.sqlite3 -static_collected/ -static/media/profile_pics/ -static/media/attachments/ diff --git a/docker/Dockerfile-couchdb b/docker/Dockerfile-couchdb index bf03851..e0a9c79 100644 --- a/docker/Dockerfile-couchdb +++ b/docker/Dockerfile-couchdb @@ -1,10 +1,7 @@ -FROM couchdb:3.3.3 +FROM couchdb:3.5.2 ARG COUCHDB_USER ARG COUCHDB_PASSWORD -ARG COUCHDB_PORT -EXPOSE ${COUCHDB_PORT} COPY docker/setup_couchdb.sh setup_couchdb.sh -RUN chmod +x setup_couchdb.sh -RUN sh setup_couchdb.sh +RUN chmod +x setup_couchdb.sh && sh setup_couchdb.sh diff --git a/docker/Dockerfile-couchdb.dockerignore b/docker/Dockerfile-couchdb.dockerignore new file mode 100644 index 0000000..d553070 --- /dev/null +++ b/docker/Dockerfile-couchdb.dockerignore @@ -0,0 +1,5 @@ +# Build-context exclusions for Dockerfile-couchdb (context = project root) +# Only docker/setup_couchdb.sh is needed; exclude everything else. + +* +!docker/setup_couchdb.sh diff --git a/docker/Dockerfile-queue b/docker/Dockerfile-queue index c4ca1d3..feb5414 100644 --- a/docker/Dockerfile-queue +++ b/docker/Dockerfile-queue @@ -1,7 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM python:3.14-slim AS builder +FROM ghcr.io/astral-sh/uv:python3.14-bookworm-slim AS builder WORKDIR /app -RUN pip install uv --break-system-packages COPY pyproject.toml uv.lock ./ ENV UV_PYTHON_PREFERENCE=only-system \ UV_PROJECT_ENVIRONMENT=/opt/venv @@ -10,7 +9,15 @@ RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ FROM python:3.14-slim AS runtime LABEL authors="AM" + +RUN groupadd --gid 1000 appuser \ + && useradd --uid 1000 --gid 1000 --no-create-home appuser + WORKDIR /app +RUN chown appuser:appuser /app COPY --from=builder /opt/venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" -COPY . . +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONPATH=/app/src + +COPY --chown=appuser:appuser . . +USER appuser diff --git a/docker/Dockerfile-queue.dockerignore b/docker/Dockerfile-queue.dockerignore new file mode 100644 index 0000000..c884204 --- /dev/null +++ b/docker/Dockerfile-queue.dockerignore @@ -0,0 +1,45 @@ +# Build-context exclusions for Dockerfile-queue (context = project root) +# Used for both the Celery worker and Celery Beat services. + +.venv/ + +__pycache__/ +*.py[cod] +*.pyo + +.pytest_cache/ +.hypothesis/ +htmlcov/ +.coverage +.coverage.* + +.mypy_cache/ +.ty_cache/ +.ruff_cache/ + +db.sqlite3 +db.sqlite3-journal +local_settings.py +static_collected/ + +static/media/profile_pics/ +static/media/attachments/ + +.env +.env.* + +node_modules/ + +tars/ +*.tar + +.git/ +.gitignore +.idea/ +.claude/ +CLAUDE.md + +kubernetes/ + +*.md +doc/ diff --git a/docker/Dockerfile-web b/docker/Dockerfile-web index c4ca1d3..174d707 100644 --- a/docker/Dockerfile-web +++ b/docker/Dockerfile-web @@ -1,7 +1,6 @@ # syntax=docker/dockerfile:1.7 -FROM python:3.14-slim AS builder +FROM ghcr.io/astral-sh/uv:python3.14-bookworm-slim AS builder WORKDIR /app -RUN pip install uv --break-system-packages COPY pyproject.toml uv.lock ./ ENV UV_PYTHON_PREFERENCE=only-system \ UV_PROJECT_ENVIRONMENT=/opt/venv @@ -10,7 +9,14 @@ RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ FROM python:3.14-slim AS runtime LABEL authors="AM" + +RUN groupadd --gid 1000 appuser \ + && useradd --uid 1000 --gid 1000 --no-create-home appuser + WORKDIR /app COPY --from=builder /opt/venv /opt/venv -ENV PATH="/opt/venv/bin:$PATH" -COPY . . +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONPATH=/app/src + +COPY --chown=appuser:appuser . . +USER appuser diff --git a/docker/Dockerfile-web.dockerignore b/docker/Dockerfile-web.dockerignore new file mode 100644 index 0000000..b4c0bc8 --- /dev/null +++ b/docker/Dockerfile-web.dockerignore @@ -0,0 +1,44 @@ +# Build-context exclusions for Dockerfile-web (context = project root) + +.venv/ + +__pycache__/ +*.py[cod] +*.pyo + +.pytest_cache/ +.hypothesis/ +htmlcov/ +.coverage +.coverage.* + +.mypy_cache/ +.ty_cache/ +.ruff_cache/ + +db.sqlite3 +db.sqlite3-journal +local_settings.py +static_collected/ + +static/media/profile_pics/ +static/media/attachments/ + +.env +.env.* + +node_modules/ + +tars/ +*.tar + +.git/ +.gitignore +.idea/ +.claude/ +CLAUDE.md + +kubernetes/ + +*.md +doc/ diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 380ed09..82f7ee0 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,4 +1,5 @@ -version: "3.9" +name: ahc + services: web: build: @@ -9,6 +10,10 @@ services: - "8000:8000" volumes: - ..:/app + env_file: + - ../.env + environment: + - PYTHONUNBUFFERED=1 depends_on: postgres_db: condition: service_healthy @@ -16,9 +21,6 @@ services: condition: service_started couch_db: condition: service_healthy - environment: - - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src entrypoint: ["/bin/bash", "-c"] command: - | @@ -26,6 +28,9 @@ services: python manage.py migrate python manage.py collectstatic --noinput python manage.py runserver 0.0.0.0:8000 + networks: + - ahc_net + restart: unless-stopped postgres_db: image: postgres:18-alpine @@ -37,13 +42,15 @@ services: ports: - "5433:5432" volumes: - - animals_db:/var/lib/postgresql/data + - animals_db:/var/lib/postgresql healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] + test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 30s retries: 6 - restart: always + networks: + - ahc_net + restart: unless-stopped couch_db: build: @@ -66,13 +73,20 @@ services: interval: 5s timeout: 30s retries: 6 - restart: always + networks: + - ahc_net + restart: unless-stopped queue: build: context: .. dockerfile: docker/Dockerfile-queue command: celery -A celery_notifications.config:celery_obj worker -l info + env_file: + - ../.env + environment: + - DJANGO_SETTINGS_MODULE=ahc.settings + - PYTHONUNBUFFERED=1 depends_on: redis: condition: service_healthy @@ -82,37 +96,47 @@ services: condition: service_healthy ports: - "5000:5000" - environment: - - DJANGO_SETTINGS_MODULE=ahc.settings - - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src + healthcheck: + test: ["CMD-SHELL", "celery -A celery_notifications.config:celery_obj inspect ping -d celery@$$HOSTNAME --timeout 5 2>&1 | grep -q pong"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + networks: + - ahc_net + restart: unless-stopped celery_beat: build: context: .. dockerfile: docker/Dockerfile-queue command: celery -A celery_notifications.config:celery_obj beat -l info + env_file: + - ../.env + environment: + - DJANGO_SETTINGS_MODULE=ahc.settings + - PYTHONUNBUFFERED=1 depends_on: redis: condition: service_healthy postgres_db: condition: service_healthy - environment: - - DJANGO_SETTINGS_MODULE=ahc.settings - - PYTHONUNBUFFERED=1 - - PYTHONPATH=/app/src - env_file: - - ../.env + networks: + - ahc_net + restart: unless-stopped redis: image: redis:7-alpine healthcheck: - test: [ "CMD", "redis-cli", "ping" ] + test: ["CMD", "redis-cli", "ping"] interval: 1s timeout: 3s retries: 30 ports: - "6379:6379" + networks: + - ahc_net + restart: unless-stopped celery_flower: image: mher/flower:2.0.1 @@ -123,7 +147,15 @@ services: - FLOWER_PORT=${FLOWER_PORT} - FLOWER_BASIC_AUTH=${FLOWER_BASIC_AUTH} depends_on: - - queue + queue: + condition: service_healthy + networks: + - ahc_net + restart: unless-stopped + +networks: + ahc_net: + driver: bridge volumes: animals_db: diff --git a/justfile b/justfile index 7cebea3..7e345ac 100644 --- a/justfile +++ b/justfile @@ -44,12 +44,12 @@ shell: uv run python manage.py shell # Start all Docker services -docker-up: - docker-compose -f docker/docker-compose.yml up -d --build +up: + docker-compose --env-file .env -f docker/docker-compose.yml up -d --build # Stop all Docker services -docker-down: - docker-compose -f docker/docker-compose.yml down +down: + docker-compose --env-file .env -f docker/docker-compose.yml down # Run pre-commit hooks on all files precommit: diff --git a/src/ahc/apps/animals/templates/animals/all_animals_stable.html b/src/ahc/apps/animals/templates/animals/all_animals_stable.html index b246f58..3e85e65 100644 --- a/src/ahc/apps/animals/templates/animals/all_animals_stable.html +++ b/src/ahc/apps/animals/templates/animals/all_animals_stable.html @@ -15,7 +15,7 @@

Placeholder title

All pets:

{% for animal in animals %} - {% partial "partials/animal_card.html#animal_card" %} + {% include "partials/animal_card.html" %} {% endfor %}
{% endif %} diff --git a/src/ahc/apps/homepage/templates/homepage/base.html b/src/ahc/apps/homepage/templates/homepage/base.html index 0b85c7e..04838f5 100644 --- a/src/ahc/apps/homepage/templates/homepage/base.html +++ b/src/ahc/apps/homepage/templates/homepage/base.html @@ -9,11 +9,10 @@ AHC - - + + + diff --git a/src/ahc/apps/homepage/templates/homepage/homepage.html b/src/ahc/apps/homepage/templates/homepage/homepage.html index 18db231..883c394 100644 --- a/src/ahc/apps/homepage/templates/homepage/homepage.html +++ b/src/ahc/apps/homepage/templates/homepage/homepage.html @@ -28,7 +28,7 @@

Pinned up:

{% for animal in recent_animals %} - {% partial "partials/animal_card.html#animal_card" %} + {% include "partials/animal_card.html" %} {% endfor %}

@@ -43,7 +43,7 @@

Recent added:

{% for animal in recent_animals %} - {% partial "partials/animal_card.html#animal_card" %} + {% include "partials/animal_card.html" %} {% endfor %}

diff --git a/static/css/custom_pico.css b/static/css/custom_pico.css index 7772820..6220a70 100644 --- a/static/css/custom_pico.css +++ b/static/css/custom_pico.css @@ -1,4 +1,15 @@ -/*! - * Pico CSS v1.5.9 (https://picocss.com) - * Copyright 2019-2023 - Licensed under MIT - */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px}@media(min-width: 576px){:root{--font-size: 17px}}@media(min-width: 768px){:root{--font-size: 18px}}@media(min-width: 992px){:root{--font-size: 19px}}@media(min-width: 1200px){:root{--font-size: 20px}}:root{--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--grid-spacing-vertical: 0;--grid-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media(min-width: 576px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 2.5)}}@media(min-width: 768px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3)}}@media(min-width: 992px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3.5)}}@media(min-width: 1200px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 4)}}@media(min-width: 576px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media(min-width: 768px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}@media(min-width: 992px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.75)}}@media(min-width: 1200px){article{--block-spacing-horizontal: calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing)}@media(min-width: 576px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2.5);--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media(min-width: 768px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 3);--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}a{--text-decoration: none}a.secondary,a.contrast{--text-decoration: underline}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type=checkbox],[type=radio]{--border-width: 2px}[type=checkbox][role=switch]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme=light],:root:not([data-theme=dark]){--background-color: #fff;--color: hsl(205, 20%, 32%);--h1-color: hsl(205, 30%, 15%);--h2-color: rgb(35.38125, 50.628125, 61.51875);--h3-color: hsl(205, 25%, 23%);--h4-color: rgb(54.63375, 72.706875, 85.61625);--h5-color: hsl(205, 20%, 32%);--h6-color: rgb(152.64, 128.66, 51.46);--muted-color: hsl(205, 10%, 50%);--muted-border-color: hsl(205, 20%, 94%);--primary: #fdd835;--primary-hover: #fbc02d;--primary-focus: rgba(253, 216, 53, 0.125);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 20%, 32%);--secondary-focus: rgba(240, 173, 5, 0.125);--secondary-inverse: #fff;--contrast: hsl(205, 30%, 15%);--contrast-hover: #000;--contrast-focus: rgba(240, 173, 5, 0.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: rgb(83.83125, 57.871875, 37.29375);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: hsl(205, 14%, 68%);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #fbc02d;--form-element-disabled-border-color: hsl(205, 14%, 68%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211, 47, 47, 0.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67, 160, 71, 0.125);--switch-background-color: hsl(205, 16%, 77%);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #fbc02d;--range-active-border-color: hsl(205, 16%, 77%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgb(245.82, 247.605, 248.88);--code-background-color: hsl(205, 20%, 94%);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 40%, 50%);--code-property-color: hsl(185, 40%, 40%);--code-value-color: hsl(40, 20%, 50%);--code-comment-color: hsl(205, 14%, 68%);--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(26.775, 40.1625, 49.725, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(26.775, 40.1625, 49.725, 0.024), 0.0625rem 0.125rem 0.75rem rgba(26.775, 40.1625, 49.725, 0.03), 0.1125rem 0.225rem 1.35rem rgba(26.775, 40.1625, 49.725, 0.036), 0.2085rem 0.417rem 2.502rem rgba(26.775, 40.1625, 49.725, 0.04302), 0.5rem 1rem 6rem rgba(26.775, 40.1625, 49.725, 0.06), 0 0 0 0.0625rem rgba(26.775, 40.1625, 49.725, 0.015);--card-sectionning-background-color: rgb(250.41, 251.3025, 251.94);--dropdown-background-color: rgb(250.41, 251.3025, 251.94);--dropdown-border-color: rgb(243.82, 216.105, 143.88);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: hsl(205, 20%, 94%);--modal-overlay-background-color: rgba(251, 192, 45, 0.7);--progress-background-color: #fbc02d;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: rgb(16.734375, 25.1015625, 31.078125);--color: hsl(205, 16%, 77%);--h1-color: hsl(205, 20%, 94%);--h2-color: rgb(243.82, 216.105, 143.88);--h3-color: #fbc02d;--h4-color: rgb(218.983, 194.957, 125.367);--h5-color: hsl(205, 16%, 77%);--h6-color: rgb(174.471, 186.609, 195.279);--muted-color: hsl(205, 10%, 50%);--muted-border-color: rgb(31.078125, 45.3953125, 55.621875);--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253, 216, 53, 0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 10%, 50%);--secondary-focus: rgba(114.75, 129.625, 140.25, 0.25);--secondary-inverse: #fff;--contrast: hsl(205, 20%, 94%);--contrast-hover: #fff;--contrast-focus: rgba(114.75, 129.625, 140.25, 0.25);--contrast-inverse: #000;--mark-background-color: rgb(208.488, 194.152, 131.912);--mark-color: rgb(16.734375, 25.1015625, 31.078125);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: rgb(16.734375, 25.1015625, 31.078125);--form-element-border-color: rgb(54.63375, 72.706875, 85.61625);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: hsl(205, 25%, 23%);--form-element-disabled-border-color: hsl(205, 20%, 32%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56, 142, 60, 0.25);--switch-background-color: rgb(54.63375, 72.706875, 85.61625);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: rgb(35.38125, 50.628125, 61.51875);--range-active-border-color: hsl(205, 25%, 23%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(114.75, 129.625, 140.25, 0.05);--code-background-color: rgb(23.428125, 35.1421875, 43.509375);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 30%, 50%);--code-property-color: hsl(185, 30%, 50%);--code-value-color: hsl(40, 10%, 50%);--code-comment-color: rgb(152.64, 128.66, 51.46);--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: rgb(20.08125, 30.121875, 37.29375);--card-border-color: var(--card-background-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color: rgb(23.428125, 35.1421875, 43.509375);--dropdown-background-color: hsl(205, 30%, 15%);--dropdown-border-color: rgb(35.38125, 50.628125, 61.51875);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35.38125, 50.628125, 61.51875, 0.75);--modal-overlay-background-color: rgba(35.38125, 50.628125, 61.51875, 0.8);--progress-background-color: rgb(35.38125, 50.628125, 61.51875);--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme=dark]{--background-color: rgb(16.734375, 25.1015625, 31.078125);--color: hsl(205, 16%, 77%);--h1-color: hsl(205, 20%, 94%);--h2-color: rgb(243.82, 216.105, 143.88);--h3-color: #fbc02d;--h4-color: rgb(218.983, 194.957, 125.367);--h5-color: hsl(205, 16%, 77%);--h6-color: rgb(174.471, 186.609, 195.279);--muted-color: hsl(205, 10%, 50%);--muted-border-color: rgb(31.078125, 45.3953125, 55.621875);--primary: #fdd835;--primary-hover: #ffeb3b;--primary-focus: rgba(253, 216, 53, 0.25);--primary-inverse: #fff;--secondary: #F0AD05;--secondary-hover: hsl(205, 10%, 50%);--secondary-focus: rgba(114.75, 129.625, 140.25, 0.25);--secondary-inverse: #fff;--contrast: hsl(205, 20%, 94%);--contrast-hover: #fff;--contrast-focus: rgba(114.75, 129.625, 140.25, 0.25);--contrast-inverse: #000;--mark-background-color: rgb(208.488, 194.152, 131.912);--mark-color: rgb(16.734375, 25.1015625, 31.078125);--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: rgb(16.734375, 25.1015625, 31.078125);--form-element-border-color: rgb(54.63375, 72.706875, 85.61625);--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: hsl(205, 25%, 23%);--form-element-disabled-border-color: hsl(205, 20%, 32%);--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56, 142, 60, 0.25);--switch-background-color: rgb(54.63375, 72.706875, 85.61625);--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: rgb(35.38125, 50.628125, 61.51875);--range-active-border-color: hsl(205, 25%, 23%);--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(114.75, 129.625, 140.25, 0.05);--code-background-color: rgb(23.428125, 35.1421875, 43.509375);--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: hsl(330, 30%, 50%);--code-property-color: hsl(185, 30%, 50%);--code-value-color: hsl(40, 10%, 50%);--code-comment-color: rgb(152.64, 128.66, 51.46);--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: rgb(20.08125, 30.121875, 37.29375);--card-border-color: var(--card-background-color);--card-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color: rgb(23.428125, 35.1421875, 43.509375);--dropdown-background-color: hsl(205, 30%, 15%);--dropdown-border-color: rgb(35.38125, 50.628125, 61.51875);--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(35.38125, 50.628125, 61.51875, 0.75);--modal-overlay-background-color: rgba(35.38125, 50.628125, 61.51875, 0.8);--progress-background-color: rgb(35.38125, 50.628125, 61.51875);--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type=checkbox],[type=radio],[type=range]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media(min-width: 576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media(min-width: 768px){.container{max-width:700px}}@media(min-width: 992px){.container{max-width:920px}}@media(min-width: 1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media(min-width: 992px){.grid{grid-template-columns:repeat(auto-fit, minmax(0%, 1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing)*.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role=link]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition)}a:is([aria-current],:hover,:active,:focus),[role=link]:is([aria-current],:hover,:active,:focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role=link]:focus{--background-color: var(--primary-focus)}a.secondary,[role=link].secondary{--color: var(--secondary)}a.secondary:is([aria-current],:hover,:active,:focus),[role=link].secondary:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}a.secondary:focus,[role=link].secondary:focus{--background-color: var(--secondary-focus)}a.contrast,[role=link].contrast{--color: var(--contrast)}a.contrast:is([aria-current],:hover,:active,:focus),[role=link].contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}a.contrast:focus,[role=link].contrast:focus{--background-color: var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}hgroup,.headings{margin-bottom:var(--typography-spacing-vertical)}hgroup>*,.headings>*{margin-bottom:0}hgroup>*:last-child,.headings>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical)*.25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical)*.25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);border-inline-start:.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical)*.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}button,input[type=submit],input[type=button],input[type=reset],[role=button]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}button:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),[role=button]:is([aria-current],:hover,:active,:focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type=submit]:focus,input[type=button]:focus,input[type=reset]:focus,[role=button]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--primary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).secondary,input[type=reset]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}:is(button,input[type=submit],input[type=button],[role=button]).secondary:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover);--color: var(--secondary-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).secondary:focus,input[type=reset]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--secondary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).contrast{--background-color: var(--contrast);--border-color: var(--contrast);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:is([aria-current],:hover,:active,:focus){--background-color: var(--contrast-hover);--border-color: var(--contrast-hover);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--contrast-focus)}:is(button,input[type=submit],input[type=button],[role=button]).outline,input[type=reset].outline{--background-color: transparent;--color: var(--primary)}:is(button,input[type=submit],input[type=button],[role=button]).outline:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--background-color: transparent;--color: var(--primary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary,input[type=reset].outline{--color: var(--secondary)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast{--color: var(--contrast)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing)*.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--background-color: var(--form-element-active-background-color)}input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--border-color: var(--form-element-active-border-color)}input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type=submit],[type=button],[type=reset])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal) !important;padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color: var(--form-element-valid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important}:where(input,select,textarea)[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color: var(--form-element-invalid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:rgba(0,0,0,0)}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}:where(input,select,textarea,.grid)+small{display:block;width:100%;margin-top:calc(var(--spacing)*-0.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing)*.25)}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-0.125em;margin-right:.375em;margin-left:0;margin-inline-start:0;margin-inline-end:.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-right:.375em;margin-bottom:0;cursor:pointer}[type=checkbox]:indeterminate{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color);--color: var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type=checkbox][role=switch]:focus{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color)}[type=checkbox][role=switch]:checked{--background-color: var(--switch-checked-background-color);--border-color: var(--switch-checked-background-color)}[type=checkbox][role=switch]:before{display:block;width:calc(1.25em - var(--border-width)*2);height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:checked{background-image:none}[type=checkbox][role=switch]:checked::before{margin-left:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type=checkbox][aria-invalid=false],[type=checkbox]:checked[aria-invalid=false],[type=radio][aria-invalid=false],[type=radio]:checked[aria-invalid=false],[type=checkbox][role=switch][aria-invalid=false],[type=checkbox][role=switch]:checked[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}[type=checkbox][aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=radio][aria-invalid=true],[type=radio]:checked[aria-invalid=true],[type=checkbox][role=switch][aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--icon-position: 0.75rem;--icon-width: 1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width)*-1);margin-left:var(--icon-position);opacity:0}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}[type=file]{--color: var(--muted-color);padding:calc(var(--form-element-spacing-vertical)*.5) 0;border:0;border-radius:0;background:none}[type=file]::file-selector-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::file-selector-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-webkit-file-upload-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-ms-browse{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-ms-browse:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}[type=range]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-0.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]:hover,[type=range]:focus{--range-border-color: var(--range-active-border-color);--range-thumb-color: var(--range-thumb-hover-color)}[type=range]:active{--range-thumb-color: var(--range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--icon-search),var(--icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--icon-search),var(--icon-invalid)}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing)/2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}pre,code,kbd,samp{font-size:.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}pre,code,kbd{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem .5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:none;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none !important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary{transition:color var(--transition)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--spacing, 1rem)*.5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:none}details summary:focus:not([role=button]){color:var(--accordion-active-summary-color)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem*var(--line-height, 1.5));background-image:var(--icon-chevron-button)}details summary[role=button]:not(.outline).contrast::after{background-image:var(--icon-chevron-button-inverse)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>header,article>footer{margin-right:calc(var(--block-spacing-horizontal)*-1);margin-left:calc(var(--block-spacing-horizontal)*-1);padding:calc(var(--block-spacing-vertical)*.66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical)*-1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical)*-1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width: 0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;backdrop-filter:var(--modal-overlay-backdrop-filter);background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing)*2);overflow:auto}@media(min-width: 576px){dialog article{max-width:510px}}@media(min-width: 768px){dialog article{max-width:700px}}dialog article>header,dialog article>footer{padding:calc(var(--block-spacing-vertical)*.5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role=button]{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type){margin-left:calc(var(--spacing)*.5)}dialog article p:last-of-type{margin:0}dialog article .close{display:block;width:1rem;height:1rem;margin-top:calc(var(--block-spacing-vertical)*-0.5);margin-bottom:var(--typography-spacing-vertical);margin-left:auto;background-image:var(--icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;opacity:.5;transition:opacity var(--transition)}dialog article .close:is([aria-current],:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--scrollbar-width, 0px);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{backdrop-filter:none;background-color:rgba(0,0,0,0)}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal)*-1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal)*-1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical)*-1) calc(var(--nav-link-spacing-horizontal)*-1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizontal)*2);margin-inline-start:calc(var(--nav-link-spacing-horizontal)/2);content:"/";color:var(--muted-color);text-align:center}nav[aria-label=breadcrumb] a[aria-current]{background-color:rgba(0,0,0,0);color:inherit;text-decoration:none;pointer-events:none}nav [role=button]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside nav,aside ol,aside ul,aside li{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical)*.5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--spacing)*.5);overflow:hidden;border:0;border-radius:var(--border-radius);background-color:var(--progress-background-color);color:var(--progress-color)}progress::-webkit-progress-bar{border-radius:var(--border-radius);background:none}progress[value]::-webkit-progress-value{background-color:var(--progress-color)}progress::-moz-progress-bar{background-color:var(--progress-color)}@media(prefers-reduced-motion: no-preference){progress:indeterminate{background:var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:rgba(0,0,0,0)}progress:indeterminate::-moz-progress-bar{background-color:rgba(0,0,0,0)}}@media(prefers-reduced-motion: no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}details[role=list],li[role=list]{position:relative}details[role=list] summary+ul,li[role=list]>ul{display:flex;z-index:99;position:absolute;top:auto;right:0;left:0;flex-direction:column;margin:0;padding:0;border:var(--border-width) solid var(--dropdown-border-color);border-radius:var(--border-radius);border-top-right-radius:0;border-top-left-radius:0;background-color:var(--dropdown-background-color);box-shadow:var(--card-box-shadow);color:var(--dropdown-color);white-space:nowrap}details[role=list] summary+ul li,li[role=list]>ul li{width:100%;margin-bottom:0;padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);list-style:none}details[role=list] summary+ul li:first-of-type,li[role=list]>ul li:first-of-type{margin-top:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li:last-of-type,li[role=list]>ul li:last-of-type{margin-bottom:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li a,li[role=list]>ul li a{display:block;margin:calc(var(--form-element-spacing-vertical)*-0.5) calc(var(--form-element-spacing-horizontal)*-1);padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);overflow:hidden;color:var(--dropdown-color);text-decoration:none;text-overflow:ellipsis}details[role=list] summary+ul li a:hover,li[role=list]>ul li a:hover{background-color:var(--dropdown-hover-background-color)}details[role=list] summary::after,li[role=list]>a::after{display:block;width:1rem;height:calc(1rem*var(--line-height, 1.5));margin-inline-start:.5rem;float:right;transform:rotate(0deg);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}details[role=list]{padding:0;border-bottom:none}details[role=list] summary{margin-bottom:0}details[role=list] summary:not([role]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2);padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--form-element-border-color);border-radius:var(--border-radius);background-color:var(--form-element-background-color);color:var(--form-element-placeholder-color);line-height:inherit;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}details[role=list] summary:not([role]):active,details[role=list] summary:not([role]):focus{border-color:var(--form-element-active-border-color);background-color:var(--form-element-active-background-color)}details[role=list] summary:not([role]):focus{box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}details[role=list][open] summary{border-bottom-right-radius:0;border-bottom-left-radius:0}details[role=list][open] summary::before{display:block;z-index:1;position:fixed;top:0;right:0;bottom:0;left:0;background:none;content:"";cursor:default}nav details[role=list] summary,nav li[role=list] a{display:flex;direction:ltr}nav details[role=list] summary+ul,nav li[role=list]>ul{min-width:fit-content;border-radius:var(--border-radius)}nav details[role=list] summary+ul li a,nav li[role=list]>ul li a{border-radius:0}nav details[role=list] summary,nav details[role=list] summary:not([role]){height:auto;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}nav details[role=list][open] summary{border-radius:var(--border-radius)}nav details[role=list] summary+ul{margin-top:var(--outline-width);margin-inline-start:0}nav details[role=list] summary[role=link]{margin-bottom:calc(var(--nav-link-spacing-vertical)*-1);line-height:var(--line-height)}nav details[role=list] summary[role=link]+ul{margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-link-spacing-horizontal)*-1)}li[role=list]:hover>ul,li[role=list] a:active~ul,li[role=list] a:focus~ul{display:flex}li[role=list]>ul{display:none;margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal))}li[role=list]>a::after{background-image:var(--icon-chevron)}label>details[role=list]{margin-top:calc(var(--spacing)*.25);margin-bottom:var(--spacing)}[aria-busy=true]{cursor:progress}[aria-busy=true]:not(input,select,textarea)::before{display:inline-block;width:1em;height:1em;border:.1875em solid currentColor;border-radius:1em;border-right-color:rgba(0,0,0,0);content:"";vertical-align:text-bottom;vertical-align:-0.125em;animation:spinner .75s linear infinite;opacity:var(--loading-spinner-opacity)}[aria-busy=true]:not(input,select,textarea):not(:empty)::before{margin-right:calc(var(--spacing)*.5);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)*.5)}[aria-busy=true]:not(input,select,textarea):empty{text-align:center}button[aria-busy=true],input[type=submit][aria-busy=true],input[type=button][aria-busy=true],input[type=reset][aria-busy=true],a[aria-busy=true]{pointer-events:none}@keyframes spinner{to{transform:rotate(360deg)}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip][data-placement=top]::before,[data-tooltip][data-placement=top]::after,[data-tooltip]::before,[data-tooltip]::after{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%, -0.25rem);border-radius:var(--border-radius);background:var(--tooltip-background-color);content:attr(data-tooltip);color:var(--tooltip-color);font-style:normal;font-weight:var(--font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip][data-placement=top]::after,[data-tooltip]::after{padding:0;transform:translate(-50%, 0rem);border-top:.3rem solid;border-right:.3rem solid rgba(0,0,0,0);border-left:.3rem solid rgba(0,0,0,0);border-radius:0;background-color:rgba(0,0,0,0);content:"";color:var(--tooltip-background-color)}[data-tooltip][data-placement=bottom]::before,[data-tooltip][data-placement=bottom]::after{top:100%;bottom:auto;transform:translate(-50%, 0.25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%, -0.3rem);border:.3rem solid rgba(0,0,0,0);border-bottom:.3rem solid}[data-tooltip][data-placement=left]::before,[data-tooltip][data-placement=left]::after{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-0.25rem, -50%)}[data-tooltip][data-placement=left]:after{transform:translate(0.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-left:.3rem solid}[data-tooltip][data-placement=right]::before,[data-tooltip][data-placement=right]::after{top:50%;right:auto;bottom:auto;left:100%;transform:translate(0.25rem, -50%)}[data-tooltip][data-placement=right]:after{transform:translate(-0.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-right:.3rem solid}[data-tooltip]:focus::before,[data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{opacity:1}@media(hover: hover)and (pointer: fine){[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::before,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-top}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::after{animation-name:tooltip-caret-slide-top}[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::before,[data-tooltip][data-placement=bottom]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-bottom}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{animation-name:tooltip-caret-slide-bottom}[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::before,[data-tooltip][data-placement=left]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-left}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{animation-name:tooltip-caret-slide-left}[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::before,[data-tooltip][data-placement=right]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-right}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{animation-name:tooltip-caret-slide-right}}@keyframes tooltip-slide-top{from{transform:translate(-50%, 0.75rem);opacity:0}to{transform:translate(-50%, -0.25rem);opacity:1}}@keyframes tooltip-caret-slide-top{from{opacity:0}50%{transform:translate(-50%, -0.25rem);opacity:0}to{transform:translate(-50%, 0rem);opacity:1}}@keyframes tooltip-slide-bottom{from{transform:translate(-50%, -0.75rem);opacity:0}to{transform:translate(-50%, 0.25rem);opacity:1}}@keyframes tooltip-caret-slide-bottom{from{opacity:0}50%{transform:translate(-50%, -0.5rem);opacity:0}to{transform:translate(-50%, -0.3rem);opacity:1}}@keyframes tooltip-slide-left{from{transform:translate(0.75rem, -50%);opacity:0}to{transform:translate(-0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-left{from{opacity:0}50%{transform:translate(0.05rem, -50%);opacity:0}to{transform:translate(0.3rem, -50%);opacity:1}}@keyframes tooltip-slide-right{from{transform:translate(-0.75rem, -50%);opacity:0}to{transform:translate(0.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-right{from{opacity:0}50%{transform:translate(-0.05rem, -50%);opacity:0}to{transform:translate(-0.3rem, -50%);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0, 0, 0, 0);position:absolute}a,area,button,input,label,select,summary,textarea,[tabindex]{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media(prefers-reduced-motion: reduce){*:not([aria-busy=true]),:not([aria-busy=true])::before,:not([aria-busy=true])::after{background-attachment:initial !important;animation-duration:1ms !important;animation-delay:-1ms !important;animation-iteration-count:1 !important;scroll-behavior:auto !important;transition-delay:0s !important;transition-duration:0s !important}}/*# sourceMappingURL=custom_pico.css.map */ +/* Custom overrides on top of Pico 2.1.1 yellow theme. + * + * Secondary color: neutral grey #bbbbbb (instead of the default blue-grey). + * --pico-secondary-border / --pico-secondary-hover-border inherit from + * --pico-secondary / --pico-secondary-hover and do not need explicit overrides. + */ +:root { + --pico-secondary: #bbbbbb; + --pico-secondary-hover: #cccccc; + --pico-secondary-background: #bbbbbb; + --pico-secondary-hover-background: #cccccc; + --pico-secondary-focus: rgba(187, 187, 187, 0.25); + --pico-secondary-underline: rgba(187, 187, 187, 0.5); + --pico-secondary-inverse: #000; +} diff --git a/static/css/custom_pico.css.map b/static/css/custom_pico.css.map deleted file mode 100644 index 4a98dc8..0000000 --- a/static/css/custom_pico.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["pico-1.5.9/scss/pico.scss","pico-1.5.9/scss/themes/default/_styles.scss","pico-1.5.9/scss/themes/default/_light.scss","pico-1.5.9/scss/themes/default.scss","pico-1.5.9/scss/themes/default/_dark.scss","pico-1.5.9/scss/layout/_document.scss","pico-1.5.9/scss/layout/_sectioning.scss","pico-1.5.9/scss/layout/_container.scss","pico-1.5.9/scss/layout/_section.scss","pico-1.5.9/scss/layout/_grid.scss","pico-1.5.9/scss/layout/_scroller.scss","pico-1.5.9/scss/content/_typography.scss","pico-1.5.9/scss/content/_embedded.scss","pico-1.5.9/scss/content/_button.scss","pico-1.5.9/scss/content/_form.scss","pico-1.5.9/scss/content/_form-checkbox-radio.scss","pico-1.5.9/scss/content/_form-alt-input-types.scss","pico-1.5.9/scss/content/_table.scss","pico-1.5.9/scss/content/_code.scss","pico-1.5.9/scss/content/_miscs.scss","pico-1.5.9/scss/components/_accordion.scss","pico-1.5.9/scss/components/_card.scss","pico-1.5.9/scss/components/_modal.scss","pico-1.5.9/scss/components/_nav.scss","pico-1.5.9/scss/components/_progress.scss","pico-1.5.9/scss/components/_dropdown.scss","pico-1.5.9/scss/utilities/_loading.scss","pico-1.5.9/scss/utilities/_tooltip.scss","pico-1.5.9/scss/utilities/_accessibility.scss","pico-1.5.9/scss/utilities/_reduce-motion.scss"],"names":[],"mappings":"CAAA;AAAA;AAAA;AAAA,GCCA,MAEE,4LAGA,mBACA,mBACA,kBAKI,yBAZN,MAaQ,mBAKF,yBAlBN,MAmBQ,mBAKF,yBAxBN,MAyBQ,mBAKF,0BA9BN,MA+BQ,mBA/BR,MAqCE,yBACA,oBACA,qBAGA,gBAGA,sCAGA,mDACA,2CAGE,2BACA,0CAIF,yCACA,wCAGA,qCACA,yCACA,oCACA,sCAGA,6CAGA,+BAGA,+CAWI,yBALJ,0CAMM,sDAKF,yBAXJ,0CAYM,oDAKF,yBAjBJ,0CAkBM,sDAKF,0BAvBJ,0CAwBM,oDAQF,yBAFJ,QAGM,yDAKF,yBARJ,QASM,wDAKF,yBAdJ,QAeM,yDAKF,0BApBJ,QAqBM,sDAMN,eAEE,mDACA,2CAGE,yBANJ,eAOM,qDACA,yDAKF,yBAbJ,eAcM,mDACA,wDAOR,EACE,wBAIE,uBAEE,6BAMN,MACE,qBAIF,kBAME,mBAGF,GACE,kBACA,oCAGF,GACE,qBACA,wCAGF,GACE,oBACA,uCAGF,GACE,qBACA,wCAGF,GACE,sBACA,yCAIF,6BAEE,oBAGF,6BACE,oBAMA,oCAEE,oBAIJ,uBACE,qBAIF,kBAIE,8MAKF,IACE,sBClPF,gDAEE,yBAGA,4BACA,+BACA,+CACA,+BACA,+CACA,+BACA,uCAGA,kCACA,yCAGA,mBACA,yBACA,2CACA,wBAGA,qBACA,sCACA,4CACA,0BAGA,+BACA,uBACA,2CACA,yBAGA,iCACA,iDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,6CACA,gDACA,mCACA,qDACA,oDACA,mDACA,iDACA,kDACA,yDACA,qCACA,6CACA,oDACA,6DACA,2CACA,kDACA,2DAGA,8CACA,uCACA,kDAGA,8BACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,oEAGA,4CACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,yCAGA,oDACA,8CACA,mDAMA,iDACA,+CACA,ycAQA,mEAGA,2DACA,sDACA,8CACA,+BACA,sDAGA,0DAGA,qCACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,sSACA,gTACA,wTACA,wVACA,4cACA,8YACA,sSACA,0VACA,qVACA,qSAGA,mBC3IF,oDACE,wBCfA,0DAGA,4BACA,+BACA,yCACA,oBACA,2CACA,+BACA,2CAGA,kCACA,4DAGA,mBACA,yBACA,0CACA,wBAGA,qBACA,sCACA,uDACA,0BAGA,+BACA,uBACA,sDACA,yBAGA,wDACA,oDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,uEACA,gEACA,mCACA,qDACA,6EACA,mDACA,iDACA,6DACA,yDACA,qCACA,6CACA,oDACA,4DACA,2CACA,kDACA,0DAGA,8DACA,uCACA,kDAGA,yDACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,2EAGA,+DACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,iDAGA,oDACA,iDACA,8CACA,mDAMA,4DACA,kDACA,yVAQA,2EAGA,gDACA,4DACA,8CACA,+BACA,6EAGA,2EAGA,gEACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,ySACA,gTACA,kTACA,wVACA,+cACA,8YACA,sSACA,6VACA,wVACA,qSAGA,mBDnIF,kBCtBE,0DAGA,4BACA,+BACA,yCACA,oBACA,2CACA,+BACA,2CAGA,kCACA,4DAGA,mBACA,yBACA,0CACA,wBAGA,qBACA,sCACA,uDACA,0BAGA,+BACA,uBACA,sDACA,yBAGA,wDACA,oDAGA,qBACA,qBAGA,qDACA,8CAKA,4CACA,kDAGA,uEACA,gEACA,mCACA,qDACA,6EACA,mDACA,iDACA,6DACA,yDACA,qCACA,6CACA,oDACA,4DACA,2CACA,kDACA,0DAGA,8DACA,uCACA,kDAGA,yDACA,gDACA,oDACA,sCACA,kDACA,2CAGA,gDACA,2EAGA,+DACA,iCACA,6CACA,0CACA,qCACA,0CACA,sCACA,iDAGA,oDACA,iDACA,8CACA,mDAMA,4DACA,kDACA,yVAQA,2EAGA,gDACA,4DACA,8CACA,+BACA,6EAGA,2EAGA,gEACA,iCAGA,+BAGA,4CACA,yCAGA,0SACA,ySACA,gTACA,kTACA,wVACA,+cACA,8YACA,sSACA,6VACA,wVACA,qSAGA,kBD9HF,mDAIE,4BEvBF,qBAGE,sBACA,4BAKF,iBAEE,wBACA,uBASF,cACE,0CACA,8BACA,sBACA,yCACA,mBACA,+BACA,2BACA,+BACA,+BACA,kCACA,yBACA,eACA,WCnCF,KACE,cAOF,KACE,WACA,SAEA,kCAGE,WACA,kBACA,iBAsCE,wCC7DJ,4BAEE,WACA,kBACA,iBACA,6BACA,4BAKE,yBAFJ,WAGM,gBACA,gBACA,gBAKF,yBAVJ,WAWM,iBAKF,yBAhBJ,WAiBM,iBAKF,0BAtBJ,WAuBM,kBChCR,QACE,kDCCE,+CACA,0CACA,aACA,0BACA,SAGE,yBARJ,MASM,yDAIJ,QACE,YCfN,OACE,cACA,SACA,UACA,gBAEA,kBACE,kCACA,yBCHJ,SAEE,mBAIF,QAEE,kBACA,gBACA,cACA,wBAEF,IACE,eAEF,IACE,WAMF,oDAUE,aACA,iDACA,mBACA,kBACA,+BACA,2BAKF,cAEE,wBACA,gCACA,aACA,yCACA,mBACA,uCAGE,qIAIF,gGACE,8BACA,6BAGF,0BACE,yCAKA,kCACE,0BAEA,oHACE,gCAGF,8CACE,2CAKJ,gCACE,yBAEA,kHACE,+BAGF,4CACE,0CAOR,kBAME,aACA,iDACA,mBACA,+BACA,2BACA,+BAGF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAEF,GACE,yBAKA,mFACE,8CAuBF,iBAEE,iDAEA,qBACE,gBAGF,2CACE,4BACA,qBACA,eACA,kBAMN,EACE,iDAIF,MACE,2BAIF,iBACE,gBACA,4BACA,oCACA,qBAEA,oBACE,2DAOF,+BACE,SACA,wDAIJ,MACE,kBAIF,KACE,uBACA,8CACA,wBACA,wBAIF,WACE,cACA,4CACA,uBACA,kBACA,wDACA,gEACA,uBAEA,kBACE,uDACA,qCAMJ,YACE,yBACA,qBACA,YAIF,IACE,uBACA,qBAIF,IACE,uBAIF,YACE,sCC5PF,0CACE,sBAIF,YAEE,qBAIF,sBACE,aACA,SAIF,eACE,kBAKF,IACE,eACA,YACA,kBAIF,wBACE,kBAIF,eACE,gBClCF,OACE,SACA,iBACA,oBACA,oBAIF,gDAIE,0BAMF,OACE,cACA,WACA,6BAGF,cACE,qBACA,qBAGF,6EAKE,mCACA,+BACA,gCACA,+DACA,oFAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,0RACE,yCACA,qCACA,qEACA,gCAGF,2GACE,sHASF,4FAEE,qCACA,iCACA,kCACA,eAEA,8KACE,2CACA,uCACA,kCAGF,wGACE,wHAMJ,yEACE,oCACA,gCACA,iCAEA,kHACE,0CACA,sCACA,iCAGF,+EACE,uHAMJ,kGAEE,gCACA,wBAEA,oLACE,gCACA,8BAKJ,4GAEE,0BAEA,8LACE,gCAKJ,iFACE,yBAEA,0HACE,+BA0BN,yMAGE,WACA,oBC1KF,+BAIE,SACA,eACA,+BACA,oBACA,uBAIF,MACE,iBAIF,OACE,oBAOF,OACE,eACA,UACA,cACA,mBAIF,SACE,cAIF,6BAEE,UAIF,wDAEE,YAKF,cACE,6BACA,oBAIF,yCACE,wBAKF,6BACE,0BACA,aAIF,mBACE,UACA,kBAIF,gBACE,aAIF,iBACE,gBAIF,aACE,aAIF,yBAEE,UACA,eAOF,qDACE,sGAOF,SACE,SACA,6BACA,UACA,SAIF,sBAEE,cACA,uCACA,8DAIF,wDAGE,WAIF,iFAGE,gBACA,oFAKF,sBAGE,yDACA,iDACA,mCACA,mBACA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BAGE,kIASF,0JACE,gEAOF,2IACE,wDAQF,0HACE,yEAKJ,sMAIE,kEACA,0DACA,6CACA,oBAME,qJAEI,+EAGA,oDACA,uEACA,oFAUF,wCACA,0BACA,4BAGF,2JACE,mCAGF,0JACE,qCAIJ,kDACE,uDAEA,qEAEI,yEACA,0FASN,iDACE,yDAEA,oEAEI,2EACA,4FAaF,sIACE,uCAOR,6HAKE,4CACA,UAIF,wDAGE,6BAMA,mBACE,SACA,+BAGF,8BACE,oEACA,oDACA,4DACA,yEACA,qCACA,wCACA,0BACA,4BAMA,wCACE,uCAaJ,0CACE,cACA,WACA,sCACA,6BACA,yBAMF,oCACE,oCCxVJ,6BAEE,wBACA,qBACA,gBACA,aACA,cACA,oBACA,oBACA,cACA,sBACA,yBACA,iCACA,kBACA,sBACA,eAEA,mDACE,aAGF,iKAGE,mCACA,+BACA,sCACA,2BACA,2BACA,4BAGF,yCACE,qBACA,oBACA,gBACA,eAMF,8BACE,mCACA,+BACA,mCACA,2BACA,2BACA,4BAKJ,aACE,kBAEA,4EAGE,2CACA,mBACA,sBAKJ,6BACE,mDACA,+CACA,6BAQA,MAJe,OAKf,OANgB,OAOhB,qDACA,cARgB,OAShB,yCACA,YAVgB,OAYhB,mCACE,mDACA,+CAGF,qCACE,2DACA,uDAGF,oCACE,cACA,2CACA,YACA,kBACA,8BACA,WAGE,kCAIJ,qCACE,sBAEA,6CACE,gDACA,wDAaJ,oQACE,uDAGF,8PACE,yDC3HF,2CAHE,UAOF,+BAPE,UAiBF,mCAJE,SACA,4CAOF,gCARE,SACA,4CAeF,4IACE,yBACA,mBACA,6DACA,kCACA,sDACA,uCACA,4BAIF,4EACE,kCAUF,sPACE,wBACA,wCACA,iCACA,UAIJ,sFAEE,iBAIF,YACE,4BACA,wDACA,SACA,gBACA,gBAoCA,kCAjCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,4DACE,2CACA,uCAQJ,wCArCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,kEACE,2CACA,uCAYJ,wBAzCE,qCACA,iCACA,kCACA,oCACA,cACA,sBACA,yCACA,sGAEA,qDACA,mCACA,aACA,yCACA,6BACA,mBACA,+BACA,eACA,+BACA,kBACA,eAGE,kIAKF,kDACE,2CACA,uCAkBN,aAOE,wBACA,qBACA,gBACA,WACA,OARe,QASf,gBAeA,4CAXE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EASJ,+BAfE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EAaJ,wBAnBE,WACA,OAfa,OAgBb,mCACA,2CAGE,2EAiCJ,mCAdE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAQJ,+BAlBE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAYJ,wBAtBE,wBACA,MAvCa,QAwCb,OAxCa,QAyCb,mBACA,iDACA,kBACA,0CACA,eAGE,0EAgBJ,sCAEE,uDACA,oDAGF,oBACE,qDAGA,0CACE,sBAGF,sCACE,sBAGF,+BACE,sBAQJ,8EACE,4EACA,mBACA,oCACA,yCACA,0BACA,4BAEA,4FAEI,uFAKF,6DAGF,kGACE,sDAGF,iGACE,wDAOJ,4CACE,wBACA,aAOE,gGACE,0CAEA,8GACE,6DC/PV,cACE,WACA,yBACA,iBACA,cAOF,MAEE,8CACA,kEACA,mBACA,+BACA,2BACA,gBACA,iBAKA,kBAEE,+DACA,gBAOA,yCACE,4DCnCN,kBAIE,iBACA,+BAIF,IACE,6BACA,cAMF,aAGE,mCACA,wCACA,wBACA,+BACA,oBAGF,SAEE,qBACA,sBAGF,IACE,cACA,6BACA,gBAEA,SACE,cACA,uBACA,gBACA,eACA,+BAOF,OACE,4BACA,+BAIF,OACE,iCACA,kBAIF,OACE,8BACA,qBAIF,QACE,gCACA,kBAKJ,IACE,kDACA,4BACA,wBC9EF,GACE,SACA,SACA,+CACA,cAIF,kBAGI,wBAQJ,OACE,qBC3BF,QACE,cACA,6BACA,8BACA,sEAEA,gBACE,iBACA,qBACA,eAEA,4BACE,2CANJ,gBAUI,mCAIF,wCACE,aAGF,wBACE,aAGF,kCACE,qBAIF,uBACE,cACA,WACA,YACA,kDACA,YACA,yBACA,qCACA,iCACA,0BACA,4BACA,WAGE,uCAIJ,sBACE,aAEA,yCACE,4CAKJ,6BACE,WACA,gBAGA,oCACE,0CACA,4CAOE,2DACE,oDASR,sBACE,mCAGE,8CACE,0CAIJ,6BACE,oBAQJ,0BACE,iBAEA,iCACE,WACA,gCC3GR,QACE,uCACA,sEACA,mCACA,wCACA,kCAEA,8BAEE,sDACA,qDACA,gFAEA,0DAGF,eACE,kDACA,4CACA,iEACA,6CACA,4CAGF,eACE,yCACA,qDACA,8DACA,gDACA,+CC7BJ,MACE,uBAGF,OACE,aACA,YACA,eACA,MACA,QACA,SACA,OACA,mBACA,uBACA,cACA,eACA,eACA,gBACA,uBACA,SACA,qDACA,uDACA,mBAGA,eACE,0CACA,cAGE,yBALJ,eAMM,iBAKF,yBAXJ,eAYM,iBAIJ,4CAEE,+EAKA,6BACE,SACA,2BACA,YAIJ,sBACE,iBAEA,oCACE,gBAEA,wDACE,oCAMJ,8BACE,SAMF,sBACE,cACA,WACA,YACA,oDACA,iDACA,iBACA,mCACA,2BACA,0BACA,4BACA,WAGE,qCAGF,+DACE,UAOR,sCAEE,aAMF,eACE,0CACA,gBACA,oBACA,kBAEA,sBACE,oBAUF,8GAEE,mBALiB,IAMjB,sCACA,yBAGF,mDACE,uBACA,6BAEA,2DACE,gBAfe,IAgBf,qBAMJ,0DAEE,mBACA,4BAIJ,yBACE,KACE,qBACA,gCAIJ,iBACE,KACE,4BACA,WC7JN,uBACE,WACA,YAOF,WAEE,aAGF,IACE,8BAEA,cAEE,mBACA,gBACA,UACA,gBAEA,0CACE,2DAEF,wCACE,4DAIJ,OACE,qBACA,SACA,kFAIA,SACE,aAIJ,0BACE,qBACA,6FAEA,4EACA,mCACA,qBAEA,mEACE,qBAKJ,2BACE,mBACA,sBAGE,mDACE,uDAIA,0DACE,kBACA,iDACA,+DACA,YACA,yBACA,kBAKN,2CACE,+BACA,cACA,qBACA,oBAKJ,kBACE,qBACA,oBACA,4EAMF,qCAIE,cAGF,SACE,2FAGA,WACE,cAIF,uBACE,eAWI,oEACE,aC3HZ,SACE,qBACA,wBAMF,SAEE,wBACA,qBAGA,qBACA,gBACA,WACA,aACA,sCACA,gBAGA,SACA,mCACA,kDAGA,4BAEA,+BACE,mCACA,gBAEF,wCACE,uCAEF,4BACE,uCAIF,8CACE,uBACE,oKAOA,oDAEA,sDACE,+BAEF,0CACE,gCAON,8CACE,iCACE,6BAKN,kCACE,GACE,2BAEF,KACE,6BCjFJ,iCAEE,kBAGF,+CAEE,aACA,WACA,kBACA,SACA,QACA,OACA,sBACA,SACA,UACA,8DACA,mCACA,0BACA,yBACA,kDACA,kCACA,4BACA,mBAEA,qDACE,WACA,gBACA,6FAEA,gBAEA,iFACE,yDAGF,+EACE,4DAGF,yDACE,cACA,uGAEA,6FAEA,gBACA,4BACA,qBACA,uBAEA,qEACE,wDASN,yDACE,cACA,WACA,0CACA,0BACA,YACA,uBACA,iCACA,0BACA,4BACA,WAKJ,mBACE,UACA,mBAGA,2BACE,gBAEA,uCACE,sGAIA,oFAEA,kEACA,mCACA,sDACA,4CACA,oBACA,eAGE,kIAKF,2FAEE,qDACA,6DAGF,6CACE,sEAMN,iCACE,6BACA,4BAEA,yCACE,cACA,UACA,eACA,MACA,QACA,SACA,OACA,gBACA,WACA,eAMN,mDAEE,aACA,cAGF,uDAEE,sBACA,mCAEA,iEACE,gBAMF,0EAEE,YACA,4EAGF,qCACE,mCAGF,kCACE,gCACA,sBAGF,0CACE,wDACA,+BAEA,6CACE,yEACA,gEASJ,0EAGE,aAGF,iBACE,aACA,yEACA,qGAKF,uBACE,qCAIJ,yBACE,oCACA,6BC7MF,iBACE,gBAMA,oDACE,qBACA,UACA,WACA,kCACA,kBACA,iCACA,WACA,2BACA,wBACA,uCACA,uCAIA,gEACE,qCACA,cACA,sBACA,0CAIJ,kDACE,kBAUF,iJACE,oBAKJ,mBACE,GACE,0BCnDJ,eACE,kBAEA,mCACE,yBACA,qBACA,YAGF,kIAIE,cACA,WACA,kBACA,YACA,SACA,qBACA,gBACA,oCACA,mCACA,2CACA,2BACA,2BACA,kBACA,+BACA,kBACA,qBACA,uBACA,mBACA,UACA,oBAIF,gEAEE,UACA,gCACA,uBACA,uCACA,sCACA,gBACA,+BACA,WACA,sCAIA,2FAEE,SACA,YACA,mCAGF,4CACE,mCACA,iCACA,0BAKF,uFAEE,QACA,WACA,YACA,UACA,oCAGF,0CACE,kCACA,iCACA,wBAKF,yFAEE,QACA,WACA,YACA,UACA,mCAGF,2CACE,mCACA,iCACA,yBAOF,kHAEE,UAQF,wCAKI,iTAEE,uBACA,iCAGF,uJACE,uCAOA,8MAEE,uBACA,oCAGF,sGACE,0CAQF,sMAEE,uBACA,kCAGF,kGACE,wCAQF,0MAEE,uBACA,mCAGF,oGACE,0CAMR,6BACE,KACE,mCACA,UAEF,GACE,oCACA,WAIJ,mCACE,KACE,UAEF,IACE,oCACA,UAEF,GACE,gCACA,WAIJ,gCACE,KACE,oCACA,UAEF,GACE,mCACA,WAIJ,sCACE,KACE,UAEF,IACE,mCACA,UAEF,GACE,mCACA,WAIJ,8BACE,KACE,mCACA,UAEF,GACE,oCACA,WAIJ,oCACE,KACE,UAEF,IACE,mCACA,UAEF,GACE,kCACA,WAIJ,+BACE,KACE,oCACA,UAEF,GACE,mCACA,WAIJ,qCACE,KACE,UAEF,IACE,oCACA,UAEF,GACE,mCACA,WCrQR,gBACE,eAIF,gCAEE,mBAIF,4BACE,gBAGF,wCACE,sBACA,kBAKF,6DASE,8BAMF,UACE,cCrCA,uCACE,qFAGE,yCACA,kCACA,gCACA,uCACA,gCACA,+BACA","file":"custom_pico.css"} \ No newline at end of file diff --git a/static/css/pico-1.5.9/.github/CONTRIBUTING.md b/static/css/pico-1.5.9/.github/CONTRIBUTING.md deleted file mode 100644 index 3ca3c52..0000000 --- a/static/css/pico-1.5.9/.github/CONTRIBUTING.md +++ /dev/null @@ -1,22 +0,0 @@ -# Contributing to Pico - -Thanks for your interest in contributing to Pico CSS! Please take a moment to review this document before submitting a [bug report](https://github.com/picocss/pico/issues) or a [pull request](https://github.com/picocss/pico/pulls). - -## Bug reports - -The [issue tracker]((https://github.com/picocss/pico/issues)) is the preferred channel for bug reports, but please respect the following restrictions: -- Please do not use the issue tracker for personal support requests. [Open a question in our discussion forums](https://github.com/picocss/pico/discussions/new?category=help) instead. -- Please do not use the issue tracker for feature requests. [Suggest any ideas you have using our discussion forums](https://github.com/picocss/pico/discussions/new?category=ideas) instead. - -## Pull requests - -Good pull requests, patches, improvements, and new features are a fantastic help. - -**Please ask first before starting work on any significant new features.** -We recommend that you first [suggest your feature idea in our discussion forums](https://github.com/picocss/pico/discussions/new?category=ideas). - -[`dev`](https://github.com/picocss/pico/tree/dev) branch is open to pull requests. - -**Do not edit [`/css`](https://github.com/picocss/pico/tree/master/css) files directly.** Edit the source files in [`/scss`](https://github.com/picocss/pico/tree/master/scss), then recompile the [`/css`](https://github.com/picocss/pico/tree/master/css) files with `npm run build`. - -Do not edit [`/docs/*.html`](https://github.com/picocss/pico/tree/master/docs) files directly. Edit the source files in [`/docs/src`](https://github.com/picocss/pico/tree/master/docs/src), then recompile the docs files with `npm run build`. diff --git a/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md b/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 38402ed..0000000 --- a/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Bug report -about: Create a bug report if you've already asked for help with a problem and confirmed something is broken with Pico CSS. ---- - -Please search for duplicate or closed issues first. - -## Describe the issue - -### Current Behavior -A concise description of the bug. - -### Expected Behavior -A concise description of what you expected. - -### Reproduction URL -We recommend including a link to a minimal reproduction of the bug using CodePen or a similar tool. -**Please do not link to your actual project.** Instead, we need a reduced test case in a new project without any unnecessary code. - -### Environment -Example: OS, versions, browser details. diff --git a/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml b/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 6cabc57..0000000 --- a/static/css/pico-1.5.9/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Get Help - url: https://github.com/picocss/pico/discussions/discussions/new?category=help - about: If you can't get something to work the way you expect, open a question in our discussion forums. - - name: Feature Request - url: https://github.com/picocss/pico/discussions/discussions/new?category=ideas - about: Suggest any ideas you have using our discussion forums. \ No newline at end of file diff --git a/static/css/pico-1.5.9/.github/examples.jpg b/static/css/pico-1.5.9/.github/examples.jpg deleted file mode 100644 index 240c034977ed34eb924a78e0dc4d844c9fcf1fa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133531 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 gUljO@0)J89FADrcfxjs57X|*Jz`v!yp diff --git a/static/css/pico-1.5.9/.gitignore b/static/css/pico-1.5.9/.gitignore deleted file mode 100644 index bde7d4c..0000000 --- a/static/css/pico-1.5.9/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules/ -tests/ -.nvmrc -.prettierrc.js -*.DS_Store diff --git a/static/css/pico-1.5.9/LICENSE.md b/static/css/pico-1.5.9/LICENSE.md deleted file mode 100644 index 275ca7e..0000000 --- a/static/css/pico-1.5.9/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019-2023 Pico - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/static/css/pico-1.5.9/README.md b/static/css/pico-1.5.9/README.md deleted file mode 100644 index 083b4da..0000000 --- a/static/css/pico-1.5.9/README.md +++ /dev/null @@ -1,212 +0,0 @@ - - -

Pico CSS

- -

- Minimal CSS Framework for semantic HTML
- Elegant styles for all native HTML elements without .classes and dark mode automatically enabled.

- Examples · - Documentation -

- -## Pico CSS -[![Standard gzipped CSS](https://img.badgesize.io/picocss/pico/master/css/pico.min.css?compression=gzip&color=1095c1&label=Standard%20CSS)](https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css) -[![Classless gzipped CSS](https://img.badgesize.io/picocss/pico/master/css/pico.classless.min.css?compression=gzip&color=1095c1&label=Classless%20CSS)](https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.classless.min.css) -[![Github release](https://img.shields.io/github/v/release/picocss/pico?color=1095c1&logo=github&logoColor=white)](https://github.com/picocss/pico/releases/latest) -[![npm version](https://img.shields.io/npm/v/@picocss/pico?color=1095c1)](https://www.npmjs.com/package/@picocss/pico) -[![License](https://img.shields.io/badge/license-MIT-%231095c1)](https://github.com/picocss/pico/blob/master/LICENSE.md) -[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/picocss.svg?style=social&label=Follow%20%40picocss)](https://twitter.com/picocss) - -https://user-images.githubusercontent.com/23470684/126863110-94061cf1-36ea-4697-94bd-2e1071a95a2f.mp4 - -**Class-light and semantic** -Pico uses simple native HTML tags as much as possible. Less than 10 .classes are used in Pico. - -**Great styles with just one CSS file** -No dependencies, package manager, external files, or JavaScript. - -**Responsive everything** -Elegant and consistent adaptive spacings and typography on all devices. - -**Light or Dark mode** -Shipped with two beautiful color themes, automatically enabled according to the user preference. - -## Table of contents - -- [Usage](#usage) -- [Class-less version](#class-less-version) -- [Examples](#examples) -- [Limitations](#limitations) -- [Documentation](#documentation) -- [Browser Support](#browser-support) -- [Contributing](#contributing) -- [Copyright and license](#copyright-and-license) - -## Usage - -There are 4 ways to get started with Pico CSS: - -**Install manually** - -[Download Pico](https://github.com/picocss/pico/archive/refs/tags/v1.5.9.zip) and link `/css/pico.min.css` in the `` of your website. - -```html - -``` - -**Install from CDN** - -Alternatively, you can use [jsDelivr CDN](https://www.jsdelivr.com/package/npm/@picocss/pico) to link pico.css. - -```html - -``` - -**Install with NPM** - -```shell -npm install @picocss/pico -``` - -**Install with Composer** - -```shell -composer require picocss/pico -``` - -## Class-less version - -Pico provides a `.classless` version ([example](https://picocss.com/examples/classless)). - -In this version, `header`, `main` and `footer` act as containers. - -Use the default `.classless` version if you need centered viewports: - -```html - -``` - -Or use the `.fluid.classless` version if you need a fluid container: - -```html - -``` - -Then just write pure HTML, and it should look great: - -```html - - - - - - - Hello, world! - - -
-

Hello, world!

-
- - -``` - -## Examples - -Minimalist templates to discover Pico in action: - -[![Examples](.github/examples.jpg)](https://picocss.com/#examples) - -- **[Preview](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-preview)** - A starter example with most of the Pico components and styles. - -- **[Right-to-left (RTL) preview](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-preview-rtl)** - A starter example in Arabic with most of the Pico components and styles. - -- **[Classless](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-classless)** - A pure semantic HTML markup, without `.classes`. - -- **[Basic template](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-basic-template)** - A basic custom template for Pico using only CSS custom properties (variables). - -- **[Company](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-company)** - A classic company or blog layout with a sidebar. - -- **[Google Amp](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-google-amp)** - A simple layout for Google Amp, with inlined CSS. - -- **[Sign in](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-sign-in)** -A minimalist layout for Login pages. - -- **[Pico + Bootstrap grid system](https://codesandbox.io/s/github/picocss/examples/tree/master/v1-bootstrap-grid)** - Custom CSS build with the Bootstrap grid system to manage complex grid layouts in Pico. - -All examples are open-sourced in [picocss/examples](https://github.com/picocss/examples). - -## Limitations - -Pico can be used without custom CSS for quick or small projects. However, it’s designed as a starting point, like a “reset CSS on steroids”. As Pico does not integrate any helpers or utilities `.classes`, this minimal CSS framework requires SCSS or CSS knowledge to build large projects. - -## Documentation - -**Getting started** - -- [Usage](https://picocss.com/docs/) -- [Themes](https://picocss.com/docs/themes.html) -- [Customization](https://picocss.com/docs/customization.html) -- [Class-less version](https://picocss.com/docs/classless.html) -- [RTL](https://picocss.com/docs/rtl.html) - -**Layout** - -- [Containers](https://picocss.com/docs/containers.html) -- [Grids](https://picocss.com/docs/grid.html) -- [Horizontal scroller](https://picocss.com/docs/scroller.html) - -**Elements** - -- [Typography](https://picocss.com/docs/typography.html) -- [Buttons](https://picocss.com/docs/buttons.html) -- [Forms](https://picocss.com/docs/forms.html) -- [Tables](https://picocss.com/docs/tables.html) - -**Components** - -- [Accordions](https://picocss.com/docs/accordions.html) -- [Cards](https://picocss.com/docs/cards.html) -- [Dropdowns](https://picocss.com/docs/dropdowns.html) -- [Modal](https://picocss.com/docs/modal.html) -- [Navs](https://picocss.com/docs/navs.html) -- [Progress](https://picocss.com/docs/progress.html) - -**Utilities** - -- [Loading](https://picocss.com/docs/loading.html) -- [Tooltips](https://picocss.com/docs/tooltips.html) - -## Browser support - -Pico is designed and tested for the latest stable Chrome, Firefox, Edge, and Safari releases. It does not support any version of IE, including IE 11. - -## Contributing - -If you are interested in contributing to Pico CSS, please read our [contributing guidelines](https://github.com/picocss/pico/blob/master/.github/CONTRIBUTING.md). - -## Copyright and license - -Licensed under the [MIT License](https://github.com/picocss/pico/blob/master/LICENSE.md). - -**Relevant third-party tools and resources we depend on:** - -Website and docs: -- [TypeIt](https://typeitjs.com/): JavaScript animated typing utility (Licensed [GPL-3.0](https://github.com/alexmacarthur/typeit/blob/master/LICENSE)) -- [Font Awesome](https://fontawesome.com/): Icons (Licensed [CC BY 4.0](https://fontawesome.com/license/free)) - -Pico Library: -- [Feather](https://feathericons.com/) Icons (Licensed [MIT](https://github.com/feathericons/feather/blob/master/LICENSE)) -- [Normalize.css](https://necolas.github.io/normalize.css/): CSS reset (Licensed [MIT](https://github.com/necolas/normalize.css/blob/master/LICENSE.md)) -- [Sanitize.css](https://csstools.github.io/sanitize.css/): Cross-browser default styling (Licensed [CC0 1.0 Universal](https://github.com/csstools/sanitize.css/blob/main/LICENSE.md)) diff --git a/static/css/pico-1.5.9/composer.json b/static/css/pico-1.5.9/composer.json deleted file mode 100644 index 2417f21..0000000 --- a/static/css/pico-1.5.9/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "picocss/pico", - "description": "Minimal CSS Framework for semantic HTML.", - "keywords": [ - "css", - "css-framework", - "dark-mode", - "dark-theme", - "lightweight", - "minimal", - "minimalist", - "minimalistic", - "native-html", - "scss-framework", - "semantic" - ], - "homepage": "https://picocss.com", - "authors": [ - { - "name": "Lucas Larroche", - "email": "lucas@larroche.com", - "homepage": "https://lucaslarroche.com", - "role": "Developer" - } - ], - "support": { - "issues": "https://github.com/picocss/pico/issues/" - }, - "license": "MIT" -} \ No newline at end of file diff --git a/static/css/pico-1.5.9/css/pico.classless.css b/static/css/pico-1.5.9/css/pico.classless.css deleted file mode 100644 index dfc0473..0000000 --- a/static/css/pico-1.5.9/css/pico.classless.css +++ /dev/null @@ -1,2498 +0,0 @@ -@charset "UTF-8"; -/*! - * Pico CSS v1.5.9 (https://picocss.com) - * Copyright 2019-2023 - Licensed under MIT - */ -/** - * Theme: default - */ -:root { - --font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", - "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol", "Noto Color Emoji"; - --line-height: 1.5; - --font-weight: 400; - --font-size: 16px; - --border-radius: 0.25rem; - --border-width: 1px; - --outline-width: 3px; - --spacing: 1rem; - --typography-spacing-vertical: 1.5rem; - --block-spacing-vertical: calc(var(--spacing) * 2); - --block-spacing-horizontal: var(--spacing); - --form-element-spacing-vertical: 0.75rem; - --form-element-spacing-horizontal: 1rem; - --nav-element-spacing-vertical: 1rem; - --nav-element-spacing-horizontal: 0.5rem; - --nav-link-spacing-vertical: 0.5rem; - --nav-link-spacing-horizontal: 0.5rem; - --form-label-font-weight: var(--font-weight); - --transition: 0.2s ease-in-out; - --modal-overlay-backdrop-filter: blur(0.25rem); -} -@media (min-width: 576px) { - :root { - --font-size: 17px; - } -} -@media (min-width: 768px) { - :root { - --font-size: 18px; - } -} -@media (min-width: 992px) { - :root { - --font-size: 19px; - } -} -@media (min-width: 1200px) { - :root { - --font-size: 20px; - } -} - -@media (min-width: 576px) { - body > header, - body > main, - body > footer, - section { - --block-spacing-vertical: calc(var(--spacing) * 2.5); - } -} -@media (min-width: 768px) { - body > header, - body > main, - body > footer, - section { - --block-spacing-vertical: calc(var(--spacing) * 3); - } -} -@media (min-width: 992px) { - body > header, - body > main, - body > footer, - section { - --block-spacing-vertical: calc(var(--spacing) * 3.5); - } -} -@media (min-width: 1200px) { - body > header, - body > main, - body > footer, - section { - --block-spacing-vertical: calc(var(--spacing) * 4); - } -} - -@media (min-width: 576px) { - article { - --block-spacing-horizontal: calc(var(--spacing) * 1.25); - } -} -@media (min-width: 768px) { - article { - --block-spacing-horizontal: calc(var(--spacing) * 1.5); - } -} -@media (min-width: 992px) { - article { - --block-spacing-horizontal: calc(var(--spacing) * 1.75); - } -} -@media (min-width: 1200px) { - article { - --block-spacing-horizontal: calc(var(--spacing) * 2); - } -} - -dialog > article { - --block-spacing-vertical: calc(var(--spacing) * 2); - --block-spacing-horizontal: var(--spacing); -} -@media (min-width: 576px) { - dialog > article { - --block-spacing-vertical: calc(var(--spacing) * 2.5); - --block-spacing-horizontal: calc(var(--spacing) * 1.25); - } -} -@media (min-width: 768px) { - dialog > article { - --block-spacing-vertical: calc(var(--spacing) * 3); - --block-spacing-horizontal: calc(var(--spacing) * 1.5); - } -} - -a { - --text-decoration: none; -} - -small { - --font-size: 0.875em; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - --font-weight: 700; -} - -h1 { - --font-size: 2rem; - --typography-spacing-vertical: 3rem; -} - -h2 { - --font-size: 1.75rem; - --typography-spacing-vertical: 2.625rem; -} - -h3 { - --font-size: 1.5rem; - --typography-spacing-vertical: 2.25rem; -} - -h4 { - --font-size: 1.25rem; - --typography-spacing-vertical: 1.874rem; -} - -h5 { - --font-size: 1.125rem; - --typography-spacing-vertical: 1.6875rem; -} - -[type=checkbox], -[type=radio] { - --border-width: 2px; -} - -[type=checkbox][role=switch] { - --border-width: 3px; -} - -thead th, -thead td, -tfoot th, -tfoot td { - --border-width: 3px; -} - -:not(thead, tfoot) > * > td { - --font-size: 0.875em; -} - -pre, -code, -kbd, -samp { - --font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", - "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - -kbd { - --font-weight: bolder; -} - -[data-theme=light], -:root:not([data-theme=dark]) { - --background-color: #fff; - --color: hsl(205, 20%, 32%); - --h1-color: hsl(205, 30%, 15%); - --h2-color: #24333e; - --h3-color: hsl(205, 25%, 23%); - --h4-color: #374956; - --h5-color: hsl(205, 20%, 32%); - --h6-color: #4d606d; - --muted-color: hsl(205, 10%, 50%); - --muted-border-color: hsl(205, 20%, 94%); - --primary: hsl(195, 85%, 41%); - --primary-hover: hsl(195, 90%, 32%); - --primary-focus: rgba(16, 149, 193, 0.125); - --primary-inverse: #fff; - --secondary: hsl(205, 15%, 41%); - --secondary-hover: hsl(205, 20%, 32%); - --secondary-focus: rgba(89, 107, 120, 0.125); - --secondary-inverse: #fff; - --contrast: hsl(205, 30%, 15%); - --contrast-hover: #000; - --contrast-focus: rgba(89, 107, 120, 0.125); - --contrast-inverse: #fff; - --mark-background-color: #fff2ca; - --mark-color: #543a26; - --ins-color: #388e3c; - --del-color: #c62828; - --blockquote-border-color: var(--muted-border-color); - --blockquote-footer-color: var(--muted-color); - --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --form-element-background-color: transparent; - --form-element-border-color: hsl(205, 14%, 68%); - --form-element-color: var(--color); - --form-element-placeholder-color: var(--muted-color); - --form-element-active-background-color: transparent; - --form-element-active-border-color: var(--primary); - --form-element-focus-color: var(--primary-focus); - --form-element-disabled-background-color: hsl(205, 18%, 86%); - --form-element-disabled-border-color: hsl(205, 14%, 68%); - --form-element-disabled-opacity: 0.5; - --form-element-invalid-border-color: #c62828; - --form-element-invalid-active-border-color: #d32f2f; - --form-element-invalid-focus-color: rgba(211, 47, 47, 0.125); - --form-element-valid-border-color: #388e3c; - --form-element-valid-active-border-color: #43a047; - --form-element-valid-focus-color: rgba(67, 160, 71, 0.125); - --switch-background-color: hsl(205, 16%, 77%); - --switch-color: var(--primary-inverse); - --switch-checked-background-color: var(--primary); - --range-border-color: hsl(205, 18%, 86%); - --range-active-border-color: hsl(205, 16%, 77%); - --range-thumb-border-color: var(--background-color); - --range-thumb-color: var(--secondary); - --range-thumb-hover-color: var(--secondary-hover); - --range-thumb-active-color: var(--primary); - --table-border-color: var(--muted-border-color); - --table-row-stripped-background-color: #f6f8f9; - --code-background-color: hsl(205, 20%, 94%); - --code-color: var(--muted-color); - --code-kbd-background-color: var(--contrast); - --code-kbd-color: var(--contrast-inverse); - --code-tag-color: hsl(330, 40%, 50%); - --code-property-color: hsl(185, 40%, 40%); - --code-value-color: hsl(40, 20%, 50%); - --code-comment-color: hsl(205, 14%, 68%); - --accordion-border-color: var(--muted-border-color); - --accordion-close-summary-color: var(--color); - --accordion-open-summary-color: var(--muted-color); - --card-background-color: var(--background-color); - --card-border-color: var(--muted-border-color); - --card-box-shadow: - 0.0145rem 0.029rem 0.174rem rgba(27, 40, 50, 0.01698), - 0.0335rem 0.067rem 0.402rem rgba(27, 40, 50, 0.024), - 0.0625rem 0.125rem 0.75rem rgba(27, 40, 50, 0.03), - 0.1125rem 0.225rem 1.35rem rgba(27, 40, 50, 0.036), - 0.2085rem 0.417rem 2.502rem rgba(27, 40, 50, 0.04302), - 0.5rem 1rem 6rem rgba(27, 40, 50, 0.06), - 0 0 0 0.0625rem rgba(27, 40, 50, 0.015); - --card-sectionning-background-color: #fbfbfc; - --dropdown-background-color: #fbfbfc; - --dropdown-border-color: #e1e6eb; - --dropdown-box-shadow: var(--card-box-shadow); - --dropdown-color: var(--color); - --dropdown-hover-background-color: hsl(205, 20%, 94%); - --modal-overlay-background-color: rgba(213, 220, 226, 0.7); - --progress-background-color: hsl(205, 18%, 86%); - --progress-color: var(--primary); - --loading-spinner-opacity: 0.5; - --tooltip-background-color: var(--contrast); - --tooltip-color: var(--contrast-inverse); - --icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); - --icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); - --icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); - --icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); - --icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); - --icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - color-scheme: light; -} - -@media only screen and (prefers-color-scheme: dark) { - :root:not([data-theme]) { - --background-color: #11191f; - --color: hsl(205, 16%, 77%); - --h1-color: hsl(205, 20%, 94%); - --h2-color: #e1e6eb; - --h3-color: hsl(205, 18%, 86%); - --h4-color: #c8d1d8; - --h5-color: hsl(205, 16%, 77%); - --h6-color: #afbbc4; - --muted-color: hsl(205, 10%, 50%); - --muted-border-color: #1f2d38; - --primary: hsl(195, 85%, 41%); - --primary-hover: hsl(195, 80%, 50%); - --primary-focus: rgba(16, 149, 193, 0.25); - --primary-inverse: #fff; - --secondary: hsl(205, 15%, 41%); - --secondary-hover: hsl(205, 10%, 50%); - --secondary-focus: rgba(115, 130, 140, 0.25); - --secondary-inverse: #fff; - --contrast: hsl(205, 20%, 94%); - --contrast-hover: #fff; - --contrast-focus: rgba(115, 130, 140, 0.25); - --contrast-inverse: #000; - --mark-background-color: #d1c284; - --mark-color: #11191f; - --ins-color: #388e3c; - --del-color: #c62828; - --blockquote-border-color: var(--muted-border-color); - --blockquote-footer-color: var(--muted-color); - --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --form-element-background-color: #11191f; - --form-element-border-color: #374956; - --form-element-color: var(--color); - --form-element-placeholder-color: var(--muted-color); - --form-element-active-background-color: var(--form-element-background-color); - --form-element-active-border-color: var(--primary); - --form-element-focus-color: var(--primary-focus); - --form-element-disabled-background-color: hsl(205, 25%, 23%); - --form-element-disabled-border-color: hsl(205, 20%, 32%); - --form-element-disabled-opacity: 0.5; - --form-element-invalid-border-color: #b71c1c; - --form-element-invalid-active-border-color: #c62828; - --form-element-invalid-focus-color: rgba(198, 40, 40, 0.25); - --form-element-valid-border-color: #2e7d32; - --form-element-valid-active-border-color: #388e3c; - --form-element-valid-focus-color: rgba(56, 142, 60, 0.25); - --switch-background-color: #374956; - --switch-color: var(--primary-inverse); - --switch-checked-background-color: var(--primary); - --range-border-color: #24333e; - --range-active-border-color: hsl(205, 25%, 23%); - --range-thumb-border-color: var(--background-color); - --range-thumb-color: var(--secondary); - --range-thumb-hover-color: var(--secondary-hover); - --range-thumb-active-color: var(--primary); - --table-border-color: var(--muted-border-color); - --table-row-stripped-background-color: rgba(115, 130, 140, 0.05); - --code-background-color: #18232c; - --code-color: var(--muted-color); - --code-kbd-background-color: var(--contrast); - --code-kbd-color: var(--contrast-inverse); - --code-tag-color: hsl(330, 30%, 50%); - --code-property-color: hsl(185, 30%, 50%); - --code-value-color: hsl(40, 10%, 50%); - --code-comment-color: #4d606d; - --accordion-border-color: var(--muted-border-color); - --accordion-active-summary-color: var(--primary); - --accordion-close-summary-color: var(--color); - --accordion-open-summary-color: var(--muted-color); - --card-background-color: #141e26; - --card-border-color: var(--card-background-color); - --card-box-shadow: - 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), - 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), - 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), - 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), - 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), - 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), - 0 0 0 0.0625rem rgba(0, 0, 0, 0.015); - --card-sectionning-background-color: #18232c; - --dropdown-background-color: hsl(205, 30%, 15%); - --dropdown-border-color: #24333e; - --dropdown-box-shadow: var(--card-box-shadow); - --dropdown-color: var(--color); - --dropdown-hover-background-color: rgba(36, 51, 62, 0.75); - --modal-overlay-background-color: rgba(36, 51, 62, 0.8); - --progress-background-color: #24333e; - --progress-color: var(--primary); - --loading-spinner-opacity: 0.5; - --tooltip-background-color: var(--contrast); - --tooltip-color: var(--contrast-inverse); - --icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); - --icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); - --icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); - --icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); - --icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); - --icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - color-scheme: dark; - } -} -[data-theme=dark] { - --background-color: #11191f; - --color: hsl(205, 16%, 77%); - --h1-color: hsl(205, 20%, 94%); - --h2-color: #e1e6eb; - --h3-color: hsl(205, 18%, 86%); - --h4-color: #c8d1d8; - --h5-color: hsl(205, 16%, 77%); - --h6-color: #afbbc4; - --muted-color: hsl(205, 10%, 50%); - --muted-border-color: #1f2d38; - --primary: hsl(195, 85%, 41%); - --primary-hover: hsl(195, 80%, 50%); - --primary-focus: rgba(16, 149, 193, 0.25); - --primary-inverse: #fff; - --secondary: hsl(205, 15%, 41%); - --secondary-hover: hsl(205, 10%, 50%); - --secondary-focus: rgba(115, 130, 140, 0.25); - --secondary-inverse: #fff; - --contrast: hsl(205, 20%, 94%); - --contrast-hover: #fff; - --contrast-focus: rgba(115, 130, 140, 0.25); - --contrast-inverse: #000; - --mark-background-color: #d1c284; - --mark-color: #11191f; - --ins-color: #388e3c; - --del-color: #c62828; - --blockquote-border-color: var(--muted-border-color); - --blockquote-footer-color: var(--muted-color); - --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0); - --form-element-background-color: #11191f; - --form-element-border-color: #374956; - --form-element-color: var(--color); - --form-element-placeholder-color: var(--muted-color); - --form-element-active-background-color: var(--form-element-background-color); - --form-element-active-border-color: var(--primary); - --form-element-focus-color: var(--primary-focus); - --form-element-disabled-background-color: hsl(205, 25%, 23%); - --form-element-disabled-border-color: hsl(205, 20%, 32%); - --form-element-disabled-opacity: 0.5; - --form-element-invalid-border-color: #b71c1c; - --form-element-invalid-active-border-color: #c62828; - --form-element-invalid-focus-color: rgba(198, 40, 40, 0.25); - --form-element-valid-border-color: #2e7d32; - --form-element-valid-active-border-color: #388e3c; - --form-element-valid-focus-color: rgba(56, 142, 60, 0.25); - --switch-background-color: #374956; - --switch-color: var(--primary-inverse); - --switch-checked-background-color: var(--primary); - --range-border-color: #24333e; - --range-active-border-color: hsl(205, 25%, 23%); - --range-thumb-border-color: var(--background-color); - --range-thumb-color: var(--secondary); - --range-thumb-hover-color: var(--secondary-hover); - --range-thumb-active-color: var(--primary); - --table-border-color: var(--muted-border-color); - --table-row-stripped-background-color: rgba(115, 130, 140, 0.05); - --code-background-color: #18232c; - --code-color: var(--muted-color); - --code-kbd-background-color: var(--contrast); - --code-kbd-color: var(--contrast-inverse); - --code-tag-color: hsl(330, 30%, 50%); - --code-property-color: hsl(185, 30%, 50%); - --code-value-color: hsl(40, 10%, 50%); - --code-comment-color: #4d606d; - --accordion-border-color: var(--muted-border-color); - --accordion-active-summary-color: var(--primary); - --accordion-close-summary-color: var(--color); - --accordion-open-summary-color: var(--muted-color); - --card-background-color: #141e26; - --card-border-color: var(--card-background-color); - --card-box-shadow: - 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698), - 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024), - 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03), - 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036), - 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302), - 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06), - 0 0 0 0.0625rem rgba(0, 0, 0, 0.015); - --card-sectionning-background-color: #18232c; - --dropdown-background-color: hsl(205, 30%, 15%); - --dropdown-border-color: #24333e; - --dropdown-box-shadow: var(--card-box-shadow); - --dropdown-color: var(--color); - --dropdown-hover-background-color: rgba(36, 51, 62, 0.75); - --modal-overlay-background-color: rgba(36, 51, 62, 0.8); - --progress-background-color: #24333e; - --progress-color: var(--primary); - --loading-spinner-opacity: 0.5; - --tooltip-background-color: var(--contrast); - --tooltip-color: var(--contrast-inverse); - --icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); - --icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E"); - --icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E"); - --icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E"); - --icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E"); - --icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E"); - --icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E"); - color-scheme: dark; -} - -progress, -[type=checkbox], -[type=radio], -[type=range] { - accent-color: var(--primary); -} - -/** - * Document - * Content-box & Responsive typography - */ -*, -*::before, -*::after { - box-sizing: border-box; - background-repeat: no-repeat; -} - -::before, -::after { - text-decoration: inherit; - vertical-align: inherit; -} - -:where(:root) { - -webkit-tap-highlight-color: transparent; - -webkit-text-size-adjust: 100%; - -moz-text-size-adjust: 100%; - text-size-adjust: 100%; - background-color: var(--background-color); - color: var(--color); - font-weight: var(--font-weight); - font-size: var(--font-size); - line-height: var(--line-height); - font-family: var(--font-family); - text-rendering: optimizeLegibility; - overflow-wrap: break-word; - cursor: default; - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; -} - -/** - * Sectioning - * Container and responsive spacings for header, main, footer - */ -main { - display: block; -} - -body { - width: 100%; - margin: 0; -} -body > header, -body > main, -body > footer { - width: 100%; - margin-right: auto; - margin-left: auto; - padding: var(--block-spacing-vertical) var(--block-spacing-horizontal); -} -@media (min-width: 576px) { - body > header, - body > main, - body > footer { - max-width: 510px; - padding-right: 0; - padding-left: 0; - } -} -@media (min-width: 768px) { - body > header, - body > main, - body > footer { - max-width: 700px; - } -} -@media (min-width: 992px) { - body > header, - body > main, - body > footer { - max-width: 920px; - } -} -@media (min-width: 1200px) { - body > header, - body > main, - body > footer { - max-width: 1130px; - } -} - -/** - * Section - * Responsive spacings for section - */ -section { - margin-bottom: var(--block-spacing-vertical); -} - -/** - * Horizontal scroller (
) - */ -figure { - display: block; - margin: 0; - padding: 0; - overflow-x: auto; -} -figure figcaption { - padding: calc(var(--spacing) * 0.5) 0; - color: var(--muted-color); -} - -/** - * Typography - */ -b, -strong { - font-weight: bolder; -} - -sub, -sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -address, -blockquote, -dl, -figure, -form, -ol, -p, -pre, -table, -ul { - margin-top: 0; - margin-bottom: var(--typography-spacing-vertical); - color: var(--color); - font-style: normal; - font-weight: var(--font-weight); - font-size: var(--font-size); -} - -a, -[role=link] { - --color: var(--primary); - --background-color: transparent; - outline: none; - background-color: var(--background-color); - color: var(--color); - -webkit-text-decoration: var(--text-decoration); - text-decoration: var(--text-decoration); - transition: background-color var(--transition), color var(--transition), box-shadow var(--transition), -webkit-text-decoration var(--transition); - transition: background-color var(--transition), color var(--transition), text-decoration var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), color var(--transition), text-decoration var(--transition), box-shadow var(--transition), -webkit-text-decoration var(--transition); -} -a:is([aria-current], :hover, :active, :focus), -[role=link]:is([aria-current], :hover, :active, :focus) { - --color: var(--primary-hover); - --text-decoration: underline; -} -a:focus, -[role=link]:focus { - --background-color: var(--primary-focus); -} - -h1, -h2, -h3, -h4, -h5, -h6 { - margin-top: 0; - margin-bottom: var(--typography-spacing-vertical); - color: var(--color); - font-weight: var(--font-weight); - font-size: var(--font-size); - font-family: var(--font-family); -} - -h1 { - --color: var(--h1-color); -} - -h2 { - --color: var(--h2-color); -} - -h3 { - --color: var(--h3-color); -} - -h4 { - --color: var(--h4-color); -} - -h5 { - --color: var(--h5-color); -} - -h6 { - --color: var(--h6-color); -} - -:where(address, blockquote, dl, figure, form, ol, p, pre, table, ul) ~ :is(h1, h2, h3, h4, h5, h6) { - margin-top: var(--typography-spacing-vertical); -} - -hgroup { - margin-bottom: var(--typography-spacing-vertical); -} -hgroup > * { - margin-bottom: 0; -} -hgroup > *:last-child { - --color: var(--muted-color); - --font-weight: unset; - font-size: 1rem; - font-family: unset; -} - -p { - margin-bottom: var(--typography-spacing-vertical); -} - -small { - font-size: var(--font-size); -} - -:where(dl, ol, ul) { - padding-right: 0; - padding-left: var(--spacing); - -webkit-padding-start: var(--spacing); - padding-inline-start: var(--spacing); - -webkit-padding-end: 0; - padding-inline-end: 0; -} -:where(dl, ol, ul) li { - margin-bottom: calc(var(--typography-spacing-vertical) * 0.25); -} - -:where(dl, ol, ul) :is(dl, ol, ul) { - margin: 0; - margin-top: calc(var(--typography-spacing-vertical) * 0.25); -} - -ul li { - list-style: square; -} - -mark { - padding: 0.125rem 0.25rem; - background-color: var(--mark-background-color); - color: var(--mark-color); - vertical-align: baseline; -} - -blockquote { - display: block; - margin: var(--typography-spacing-vertical) 0; - padding: var(--spacing); - border-right: none; - border-left: 0.25rem solid var(--blockquote-border-color); - -webkit-border-start: 0.25rem solid var(--blockquote-border-color); - border-inline-start: 0.25rem solid var(--blockquote-border-color); - -webkit-border-end: none; - border-inline-end: none; -} -blockquote footer { - margin-top: calc(var(--typography-spacing-vertical) * 0.5); - color: var(--blockquote-footer-color); -} - -abbr[title] { - border-bottom: 1px dotted; - text-decoration: none; - cursor: help; -} - -ins { - color: var(--ins-color); - text-decoration: none; -} - -del { - color: var(--del-color); -} - -::-moz-selection { - background-color: var(--primary-focus); -} - -::selection { - background-color: var(--primary-focus); -} - -/** - * Embedded content - */ -:where(audio, canvas, iframe, img, svg, video) { - vertical-align: middle; -} - -audio, -video { - display: inline-block; -} - -audio:not([controls]) { - display: none; - height: 0; -} - -:where(iframe) { - border-style: none; -} - -img { - max-width: 100%; - height: auto; - border-style: none; -} - -:where(svg:not([fill])) { - fill: currentColor; -} - -svg:not(:root) { - overflow: hidden; -} - -/** - * Button - */ -button { - margin: 0; - overflow: visible; - font-family: inherit; - text-transform: none; -} - -button, -[type=button], -[type=reset], -[type=submit] { - -webkit-appearance: button; -} - -button { - display: block; - width: 100%; - margin-bottom: var(--spacing); -} - -[role=button] { - display: inline-block; - text-decoration: none; -} - -button, -input[type=submit], -input[type=button], -input[type=reset], -[role=button] { - --background-color: var(--primary); - --border-color: var(--primary); - --color: var(--primary-inverse); - --box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); - padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal); - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius); - outline: none; - background-color: var(--background-color); - box-shadow: var(--box-shadow); - color: var(--color); - font-weight: var(--font-weight); - font-size: 1rem; - line-height: var(--line-height); - text-align: center; - cursor: pointer; - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} -button:is([aria-current], :hover, :active, :focus), -input[type=submit]:is([aria-current], :hover, :active, :focus), -input[type=button]:is([aria-current], :hover, :active, :focus), -input[type=reset]:is([aria-current], :hover, :active, :focus), -[role=button]:is([aria-current], :hover, :active, :focus) { - --background-color: var(--primary-hover); - --border-color: var(--primary-hover); - --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)); - --color: var(--primary-inverse); -} -button:focus, -input[type=submit]:focus, -input[type=button]:focus, -input[type=reset]:focus, -[role=button]:focus { - --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), - 0 0 0 var(--outline-width) var(--primary-focus); -} - -input[type=reset] { - --background-color: var(--secondary); - --border-color: var(--secondary); - --color: var(--secondary-inverse); - cursor: pointer; -} -input[type=reset]:is([aria-current], :hover, :active, :focus) { - --background-color: var(--secondary-hover); - --border-color: var(--secondary-hover); -} -input[type=reset]:focus { - --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), - 0 0 0 var(--outline-width) var(--secondary-focus); -} - -:where(button, [type=submit], [type=button], [type=reset], [role=button])[disabled], -:where(fieldset[disabled]) :is(button, [type=submit], [type=button], [type=reset], [role=button]), -a[role=button]:not([href]) { - opacity: 0.5; - pointer-events: none; -} - -/** - * Form elements - */ -input, -optgroup, -select, -textarea { - margin: 0; - font-size: 1rem; - line-height: var(--line-height); - font-family: inherit; - letter-spacing: inherit; -} - -input { - overflow: visible; -} - -select { - text-transform: none; -} - -legend { - max-width: 100%; - padding: 0; - color: inherit; - white-space: normal; -} - -textarea { - overflow: auto; -} - -[type=checkbox], -[type=radio] { - padding: 0; -} - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -[type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; -} - -[type=search]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} - -::-moz-focus-inner { - padding: 0; - border-style: none; -} - -:-moz-focusring { - outline: none; -} - -:-moz-ui-invalid { - box-shadow: none; -} - -::-ms-expand { - display: none; -} - -[type=file], -[type=range] { - padding: 0; - border-width: 0; -} - -input:not([type=checkbox], [type=radio], [type=range]) { - height: calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2); -} - -fieldset { - margin: 0; - margin-bottom: var(--spacing); - padding: 0; - border: 0; -} - -label, -fieldset legend { - display: block; - margin-bottom: calc(var(--spacing) * 0.25); - font-weight: var(--form-label-font-weight, var(--font-weight)); -} - -input:not([type=checkbox], [type=radio]), -select, -textarea { - width: 100%; -} - -input:not([type=checkbox], [type=radio], [type=range], [type=file]), -select, -textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal); -} - -input, -select, -textarea { - --background-color: var(--form-element-background-color); - --border-color: var(--form-element-border-color); - --color: var(--form-element-color); - --box-shadow: none; - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius); - outline: none; - background-color: var(--background-color); - box-shadow: var(--box-shadow); - color: var(--color); - font-weight: var(--font-weight); - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} - -input:not([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [readonly]):is(:active, :focus), -:where(select, textarea):is(:active, :focus) { - --background-color: var(--form-element-active-background-color); -} - -input:not([type=submit], [type=button], [type=reset], [role=switch], [readonly]):is(:active, :focus), -:where(select, textarea):is(:active, :focus) { - --border-color: var(--form-element-active-border-color); -} - -input:not([type=submit], [type=button], [type=reset], [type=range], [type=file], [readonly]):focus, -select:focus, -textarea:focus { - --box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color); -} - -input:not([type=submit], [type=button], [type=reset])[disabled], -select[disabled], -textarea[disabled], -:where(fieldset[disabled]) :is(input:not([type=submit], [type=button], [type=reset]), select, textarea) { - --background-color: var(--form-element-disabled-background-color); - --border-color: var(--form-element-disabled-border-color); - opacity: var(--form-element-disabled-opacity); - pointer-events: none; -} - -:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid] { - padding-right: calc(var(--form-element-spacing-horizontal) + 1.5rem) !important; - padding-left: var(--form-element-spacing-horizontal); - -webkit-padding-start: var(--form-element-spacing-horizontal) !important; - padding-inline-start: var(--form-element-spacing-horizontal) !important; - -webkit-padding-end: calc(var(--form-element-spacing-horizontal) + 1.5rem) !important; - padding-inline-end: calc(var(--form-element-spacing-horizontal) + 1.5rem) !important; - background-position: center right 0.75rem; - background-size: 1rem auto; - background-repeat: no-repeat; -} -:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid=false] { - background-image: var(--icon-valid); -} -:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid=true] { - background-image: var(--icon-invalid); -} -:where(input, select, textarea)[aria-invalid=false] { - --border-color: var(--form-element-valid-border-color); -} -:where(input, select, textarea)[aria-invalid=false]:is(:active, :focus) { - --border-color: var(--form-element-valid-active-border-color) !important; - --box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important; -} -:where(input, select, textarea)[aria-invalid=true] { - --border-color: var(--form-element-invalid-border-color); -} -:where(input, select, textarea)[aria-invalid=true]:is(:active, :focus) { - --border-color: var(--form-element-invalid-active-border-color) !important; - --box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important; -} - -[dir=rtl] :where(input, select, textarea):not([type=checkbox], [type=radio]):is([aria-invalid], [aria-invalid=true], [aria-invalid=false]) { - background-position: center left 0.75rem; -} - -input::placeholder, -input::-webkit-input-placeholder, -textarea::placeholder, -textarea::-webkit-input-placeholder, -select:invalid { - color: var(--form-element-placeholder-color); - opacity: 1; -} - -input:not([type=checkbox], [type=radio]), -select, -textarea { - margin-bottom: var(--spacing); -} - -select::-ms-expand { - border: 0; - background-color: transparent; -} -select:not([multiple], [size]) { - padding-right: calc(var(--form-element-spacing-horizontal) + 1.5rem); - padding-left: var(--form-element-spacing-horizontal); - -webkit-padding-start: var(--form-element-spacing-horizontal); - padding-inline-start: var(--form-element-spacing-horizontal); - -webkit-padding-end: calc(var(--form-element-spacing-horizontal) + 1.5rem); - padding-inline-end: calc(var(--form-element-spacing-horizontal) + 1.5rem); - background-image: var(--icon-chevron); - background-position: center right 0.75rem; - background-size: 1rem auto; - background-repeat: no-repeat; -} - -[dir=rtl] select:not([multiple], [size]) { - background-position: center left 0.75rem; -} - -:where(input, select, textarea) + small { - display: block; - width: 100%; - margin-top: calc(var(--spacing) * -0.75); - margin-bottom: var(--spacing); - color: var(--muted-color); -} - -label > :where(input, select, textarea) { - margin-top: calc(var(--spacing) * 0.25); -} - -/** - * Form elements - * Checkboxes & Radios - */ -[type=checkbox], -[type=radio] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - width: 1.25em; - height: 1.25em; - margin-top: -0.125em; - margin-right: 0.375em; - margin-left: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: 0.375em; - margin-inline-end: 0.375em; - border-width: var(--border-width); - font-size: inherit; - vertical-align: middle; - cursor: pointer; -} -[type=checkbox]::-ms-check, -[type=radio]::-ms-check { - display: none; -} -[type=checkbox]:checked, [type=checkbox]:checked:active, [type=checkbox]:checked:focus, -[type=radio]:checked, -[type=radio]:checked:active, -[type=radio]:checked:focus { - --background-color: var(--primary); - --border-color: var(--primary); - background-image: var(--icon-checkbox); - background-position: center; - background-size: 0.75em auto; - background-repeat: no-repeat; -} -[type=checkbox] ~ label, -[type=radio] ~ label { - display: inline-block; - margin-right: 0.375em; - margin-bottom: 0; - cursor: pointer; -} - -[type=checkbox]:indeterminate { - --background-color: var(--primary); - --border-color: var(--primary); - background-image: var(--icon-minus); - background-position: center; - background-size: 0.75em auto; - background-repeat: no-repeat; -} - -[type=radio] { - border-radius: 50%; -} -[type=radio]:checked, [type=radio]:checked:active, [type=radio]:checked:focus { - --background-color: var(--primary-inverse); - border-width: 0.35em; - background-image: none; -} - -[type=checkbox][role=switch] { - --background-color: var(--switch-background-color); - --border-color: var(--switch-background-color); - --color: var(--switch-color); - width: 2.25em; - height: 1.25em; - border: var(--border-width) solid var(--border-color); - border-radius: 1.25em; - background-color: var(--background-color); - line-height: 1.25em; -} -[type=checkbox][role=switch]:focus { - --background-color: var(--switch-background-color); - --border-color: var(--switch-background-color); -} -[type=checkbox][role=switch]:checked { - --background-color: var(--switch-checked-background-color); - --border-color: var(--switch-checked-background-color); -} -[type=checkbox][role=switch]:before { - display: block; - width: calc(1.25em - (var(--border-width) * 2)); - height: 100%; - border-radius: 50%; - background-color: var(--color); - content: ""; - transition: margin 0.1s ease-in-out; -} -[type=checkbox][role=switch]:checked { - background-image: none; -} -[type=checkbox][role=switch]:checked::before { - margin-left: calc(1.125em - var(--border-width)); - -webkit-margin-start: calc(1.125em - var(--border-width)); - margin-inline-start: calc(1.125em - var(--border-width)); -} - -[type=checkbox][aria-invalid=false], -[type=checkbox]:checked[aria-invalid=false], -[type=radio][aria-invalid=false], -[type=radio]:checked[aria-invalid=false], -[type=checkbox][role=switch][aria-invalid=false], -[type=checkbox][role=switch]:checked[aria-invalid=false] { - --border-color: var(--form-element-valid-border-color); -} -[type=checkbox][aria-invalid=true], -[type=checkbox]:checked[aria-invalid=true], -[type=radio][aria-invalid=true], -[type=radio]:checked[aria-invalid=true], -[type=checkbox][role=switch][aria-invalid=true], -[type=checkbox][role=switch]:checked[aria-invalid=true] { - --border-color: var(--form-element-invalid-border-color); -} - -/** - * Form elements - * Alternatives input types (Not Checkboxes & Radios) - */ -[type=color]::-webkit-color-swatch-wrapper { - padding: 0; -} -[type=color]::-moz-focus-inner { - padding: 0; -} -[type=color]::-webkit-color-swatch { - border: 0; - border-radius: calc(var(--border-radius) * 0.5); -} -[type=color]::-moz-color-swatch { - border: 0; - border-radius: calc(var(--border-radius) * 0.5); -} - -input:not([type=checkbox], [type=radio], [type=range], [type=file]):is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) { - --icon-position: 0.75rem; - --icon-width: 1rem; - padding-right: calc(var(--icon-width) + var(--icon-position)); - background-image: var(--icon-date); - background-position: center right var(--icon-position); - background-size: var(--icon-width) auto; - background-repeat: no-repeat; -} -input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=time] { - background-image: var(--icon-time); -} - -[type=date]::-webkit-calendar-picker-indicator, -[type=datetime-local]::-webkit-calendar-picker-indicator, -[type=month]::-webkit-calendar-picker-indicator, -[type=time]::-webkit-calendar-picker-indicator, -[type=week]::-webkit-calendar-picker-indicator { - width: var(--icon-width); - margin-right: calc(var(--icon-width) * -1); - margin-left: var(--icon-position); - opacity: 0; -} - -[dir=rtl] :is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) { - text-align: right; -} - -[type=file] { - --color: var(--muted-color); - padding: calc(var(--form-element-spacing-vertical) * 0.5) 0; - border: 0; - border-radius: 0; - background: none; -} -[type=file]::file-selector-button { - --background-color: var(--secondary); - --border-color: var(--secondary); - --color: var(--secondary-inverse); - margin-right: calc(var(--spacing) / 2); - margin-left: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: calc(var(--spacing) / 2); - margin-inline-end: calc(var(--spacing) / 2); - padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5); - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius); - outline: none; - background-color: var(--background-color); - box-shadow: var(--box-shadow); - color: var(--color); - font-weight: var(--font-weight); - font-size: 1rem; - line-height: var(--line-height); - text-align: center; - cursor: pointer; - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} -[type=file]::file-selector-button:is(:hover, :active, :focus) { - --background-color: var(--secondary-hover); - --border-color: var(--secondary-hover); -} -[type=file]::-webkit-file-upload-button { - --background-color: var(--secondary); - --border-color: var(--secondary); - --color: var(--secondary-inverse); - margin-right: calc(var(--spacing) / 2); - margin-left: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: calc(var(--spacing) / 2); - margin-inline-end: calc(var(--spacing) / 2); - padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5); - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius); - outline: none; - background-color: var(--background-color); - box-shadow: var(--box-shadow); - color: var(--color); - font-weight: var(--font-weight); - font-size: 1rem; - line-height: var(--line-height); - text-align: center; - cursor: pointer; - -webkit-transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} -[type=file]::-webkit-file-upload-button:is(:hover, :active, :focus) { - --background-color: var(--secondary-hover); - --border-color: var(--secondary-hover); -} -[type=file]::-ms-browse { - --background-color: var(--secondary); - --border-color: var(--secondary); - --color: var(--secondary-inverse); - margin-right: calc(var(--spacing) / 2); - margin-left: 0; - margin-inline-start: 0; - margin-inline-end: calc(var(--spacing) / 2); - padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5); - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius); - outline: none; - background-color: var(--background-color); - box-shadow: var(--box-shadow); - color: var(--color); - font-weight: var(--font-weight); - font-size: 1rem; - line-height: var(--line-height); - text-align: center; - cursor: pointer; - -ms-transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} -[type=file]::-ms-browse:is(:hover, :active, :focus) { - --background-color: var(--secondary-hover); - --border-color: var(--secondary-hover); -} - -[type=range] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - width: 100%; - height: 1.25rem; - background: none; -} -[type=range]::-webkit-slider-runnable-track { - width: 100%; - height: 0.25rem; - border-radius: var(--border-radius); - background-color: var(--range-border-color); - -webkit-transition: background-color var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), box-shadow var(--transition); -} -[type=range]::-moz-range-track { - width: 100%; - height: 0.25rem; - border-radius: var(--border-radius); - background-color: var(--range-border-color); - -moz-transition: background-color var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), box-shadow var(--transition); -} -[type=range]::-ms-track { - width: 100%; - height: 0.25rem; - border-radius: var(--border-radius); - background-color: var(--range-border-color); - -ms-transition: background-color var(--transition), box-shadow var(--transition); - transition: background-color var(--transition), box-shadow var(--transition); -} -[type=range]::-webkit-slider-thumb { - -webkit-appearance: none; - width: 1.25rem; - height: 1.25rem; - margin-top: -0.5rem; - border: 2px solid var(--range-thumb-border-color); - border-radius: 50%; - background-color: var(--range-thumb-color); - cursor: pointer; - -webkit-transition: background-color var(--transition), transform var(--transition); - transition: background-color var(--transition), transform var(--transition); -} -[type=range]::-moz-range-thumb { - -webkit-appearance: none; - width: 1.25rem; - height: 1.25rem; - margin-top: -0.5rem; - border: 2px solid var(--range-thumb-border-color); - border-radius: 50%; - background-color: var(--range-thumb-color); - cursor: pointer; - -moz-transition: background-color var(--transition), transform var(--transition); - transition: background-color var(--transition), transform var(--transition); -} -[type=range]::-ms-thumb { - -webkit-appearance: none; - width: 1.25rem; - height: 1.25rem; - margin-top: -0.5rem; - border: 2px solid var(--range-thumb-border-color); - border-radius: 50%; - background-color: var(--range-thumb-color); - cursor: pointer; - -ms-transition: background-color var(--transition), transform var(--transition); - transition: background-color var(--transition), transform var(--transition); -} -[type=range]:hover, [type=range]:focus { - --range-border-color: var(--range-active-border-color); - --range-thumb-color: var(--range-thumb-hover-color); -} -[type=range]:active { - --range-thumb-color: var(--range-thumb-active-color); -} -[type=range]:active::-webkit-slider-thumb { - transform: scale(1.25); -} -[type=range]:active::-moz-range-thumb { - transform: scale(1.25); -} -[type=range]:active::-ms-thumb { - transform: scale(1.25); -} - -input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] { - -webkit-padding-start: calc(var(--form-element-spacing-horizontal) + 1.75rem); - padding-inline-start: calc(var(--form-element-spacing-horizontal) + 1.75rem); - border-radius: 5rem; - background-image: var(--icon-search); - background-position: center left 1.125rem; - background-size: 1rem auto; - background-repeat: no-repeat; -} -input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] { - -webkit-padding-start: calc(var(--form-element-spacing-horizontal) + 1.75rem) !important; - padding-inline-start: calc(var(--form-element-spacing-horizontal) + 1.75rem) !important; - background-position: center left 1.125rem, center right 0.75rem; -} -input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=false] { - background-image: var(--icon-search), var(--icon-valid); -} -input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=true] { - background-image: var(--icon-search), var(--icon-invalid); -} - -[type=search]::-webkit-search-cancel-button { - -webkit-appearance: none; - display: none; -} - -[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] { - background-position: center right 1.125rem; -} -[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] { - background-position: center right 1.125rem, center left 0.75rem; -} - -/** - * Table - */ -:where(table) { - width: 100%; - border-collapse: collapse; - border-spacing: 0; - text-indent: 0; -} - -th, -td { - padding: calc(var(--spacing) / 2) var(--spacing); - border-bottom: var(--border-width) solid var(--table-border-color); - color: var(--color); - font-weight: var(--font-weight); - font-size: var(--font-size); - text-align: left; - text-align: start; -} - -tfoot th, -tfoot td { - border-top: var(--border-width) solid var(--table-border-color); - border-bottom: 0; -} - -table[role=grid] tbody tr:nth-child(odd) { - background-color: var(--table-row-stripped-background-color); -} - -/** - * Code - */ -pre, -code, -kbd, -samp { - font-size: 0.875em; - font-family: var(--font-family); -} - -pre { - -ms-overflow-style: scrollbar; - overflow: auto; -} - -pre, -code, -kbd { - border-radius: var(--border-radius); - background: var(--code-background-color); - color: var(--code-color); - font-weight: var(--font-weight); - line-height: initial; -} - -code, -kbd { - display: inline-block; - padding: 0.375rem 0.5rem; -} - -pre { - display: block; - margin-bottom: var(--spacing); - overflow-x: auto; -} -pre > code { - display: block; - padding: var(--spacing); - background: none; - font-size: 14px; - line-height: var(--line-height); -} - -code b { - color: var(--code-tag-color); - font-weight: var(--font-weight); -} -code i { - color: var(--code-property-color); - font-style: normal; -} -code u { - color: var(--code-value-color); - text-decoration: none; -} -code em { - color: var(--code-comment-color); - font-style: normal; -} - -kbd { - background-color: var(--code-kbd-background-color); - color: var(--code-kbd-color); - vertical-align: baseline; -} - -/** - * Miscs - */ -hr { - height: 0; - border: 0; - border-top: 1px solid var(--muted-border-color); - color: inherit; -} - -[hidden], -template { - display: none !important; -} - -canvas { - display: inline-block; -} - -/** - * Accordion (
) - */ -details { - display: block; - margin-bottom: var(--spacing); - padding-bottom: var(--spacing); - border-bottom: var(--border-width) solid var(--accordion-border-color); -} -details summary { - line-height: 1rem; - list-style-type: none; - cursor: pointer; - transition: color var(--transition); -} -details summary:not([role]) { - color: var(--accordion-close-summary-color); -} -details summary::-webkit-details-marker { - display: none; -} -details summary::marker { - display: none; -} -details summary::-moz-list-bullet { - list-style-type: none; -} -details summary::after { - display: block; - width: 1rem; - height: 1rem; - -webkit-margin-start: calc(var(--spacing, 1rem) * 0.5); - margin-inline-start: calc(var(--spacing, 1rem) * 0.5); - float: right; - transform: rotate(-90deg); - background-image: var(--icon-chevron); - background-position: right center; - background-size: 1rem auto; - background-repeat: no-repeat; - content: ""; - transition: transform var(--transition); -} -details summary:focus { - outline: none; -} -details summary:focus:not([role=button]) { - color: var(--accordion-active-summary-color); -} -details summary[role=button] { - width: 100%; - text-align: left; -} -details summary[role=button]::after { - height: calc(1rem * var(--line-height, 1.5)); - background-image: var(--icon-chevron-button); -} -details[open] > summary { - margin-bottom: calc(var(--spacing)); -} -details[open] > summary:not([role]):not(:focus) { - color: var(--accordion-open-summary-color); -} -details[open] > summary::after { - transform: rotate(0); -} - -[dir=rtl] details summary { - text-align: right; -} -[dir=rtl] details summary::after { - float: left; - background-position: left center; -} - -/** - * Card (
) - */ -article { - margin: var(--block-spacing-vertical) 0; - padding: var(--block-spacing-vertical) var(--block-spacing-horizontal); - border-radius: var(--border-radius); - background: var(--card-background-color); - box-shadow: var(--card-box-shadow); -} -article > header, -article > footer { - margin-right: calc(var(--block-spacing-horizontal) * -1); - margin-left: calc(var(--block-spacing-horizontal) * -1); - padding: calc(var(--block-spacing-vertical) * 0.66) var(--block-spacing-horizontal); - background-color: var(--card-sectionning-background-color); -} -article > header { - margin-top: calc(var(--block-spacing-vertical) * -1); - margin-bottom: var(--block-spacing-vertical); - border-bottom: var(--border-width) solid var(--card-border-color); - border-top-right-radius: var(--border-radius); - border-top-left-radius: var(--border-radius); -} -article > footer { - margin-top: var(--block-spacing-vertical); - margin-bottom: calc(var(--block-spacing-vertical) * -1); - border-top: var(--border-width) solid var(--card-border-color); - border-bottom-right-radius: var(--border-radius); - border-bottom-left-radius: var(--border-radius); -} - -/** - * Modal () - */ -:root { - --scrollbar-width: 0px; -} - -dialog { - display: flex; - z-index: 999; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - align-items: center; - justify-content: center; - width: inherit; - min-width: 100%; - height: inherit; - min-height: 100%; - padding: var(--spacing); - border: 0; - -webkit-backdrop-filter: var(--modal-overlay-backdrop-filter); - backdrop-filter: var(--modal-overlay-backdrop-filter); - background-color: var(--modal-overlay-background-color); - color: var(--color); -} -dialog article { - max-height: calc(100vh - var(--spacing) * 2); - overflow: auto; -} -@media (min-width: 576px) { - dialog article { - max-width: 510px; - } -} -@media (min-width: 768px) { - dialog article { - max-width: 700px; - } -} -dialog article > header, -dialog article > footer { - padding: calc(var(--block-spacing-vertical) * 0.5) var(--block-spacing-horizontal); -} -dialog article > header .close { - margin: 0; - margin-left: var(--spacing); - float: right; -} -dialog article > footer { - text-align: right; -} -dialog article > footer [role=button] { - margin-bottom: 0; -} -dialog article > footer [role=button]:not(:first-of-type) { - margin-left: calc(var(--spacing) * 0.5); -} -dialog article p:last-of-type { - margin: 0; -} -dialog:not([open]), dialog[open=false] { - display: none; -} - -/** - * Nav - */ -:where(nav li)::before { - float: left; - content: "​"; -} - -nav, -nav ul { - display: flex; -} - -nav { - justify-content: space-between; -} -nav ol, -nav ul { - align-items: center; - margin-bottom: 0; - padding: 0; - list-style: none; -} -nav ol:first-of-type, -nav ul:first-of-type { - margin-left: calc(var(--nav-element-spacing-horizontal) * -1); -} -nav ol:last-of-type, -nav ul:last-of-type { - margin-right: calc(var(--nav-element-spacing-horizontal) * -1); -} -nav li { - display: inline-block; - margin: 0; - padding: var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal); -} -nav li > * { - --spacing: 0; -} -nav :where(a, [role=link]) { - display: inline-block; - margin: calc(var(--nav-link-spacing-vertical) * -1) calc(var(--nav-link-spacing-horizontal) * -1); - padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal); - border-radius: var(--border-radius); - text-decoration: none; -} -nav :where(a, [role=link]):is([aria-current], :hover, :active, :focus) { - text-decoration: none; -} -nav[aria-label=breadcrumb] { - align-items: center; - justify-content: start; -} -nav[aria-label=breadcrumb] ul li:not(:first-child) { - -webkit-margin-start: var(--nav-link-spacing-horizontal); - margin-inline-start: var(--nav-link-spacing-horizontal); -} -nav[aria-label=breadcrumb] ul li:not(:last-child) ::after { - position: absolute; - width: calc(var(--nav-link-spacing-horizontal) * 2); - -webkit-margin-start: calc(var(--nav-link-spacing-horizontal) / 2); - margin-inline-start: calc(var(--nav-link-spacing-horizontal) / 2); - content: "/"; - color: var(--muted-color); - text-align: center; -} -nav[aria-label=breadcrumb] a[aria-current] { - background-color: transparent; - color: inherit; - text-decoration: none; - pointer-events: none; -} -nav [role=button] { - margin-right: inherit; - margin-left: inherit; - padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal); -} - -aside nav, -aside ol, -aside ul, -aside li { - display: block; -} -aside li { - padding: calc(var(--nav-element-spacing-vertical) * 0.5) var(--nav-element-spacing-horizontal); -} -aside li a { - display: block; -} -aside li [role=button] { - margin: inherit; -} - -[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after { - content: "\\"; -} - -/** - * Progress - */ -progress { - display: inline-block; - vertical-align: baseline; -} - -progress { - -webkit-appearance: none; - -moz-appearance: none; - display: inline-block; - appearance: none; - width: 100%; - height: 0.5rem; - margin-bottom: calc(var(--spacing) * 0.5); - overflow: hidden; - border: 0; - border-radius: var(--border-radius); - background-color: var(--progress-background-color); - color: var(--progress-color); -} -progress::-webkit-progress-bar { - border-radius: var(--border-radius); - background: none; -} -progress[value]::-webkit-progress-value { - background-color: var(--progress-color); -} -progress::-moz-progress-bar { - background-color: var(--progress-color); -} -@media (prefers-reduced-motion: no-preference) { - progress:indeterminate { - background: var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat; - animation: progress-indeterminate 1s linear infinite; - } - progress:indeterminate[value]::-webkit-progress-value { - background-color: transparent; - } - progress:indeterminate::-moz-progress-bar { - background-color: transparent; - } -} - -@media (prefers-reduced-motion: no-preference) { - [dir=rtl] progress:indeterminate { - animation-direction: reverse; - } -} - -@keyframes progress-indeterminate { - 0% { - background-position: 200% 0; - } - 100% { - background-position: -200% 0; - } -} -/** - * Dropdown ([role="list"]) - */ -details[role=list], -li[role=list] { - position: relative; -} - -details[role=list] summary + ul, -li[role=list] > ul { - display: flex; - z-index: 99; - position: absolute; - top: auto; - right: 0; - left: 0; - flex-direction: column; - margin: 0; - padding: 0; - border: var(--border-width) solid var(--dropdown-border-color); - border-radius: var(--border-radius); - border-top-right-radius: 0; - border-top-left-radius: 0; - background-color: var(--dropdown-background-color); - box-shadow: var(--card-box-shadow); - color: var(--dropdown-color); - white-space: nowrap; -} -details[role=list] summary + ul li, -li[role=list] > ul li { - width: 100%; - margin-bottom: 0; - padding: calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal); - list-style: none; -} -details[role=list] summary + ul li:first-of-type, -li[role=list] > ul li:first-of-type { - margin-top: calc(var(--form-element-spacing-vertical) * 0.5); -} -details[role=list] summary + ul li:last-of-type, -li[role=list] > ul li:last-of-type { - margin-bottom: calc(var(--form-element-spacing-vertical) * 0.5); -} -details[role=list] summary + ul li a, -li[role=list] > ul li a { - display: block; - margin: calc(var(--form-element-spacing-vertical) * -0.5) calc(var(--form-element-spacing-horizontal) * -1); - padding: calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal); - overflow: hidden; - color: var(--dropdown-color); - text-decoration: none; - text-overflow: ellipsis; -} -details[role=list] summary + ul li a:hover, -li[role=list] > ul li a:hover { - background-color: var(--dropdown-hover-background-color); -} - -details[role=list] summary::after, -li[role=list] > a::after { - display: block; - width: 1rem; - height: calc(1rem * var(--line-height, 1.5)); - -webkit-margin-start: 0.5rem; - margin-inline-start: 0.5rem; - float: right; - transform: rotate(0deg); - background-position: right center; - background-size: 1rem auto; - background-repeat: no-repeat; - content: ""; -} - -details[role=list] { - padding: 0; - border-bottom: none; -} -details[role=list] summary { - margin-bottom: 0; -} -details[role=list] summary:not([role]) { - height: calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2); - padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal); - border: var(--border-width) solid var(--form-element-border-color); - border-radius: var(--border-radius); - background-color: var(--form-element-background-color); - color: var(--form-element-placeholder-color); - line-height: inherit; - cursor: pointer; - transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition); -} -details[role=list] summary:not([role]):active, details[role=list] summary:not([role]):focus { - border-color: var(--form-element-active-border-color); - background-color: var(--form-element-active-background-color); -} -details[role=list] summary:not([role]):focus { - box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color); -} -details[role=list][open] summary { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -details[role=list][open] summary::before { - display: block; - z-index: 1; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: none; - content: ""; - cursor: default; -} - -nav details[role=list] summary, -nav li[role=list] a { - display: flex; - direction: ltr; -} - -nav details[role=list] summary + ul, -nav li[role=list] > ul { - min-width: -moz-fit-content; - min-width: fit-content; - border-radius: var(--border-radius); -} -nav details[role=list] summary + ul li a, -nav li[role=list] > ul li a { - border-radius: 0; -} - -nav details[role=list] summary, -nav details[role=list] summary:not([role]) { - height: auto; - padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal); -} -nav details[role=list][open] summary { - border-radius: var(--border-radius); -} -nav details[role=list] summary + ul { - margin-top: var(--outline-width); - -webkit-margin-start: 0; - margin-inline-start: 0; -} -nav details[role=list] summary[role=link] { - margin-bottom: calc(var(--nav-link-spacing-vertical) * -1); - line-height: var(--line-height); -} -nav details[role=list] summary[role=link] + ul { - margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width)); - -webkit-margin-start: calc(var(--nav-link-spacing-horizontal) * -1); - margin-inline-start: calc(var(--nav-link-spacing-horizontal) * -1); -} - -li[role=list]:hover > ul, -li[role=list] a:active ~ ul, -li[role=list] a:focus ~ ul { - display: flex; -} -li[role=list] > ul { - display: none; - margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width)); - -webkit-margin-start: calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal)); - margin-inline-start: calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal)); -} -li[role=list] > a::after { - background-image: var(--icon-chevron); -} - -label > details[role=list] { - margin-top: calc(var(--spacing) * 0.25); - margin-bottom: var(--spacing); -} - -/** - * Loading ([aria-busy=true]) - */ -[aria-busy=true] { - cursor: progress; -} - -[aria-busy=true]:not(input, select, textarea)::before { - display: inline-block; - width: 1em; - height: 1em; - border: 0.1875em solid currentColor; - border-radius: 1em; - border-right-color: transparent; - content: ""; - vertical-align: text-bottom; - vertical-align: -0.125em; - animation: spinner 0.75s linear infinite; - opacity: var(--loading-spinner-opacity); -} -[aria-busy=true]:not(input, select, textarea):not(:empty)::before { - margin-right: calc(var(--spacing) * 0.5); - margin-left: 0; - -webkit-margin-start: 0; - margin-inline-start: 0; - -webkit-margin-end: calc(var(--spacing) * 0.5); - margin-inline-end: calc(var(--spacing) * 0.5); -} -[aria-busy=true]:not(input, select, textarea):empty { - text-align: center; -} - -button[aria-busy=true], -input[type=submit][aria-busy=true], -input[type=button][aria-busy=true], -input[type=reset][aria-busy=true], -a[aria-busy=true] { - pointer-events: none; -} - -@keyframes spinner { - to { - transform: rotate(360deg); - } -} -/** - * Tooltip ([data-tooltip]) - */ -[data-tooltip] { - position: relative; -} -[data-tooltip]:not(a, button, input) { - border-bottom: 1px dotted; - text-decoration: none; - cursor: help; -} -[data-tooltip][data-placement=top]::before, [data-tooltip][data-placement=top]::after, [data-tooltip]::before, [data-tooltip]::after { - display: block; - z-index: 99; - position: absolute; - bottom: 100%; - left: 50%; - padding: 0.25rem 0.5rem; - overflow: hidden; - transform: translate(-50%, -0.25rem); - border-radius: var(--border-radius); - background: var(--tooltip-background-color); - content: attr(data-tooltip); - color: var(--tooltip-color); - font-style: normal; - font-weight: var(--font-weight); - font-size: 0.875rem; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; - opacity: 0; - pointer-events: none; -} -[data-tooltip][data-placement=top]::after, [data-tooltip]::after { - padding: 0; - transform: translate(-50%, 0rem); - border-top: 0.3rem solid; - border-right: 0.3rem solid transparent; - border-left: 0.3rem solid transparent; - border-radius: 0; - background-color: transparent; - content: ""; - color: var(--tooltip-background-color); -} -[data-tooltip][data-placement=bottom]::before, [data-tooltip][data-placement=bottom]::after { - top: 100%; - bottom: auto; - transform: translate(-50%, 0.25rem); -} -[data-tooltip][data-placement=bottom]:after { - transform: translate(-50%, -0.3rem); - border: 0.3rem solid transparent; - border-bottom: 0.3rem solid; -} -[data-tooltip][data-placement=left]::before, [data-tooltip][data-placement=left]::after { - top: 50%; - right: 100%; - bottom: auto; - left: auto; - transform: translate(-0.25rem, -50%); -} -[data-tooltip][data-placement=left]:after { - transform: translate(0.3rem, -50%); - border: 0.3rem solid transparent; - border-left: 0.3rem solid; -} -[data-tooltip][data-placement=right]::before, [data-tooltip][data-placement=right]::after { - top: 50%; - right: auto; - bottom: auto; - left: 100%; - transform: translate(0.25rem, -50%); -} -[data-tooltip][data-placement=right]:after { - transform: translate(-0.3rem, -50%); - border: 0.3rem solid transparent; - border-right: 0.3rem solid; -} -[data-tooltip]:focus::before, [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after { - opacity: 1; -} -@media (hover: hover) and (pointer: fine) { - [data-tooltip][data-placement=bottom]:focus::before, [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::before, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after { - animation-duration: 0.2s; - animation-name: tooltip-slide-top; - } - [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after, [data-tooltip]:hover::after { - animation-name: tooltip-caret-slide-top; - } - [data-tooltip][data-placement=bottom]:focus::before, [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::before, [data-tooltip][data-placement=bottom]:hover::after { - animation-duration: 0.2s; - animation-name: tooltip-slide-bottom; - } - [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::after { - animation-name: tooltip-caret-slide-bottom; - } - [data-tooltip][data-placement=left]:focus::before, [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::before, [data-tooltip][data-placement=left]:hover::after { - animation-duration: 0.2s; - animation-name: tooltip-slide-left; - } - [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::after { - animation-name: tooltip-caret-slide-left; - } - [data-tooltip][data-placement=right]:focus::before, [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::before, [data-tooltip][data-placement=right]:hover::after { - animation-duration: 0.2s; - animation-name: tooltip-slide-right; - } - [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::after { - animation-name: tooltip-caret-slide-right; - } -} -@keyframes tooltip-slide-top { - from { - transform: translate(-50%, 0.75rem); - opacity: 0; - } - to { - transform: translate(-50%, -0.25rem); - opacity: 1; - } -} -@keyframes tooltip-caret-slide-top { - from { - opacity: 0; - } - 50% { - transform: translate(-50%, -0.25rem); - opacity: 0; - } - to { - transform: translate(-50%, 0rem); - opacity: 1; - } -} -@keyframes tooltip-slide-bottom { - from { - transform: translate(-50%, -0.75rem); - opacity: 0; - } - to { - transform: translate(-50%, 0.25rem); - opacity: 1; - } -} -@keyframes tooltip-caret-slide-bottom { - from { - opacity: 0; - } - 50% { - transform: translate(-50%, -0.5rem); - opacity: 0; - } - to { - transform: translate(-50%, -0.3rem); - opacity: 1; - } -} -@keyframes tooltip-slide-left { - from { - transform: translate(0.75rem, -50%); - opacity: 0; - } - to { - transform: translate(-0.25rem, -50%); - opacity: 1; - } -} -@keyframes tooltip-caret-slide-left { - from { - opacity: 0; - } - 50% { - transform: translate(0.05rem, -50%); - opacity: 0; - } - to { - transform: translate(0.3rem, -50%); - opacity: 1; - } -} -@keyframes tooltip-slide-right { - from { - transform: translate(-0.75rem, -50%); - opacity: 0; - } - to { - transform: translate(0.25rem, -50%); - opacity: 1; - } -} -@keyframes tooltip-caret-slide-right { - from { - opacity: 0; - } - 50% { - transform: translate(-0.05rem, -50%); - opacity: 0; - } - to { - transform: translate(-0.3rem, -50%); - opacity: 1; - } -} - -/** - * Accessibility & User interaction - */ -[aria-controls] { - cursor: pointer; -} - -[aria-disabled=true], -[disabled] { - cursor: not-allowed; -} - -[aria-hidden=false][hidden] { - display: initial; -} - -[aria-hidden=false][hidden]:not(:focus) { - clip: rect(0, 0, 0, 0); - position: absolute; -} - -a, -area, -button, -input, -label, -select, -summary, -textarea, -[tabindex] { - -ms-touch-action: manipulation; -} - -[dir=rtl] { - direction: rtl; -} - -/** -* Reduce Motion Features -*/ -@media (prefers-reduced-motion: reduce) { - *:not([aria-busy=true]), - :not([aria-busy=true])::before, - :not([aria-busy=true])::after { - background-attachment: initial !important; - animation-duration: 1ms !important; - animation-delay: -1ms !important; - animation-iteration-count: 1 !important; - scroll-behavior: auto !important; - transition-delay: 0s !important; - transition-duration: 0s !important; - } -} - -/*# sourceMappingURL=pico.classless.css.map */ \ No newline at end of file diff --git a/static/css/pico-1.5.9/css/pico.classless.css.map b/static/css/pico-1.5.9/css/pico.classless.css.map deleted file mode 100644 index 19455e3..0000000 --- a/static/css/pico-1.5.9/css/pico.classless.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["pico.classless.css","../scss/pico.scss","../scss/themes/default.scss","../scss/themes/default/_styles.scss","../scss/themes/default/_light.scss","../scss/themes/default/_dark.scss","../scss/layout/_document.scss","../scss/layout/_sectioning.scss","../scss/layout/_section.scss","../scss/layout/_scroller.scss","../scss/content/_typography.scss","../scss/content/_embedded.scss","../scss/content/_button.scss","../scss/content/_form.scss","../scss/content/_form-checkbox-radio.scss","../scss/content/_form-alt-input-types.scss","../scss/content/_table.scss","../scss/content/_code.scss","../scss/content/_miscs.scss","../scss/components/_accordion.scss","../scss/components/_card.scss","../scss/components/_modal.scss","../scss/components/_nav.scss","../scss/components/_progress.scss","../scss/components/_dropdown.scss","../scss/utilities/_loading.scss","../scss/utilities/_tooltip.scss","../scss/utilities/_accessibility.scss","../scss/utilities/_reduce-motion.scss"],"names":[],"mappings":"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","file":"pico.classless.css","sourcesContent":["@charset \"UTF-8\";\n/*!\n * Pico CSS v1.5.9 (https://picocss.com)\n * Copyright 2019-2023 - Licensed under MIT\n */\n/**\n * Theme: default\n */\n:root {\n --font-family: system-ui, -apple-system, \"Segoe UI\", \"Roboto\", \"Ubuntu\",\n \"Cantarell\", \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\",\n \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --line-height: 1.5;\n --font-weight: 400;\n --font-size: 16px;\n --border-radius: 0.25rem;\n --border-width: 1px;\n --outline-width: 3px;\n --spacing: 1rem;\n --typography-spacing-vertical: 1.5rem;\n --block-spacing-vertical: calc(var(--spacing) * 2);\n --block-spacing-horizontal: var(--spacing);\n --form-element-spacing-vertical: 0.75rem;\n --form-element-spacing-horizontal: 1rem;\n --nav-element-spacing-vertical: 1rem;\n --nav-element-spacing-horizontal: 0.5rem;\n --nav-link-spacing-vertical: 0.5rem;\n --nav-link-spacing-horizontal: 0.5rem;\n --form-label-font-weight: var(--font-weight);\n --transition: 0.2s ease-in-out;\n --modal-overlay-backdrop-filter: blur(0.25rem);\n}\n@media (min-width: 576px) {\n :root {\n --font-size: 17px;\n }\n}\n@media (min-width: 768px) {\n :root {\n --font-size: 18px;\n }\n}\n@media (min-width: 992px) {\n :root {\n --font-size: 19px;\n }\n}\n@media (min-width: 1200px) {\n :root {\n --font-size: 20px;\n }\n}\n\n@media (min-width: 576px) {\n body > header,\n body > main,\n body > footer,\n section {\n --block-spacing-vertical: calc(var(--spacing) * 2.5);\n }\n}\n@media (min-width: 768px) {\n body > header,\n body > main,\n body > footer,\n section {\n --block-spacing-vertical: calc(var(--spacing) * 3);\n }\n}\n@media (min-width: 992px) {\n body > header,\n body > main,\n body > footer,\n section {\n --block-spacing-vertical: calc(var(--spacing) * 3.5);\n }\n}\n@media (min-width: 1200px) {\n body > header,\n body > main,\n body > footer,\n section {\n --block-spacing-vertical: calc(var(--spacing) * 4);\n }\n}\n\n@media (min-width: 576px) {\n article {\n --block-spacing-horizontal: calc(var(--spacing) * 1.25);\n }\n}\n@media (min-width: 768px) {\n article {\n --block-spacing-horizontal: calc(var(--spacing) * 1.5);\n }\n}\n@media (min-width: 992px) {\n article {\n --block-spacing-horizontal: calc(var(--spacing) * 1.75);\n }\n}\n@media (min-width: 1200px) {\n article {\n --block-spacing-horizontal: calc(var(--spacing) * 2);\n }\n}\n\ndialog > article {\n --block-spacing-vertical: calc(var(--spacing) * 2);\n --block-spacing-horizontal: var(--spacing);\n}\n@media (min-width: 576px) {\n dialog > article {\n --block-spacing-vertical: calc(var(--spacing) * 2.5);\n --block-spacing-horizontal: calc(var(--spacing) * 1.25);\n }\n}\n@media (min-width: 768px) {\n dialog > article {\n --block-spacing-vertical: calc(var(--spacing) * 3);\n --block-spacing-horizontal: calc(var(--spacing) * 1.5);\n }\n}\n\na {\n --text-decoration: none;\n}\n\nsmall {\n --font-size: 0.875em;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n --font-weight: 700;\n}\n\nh1 {\n --font-size: 2rem;\n --typography-spacing-vertical: 3rem;\n}\n\nh2 {\n --font-size: 1.75rem;\n --typography-spacing-vertical: 2.625rem;\n}\n\nh3 {\n --font-size: 1.5rem;\n --typography-spacing-vertical: 2.25rem;\n}\n\nh4 {\n --font-size: 1.25rem;\n --typography-spacing-vertical: 1.874rem;\n}\n\nh5 {\n --font-size: 1.125rem;\n --typography-spacing-vertical: 1.6875rem;\n}\n\n[type=checkbox],\n[type=radio] {\n --border-width: 2px;\n}\n\n[type=checkbox][role=switch] {\n --border-width: 3px;\n}\n\nthead th,\nthead td,\ntfoot th,\ntfoot td {\n --border-width: 3px;\n}\n\n:not(thead, tfoot) > * > td {\n --font-size: 0.875em;\n}\n\npre,\ncode,\nkbd,\nsamp {\n --font-family: \"Menlo\", \"Consolas\", \"Roboto Mono\", \"Ubuntu Monospace\",\n \"Noto Mono\", \"Oxygen Mono\", \"Liberation Mono\", monospace,\n \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n}\n\nkbd {\n --font-weight: bolder;\n}\n\n[data-theme=light],\n:root:not([data-theme=dark]) {\n --background-color: #fff;\n --color: hsl(205, 20%, 32%);\n --h1-color: hsl(205, 30%, 15%);\n --h2-color: #24333e;\n --h3-color: hsl(205, 25%, 23%);\n --h4-color: #374956;\n --h5-color: hsl(205, 20%, 32%);\n --h6-color: #4d606d;\n --muted-color: hsl(205, 10%, 50%);\n --muted-border-color: hsl(205, 20%, 94%);\n --primary: hsl(195, 85%, 41%);\n --primary-hover: hsl(195, 90%, 32%);\n --primary-focus: rgba(16, 149, 193, 0.125);\n --primary-inverse: #fff;\n --secondary: hsl(205, 15%, 41%);\n --secondary-hover: hsl(205, 20%, 32%);\n --secondary-focus: rgba(89, 107, 120, 0.125);\n --secondary-inverse: #fff;\n --contrast: hsl(205, 30%, 15%);\n --contrast-hover: #000;\n --contrast-focus: rgba(89, 107, 120, 0.125);\n --contrast-inverse: #fff;\n --mark-background-color: #fff2ca;\n --mark-color: #543a26;\n --ins-color: #388e3c;\n --del-color: #c62828;\n --blockquote-border-color: var(--muted-border-color);\n --blockquote-footer-color: var(--muted-color);\n --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --form-element-background-color: transparent;\n --form-element-border-color: hsl(205, 14%, 68%);\n --form-element-color: var(--color);\n --form-element-placeholder-color: var(--muted-color);\n --form-element-active-background-color: transparent;\n --form-element-active-border-color: var(--primary);\n --form-element-focus-color: var(--primary-focus);\n --form-element-disabled-background-color: hsl(205, 18%, 86%);\n --form-element-disabled-border-color: hsl(205, 14%, 68%);\n --form-element-disabled-opacity: 0.5;\n --form-element-invalid-border-color: #c62828;\n --form-element-invalid-active-border-color: #d32f2f;\n --form-element-invalid-focus-color: rgba(211, 47, 47, 0.125);\n --form-element-valid-border-color: #388e3c;\n --form-element-valid-active-border-color: #43a047;\n --form-element-valid-focus-color: rgba(67, 160, 71, 0.125);\n --switch-background-color: hsl(205, 16%, 77%);\n --switch-color: var(--primary-inverse);\n --switch-checked-background-color: var(--primary);\n --range-border-color: hsl(205, 18%, 86%);\n --range-active-border-color: hsl(205, 16%, 77%);\n --range-thumb-border-color: var(--background-color);\n --range-thumb-color: var(--secondary);\n --range-thumb-hover-color: var(--secondary-hover);\n --range-thumb-active-color: var(--primary);\n --table-border-color: var(--muted-border-color);\n --table-row-stripped-background-color: #f6f8f9;\n --code-background-color: hsl(205, 20%, 94%);\n --code-color: var(--muted-color);\n --code-kbd-background-color: var(--contrast);\n --code-kbd-color: var(--contrast-inverse);\n --code-tag-color: hsl(330, 40%, 50%);\n --code-property-color: hsl(185, 40%, 40%);\n --code-value-color: hsl(40, 20%, 50%);\n --code-comment-color: hsl(205, 14%, 68%);\n --accordion-border-color: var(--muted-border-color);\n --accordion-close-summary-color: var(--color);\n --accordion-open-summary-color: var(--muted-color);\n --card-background-color: var(--background-color);\n --card-border-color: var(--muted-border-color);\n --card-box-shadow:\n 0.0145rem 0.029rem 0.174rem rgba(27, 40, 50, 0.01698),\n 0.0335rem 0.067rem 0.402rem rgba(27, 40, 50, 0.024),\n 0.0625rem 0.125rem 0.75rem rgba(27, 40, 50, 0.03),\n 0.1125rem 0.225rem 1.35rem rgba(27, 40, 50, 0.036),\n 0.2085rem 0.417rem 2.502rem rgba(27, 40, 50, 0.04302),\n 0.5rem 1rem 6rem rgba(27, 40, 50, 0.06),\n 0 0 0 0.0625rem rgba(27, 40, 50, 0.015);\n --card-sectionning-background-color: #fbfbfc;\n --dropdown-background-color: #fbfbfc;\n --dropdown-border-color: #e1e6eb;\n --dropdown-box-shadow: var(--card-box-shadow);\n --dropdown-color: var(--color);\n --dropdown-hover-background-color: hsl(205, 20%, 94%);\n --modal-overlay-background-color: rgba(213, 220, 226, 0.7);\n --progress-background-color: hsl(205, 18%, 86%);\n --progress-color: var(--primary);\n --loading-spinner-opacity: 0.5;\n --tooltip-background-color: var(--contrast);\n --tooltip-color: var(--contrast-inverse);\n --icon-checkbox: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button-inverse: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-close: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");\n --icon-date: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");\n --icon-invalid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\");\n --icon-minus: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");\n --icon-search: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");\n --icon-time: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-valid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n color-scheme: light;\n}\n\n@media only screen and (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n --background-color: #11191f;\n --color: hsl(205, 16%, 77%);\n --h1-color: hsl(205, 20%, 94%);\n --h2-color: #e1e6eb;\n --h3-color: hsl(205, 18%, 86%);\n --h4-color: #c8d1d8;\n --h5-color: hsl(205, 16%, 77%);\n --h6-color: #afbbc4;\n --muted-color: hsl(205, 10%, 50%);\n --muted-border-color: #1f2d38;\n --primary: hsl(195, 85%, 41%);\n --primary-hover: hsl(195, 80%, 50%);\n --primary-focus: rgba(16, 149, 193, 0.25);\n --primary-inverse: #fff;\n --secondary: hsl(205, 15%, 41%);\n --secondary-hover: hsl(205, 10%, 50%);\n --secondary-focus: rgba(115, 130, 140, 0.25);\n --secondary-inverse: #fff;\n --contrast: hsl(205, 20%, 94%);\n --contrast-hover: #fff;\n --contrast-focus: rgba(115, 130, 140, 0.25);\n --contrast-inverse: #000;\n --mark-background-color: #d1c284;\n --mark-color: #11191f;\n --ins-color: #388e3c;\n --del-color: #c62828;\n --blockquote-border-color: var(--muted-border-color);\n --blockquote-footer-color: var(--muted-color);\n --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --form-element-background-color: #11191f;\n --form-element-border-color: #374956;\n --form-element-color: var(--color);\n --form-element-placeholder-color: var(--muted-color);\n --form-element-active-background-color: var(--form-element-background-color);\n --form-element-active-border-color: var(--primary);\n --form-element-focus-color: var(--primary-focus);\n --form-element-disabled-background-color: hsl(205, 25%, 23%);\n --form-element-disabled-border-color: hsl(205, 20%, 32%);\n --form-element-disabled-opacity: 0.5;\n --form-element-invalid-border-color: #b71c1c;\n --form-element-invalid-active-border-color: #c62828;\n --form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);\n --form-element-valid-border-color: #2e7d32;\n --form-element-valid-active-border-color: #388e3c;\n --form-element-valid-focus-color: rgba(56, 142, 60, 0.25);\n --switch-background-color: #374956;\n --switch-color: var(--primary-inverse);\n --switch-checked-background-color: var(--primary);\n --range-border-color: #24333e;\n --range-active-border-color: hsl(205, 25%, 23%);\n --range-thumb-border-color: var(--background-color);\n --range-thumb-color: var(--secondary);\n --range-thumb-hover-color: var(--secondary-hover);\n --range-thumb-active-color: var(--primary);\n --table-border-color: var(--muted-border-color);\n --table-row-stripped-background-color: rgba(115, 130, 140, 0.05);\n --code-background-color: #18232c;\n --code-color: var(--muted-color);\n --code-kbd-background-color: var(--contrast);\n --code-kbd-color: var(--contrast-inverse);\n --code-tag-color: hsl(330, 30%, 50%);\n --code-property-color: hsl(185, 30%, 50%);\n --code-value-color: hsl(40, 10%, 50%);\n --code-comment-color: #4d606d;\n --accordion-border-color: var(--muted-border-color);\n --accordion-active-summary-color: var(--primary);\n --accordion-close-summary-color: var(--color);\n --accordion-open-summary-color: var(--muted-color);\n --card-background-color: #141e26;\n --card-border-color: var(--card-background-color);\n --card-box-shadow:\n 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698),\n 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024),\n 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03),\n 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036),\n 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302),\n 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06),\n 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);\n --card-sectionning-background-color: #18232c;\n --dropdown-background-color: hsl(205, 30%, 15%);\n --dropdown-border-color: #24333e;\n --dropdown-box-shadow: var(--card-box-shadow);\n --dropdown-color: var(--color);\n --dropdown-hover-background-color: rgba(36, 51, 62, 0.75);\n --modal-overlay-background-color: rgba(36, 51, 62, 0.8);\n --progress-background-color: #24333e;\n --progress-color: var(--primary);\n --loading-spinner-opacity: 0.5;\n --tooltip-background-color: var(--contrast);\n --tooltip-color: var(--contrast-inverse);\n --icon-checkbox: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button-inverse: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-close: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");\n --icon-date: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");\n --icon-invalid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\");\n --icon-minus: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");\n --icon-search: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");\n --icon-time: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-valid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n color-scheme: dark;\n }\n}\n[data-theme=dark] {\n --background-color: #11191f;\n --color: hsl(205, 16%, 77%);\n --h1-color: hsl(205, 20%, 94%);\n --h2-color: #e1e6eb;\n --h3-color: hsl(205, 18%, 86%);\n --h4-color: #c8d1d8;\n --h5-color: hsl(205, 16%, 77%);\n --h6-color: #afbbc4;\n --muted-color: hsl(205, 10%, 50%);\n --muted-border-color: #1f2d38;\n --primary: hsl(195, 85%, 41%);\n --primary-hover: hsl(195, 80%, 50%);\n --primary-focus: rgba(16, 149, 193, 0.25);\n --primary-inverse: #fff;\n --secondary: hsl(205, 15%, 41%);\n --secondary-hover: hsl(205, 10%, 50%);\n --secondary-focus: rgba(115, 130, 140, 0.25);\n --secondary-inverse: #fff;\n --contrast: hsl(205, 20%, 94%);\n --contrast-hover: #fff;\n --contrast-focus: rgba(115, 130, 140, 0.25);\n --contrast-inverse: #000;\n --mark-background-color: #d1c284;\n --mark-color: #11191f;\n --ins-color: #388e3c;\n --del-color: #c62828;\n --blockquote-border-color: var(--muted-border-color);\n --blockquote-footer-color: var(--muted-color);\n --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --form-element-background-color: #11191f;\n --form-element-border-color: #374956;\n --form-element-color: var(--color);\n --form-element-placeholder-color: var(--muted-color);\n --form-element-active-background-color: var(--form-element-background-color);\n --form-element-active-border-color: var(--primary);\n --form-element-focus-color: var(--primary-focus);\n --form-element-disabled-background-color: hsl(205, 25%, 23%);\n --form-element-disabled-border-color: hsl(205, 20%, 32%);\n --form-element-disabled-opacity: 0.5;\n --form-element-invalid-border-color: #b71c1c;\n --form-element-invalid-active-border-color: #c62828;\n --form-element-invalid-focus-color: rgba(198, 40, 40, 0.25);\n --form-element-valid-border-color: #2e7d32;\n --form-element-valid-active-border-color: #388e3c;\n --form-element-valid-focus-color: rgba(56, 142, 60, 0.25);\n --switch-background-color: #374956;\n --switch-color: var(--primary-inverse);\n --switch-checked-background-color: var(--primary);\n --range-border-color: #24333e;\n --range-active-border-color: hsl(205, 25%, 23%);\n --range-thumb-border-color: var(--background-color);\n --range-thumb-color: var(--secondary);\n --range-thumb-hover-color: var(--secondary-hover);\n --range-thumb-active-color: var(--primary);\n --table-border-color: var(--muted-border-color);\n --table-row-stripped-background-color: rgba(115, 130, 140, 0.05);\n --code-background-color: #18232c;\n --code-color: var(--muted-color);\n --code-kbd-background-color: var(--contrast);\n --code-kbd-color: var(--contrast-inverse);\n --code-tag-color: hsl(330, 30%, 50%);\n --code-property-color: hsl(185, 30%, 50%);\n --code-value-color: hsl(40, 10%, 50%);\n --code-comment-color: #4d606d;\n --accordion-border-color: var(--muted-border-color);\n --accordion-active-summary-color: var(--primary);\n --accordion-close-summary-color: var(--color);\n --accordion-open-summary-color: var(--muted-color);\n --card-background-color: #141e26;\n --card-border-color: var(--card-background-color);\n --card-box-shadow:\n 0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698),\n 0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024),\n 0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03),\n 0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036),\n 0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302),\n 0.5rem 1rem 6rem rgba(0, 0, 0, 0.06),\n 0 0 0 0.0625rem rgba(0, 0, 0, 0.015);\n --card-sectionning-background-color: #18232c;\n --dropdown-background-color: hsl(205, 30%, 15%);\n --dropdown-border-color: #24333e;\n --dropdown-box-shadow: var(--card-box-shadow);\n --dropdown-color: var(--color);\n --dropdown-hover-background-color: rgba(36, 51, 62, 0.75);\n --modal-overlay-background-color: rgba(36, 51, 62, 0.8);\n --progress-background-color: #24333e;\n --progress-color: var(--primary);\n --loading-spinner-opacity: 0.5;\n --tooltip-background-color: var(--contrast);\n --tooltip-color: var(--contrast-inverse);\n --icon-checkbox: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button-inverse: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-close: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");\n --icon-date: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");\n --icon-invalid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\");\n --icon-minus: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");\n --icon-search: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");\n --icon-time: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-valid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n color-scheme: dark;\n}\n\nprogress,\n[type=checkbox],\n[type=radio],\n[type=range] {\n accent-color: var(--primary);\n}\n\n/**\n * Document\n * Content-box & Responsive typography\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n background-repeat: no-repeat;\n}\n\n::before,\n::after {\n text-decoration: inherit;\n vertical-align: inherit;\n}\n\n:where(:root) {\n -webkit-tap-highlight-color: transparent;\n -webkit-text-size-adjust: 100%;\n text-size-adjust: 100%;\n background-color: var(--background-color);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n line-height: var(--line-height);\n font-family: var(--font-family);\n text-rendering: optimizeLegibility;\n overflow-wrap: break-word;\n cursor: default;\n tab-size: 4;\n}\n\n/**\n * Sectioning\n * Container and responsive spacings for header, main, footer\n */\nmain {\n display: block;\n}\n\nbody {\n width: 100%;\n margin: 0;\n}\nbody > header,\nbody > main,\nbody > footer {\n width: 100%;\n margin-right: auto;\n margin-left: auto;\n padding: var(--block-spacing-vertical) var(--block-spacing-horizontal);\n}\n@media (min-width: 576px) {\n body > header,\n body > main,\n body > footer {\n max-width: 510px;\n padding-right: 0;\n padding-left: 0;\n }\n}\n@media (min-width: 768px) {\n body > header,\n body > main,\n body > footer {\n max-width: 700px;\n }\n}\n@media (min-width: 992px) {\n body > header,\n body > main,\n body > footer {\n max-width: 920px;\n }\n}\n@media (min-width: 1200px) {\n body > header,\n body > main,\n body > footer {\n max-width: 1130px;\n }\n}\n\n/**\n * Section\n * Responsive spacings for section\n */\nsection {\n margin-bottom: var(--block-spacing-vertical);\n}\n\n/**\n * Horizontal scroller (
)\n */\nfigure {\n display: block;\n margin: 0;\n padding: 0;\n overflow-x: auto;\n}\nfigure figcaption {\n padding: calc(var(--spacing) * 0.5) 0;\n color: var(--muted-color);\n}\n\n/**\n * Typography\n */\nb,\nstrong {\n font-weight: bolder;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\naddress,\nblockquote,\ndl,\nfigure,\nform,\nol,\np,\npre,\ntable,\nul {\n margin-top: 0;\n margin-bottom: var(--typography-spacing-vertical);\n color: var(--color);\n font-style: normal;\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n}\n\na,\n[role=link] {\n --color: var(--primary);\n --background-color: transparent;\n outline: none;\n background-color: var(--background-color);\n color: var(--color);\n text-decoration: var(--text-decoration);\n transition: background-color var(--transition), color var(--transition), text-decoration var(--transition), box-shadow var(--transition);\n}\na:is([aria-current], :hover, :active, :focus),\n[role=link]:is([aria-current], :hover, :active, :focus) {\n --color: var(--primary-hover);\n --text-decoration: underline;\n}\na:focus,\n[role=link]:focus {\n --background-color: var(--primary-focus);\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n margin-top: 0;\n margin-bottom: var(--typography-spacing-vertical);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n font-family: var(--font-family);\n}\n\nh1 {\n --color: var(--h1-color);\n}\n\nh2 {\n --color: var(--h2-color);\n}\n\nh3 {\n --color: var(--h3-color);\n}\n\nh4 {\n --color: var(--h4-color);\n}\n\nh5 {\n --color: var(--h5-color);\n}\n\nh6 {\n --color: var(--h6-color);\n}\n\n:where(address, blockquote, dl, figure, form, ol, p, pre, table, ul) ~ :is(h1, h2, h3, h4, h5, h6) {\n margin-top: var(--typography-spacing-vertical);\n}\n\nhgroup {\n margin-bottom: var(--typography-spacing-vertical);\n}\nhgroup > * {\n margin-bottom: 0;\n}\nhgroup > *:last-child {\n --color: var(--muted-color);\n --font-weight: unset;\n font-size: 1rem;\n font-family: unset;\n}\n\np {\n margin-bottom: var(--typography-spacing-vertical);\n}\n\nsmall {\n font-size: var(--font-size);\n}\n\n:where(dl, ol, ul) {\n padding-right: 0;\n padding-left: var(--spacing);\n padding-inline-start: var(--spacing);\n padding-inline-end: 0;\n}\n:where(dl, ol, ul) li {\n margin-bottom: calc(var(--typography-spacing-vertical) * 0.25);\n}\n\n:where(dl, ol, ul) :is(dl, ol, ul) {\n margin: 0;\n margin-top: calc(var(--typography-spacing-vertical) * 0.25);\n}\n\nul li {\n list-style: square;\n}\n\nmark {\n padding: 0.125rem 0.25rem;\n background-color: var(--mark-background-color);\n color: var(--mark-color);\n vertical-align: baseline;\n}\n\nblockquote {\n display: block;\n margin: var(--typography-spacing-vertical) 0;\n padding: var(--spacing);\n border-right: none;\n border-left: 0.25rem solid var(--blockquote-border-color);\n border-inline-start: 0.25rem solid var(--blockquote-border-color);\n border-inline-end: none;\n}\nblockquote footer {\n margin-top: calc(var(--typography-spacing-vertical) * 0.5);\n color: var(--blockquote-footer-color);\n}\n\nabbr[title] {\n border-bottom: 1px dotted;\n text-decoration: none;\n cursor: help;\n}\n\nins {\n color: var(--ins-color);\n text-decoration: none;\n}\n\ndel {\n color: var(--del-color);\n}\n\n::selection {\n background-color: var(--primary-focus);\n}\n\n/**\n * Embedded content\n */\n:where(audio, canvas, iframe, img, svg, video) {\n vertical-align: middle;\n}\n\naudio,\nvideo {\n display: inline-block;\n}\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n:where(iframe) {\n border-style: none;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n border-style: none;\n}\n\n:where(svg:not([fill])) {\n fill: currentColor;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n/**\n * Button\n */\nbutton {\n margin: 0;\n overflow: visible;\n font-family: inherit;\n text-transform: none;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n -webkit-appearance: button;\n}\n\nbutton {\n display: block;\n width: 100%;\n margin-bottom: var(--spacing);\n}\n\n[role=button] {\n display: inline-block;\n text-decoration: none;\n}\n\nbutton,\ninput[type=submit],\ninput[type=button],\ninput[type=reset],\n[role=button] {\n --background-color: var(--primary);\n --border-color: var(--primary);\n --color: var(--primary-inverse);\n --box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));\n padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: 1rem;\n line-height: var(--line-height);\n text-align: center;\n cursor: pointer;\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\nbutton:is([aria-current], :hover, :active, :focus),\ninput[type=submit]:is([aria-current], :hover, :active, :focus),\ninput[type=button]:is([aria-current], :hover, :active, :focus),\ninput[type=reset]:is([aria-current], :hover, :active, :focus),\n[role=button]:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--primary-hover);\n --border-color: var(--primary-hover);\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));\n --color: var(--primary-inverse);\n}\nbutton:focus,\ninput[type=submit]:focus,\ninput[type=button]:focus,\ninput[type=reset]:focus,\n[role=button]:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--primary-focus);\n}\n\ninput[type=reset] {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n cursor: pointer;\n}\ninput[type=reset]:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n}\ninput[type=reset]:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--secondary-focus);\n}\n\n:where(button, [type=submit], [type=button], [type=reset], [role=button])[disabled],\n:where(fieldset[disabled]) :is(button, [type=submit], [type=button], [type=reset], [role=button]),\na[role=button]:not([href]) {\n opacity: 0.5;\n pointer-events: none;\n}\n\n/**\n * Form elements\n */\ninput,\noptgroup,\nselect,\ntextarea {\n margin: 0;\n font-size: 1rem;\n line-height: var(--line-height);\n font-family: inherit;\n letter-spacing: inherit;\n}\n\ninput {\n overflow: visible;\n}\n\nselect {\n text-transform: none;\n}\n\nlegend {\n max-width: 100%;\n padding: 0;\n color: inherit;\n white-space: normal;\n}\n\ntextarea {\n overflow: auto;\n}\n\n[type=checkbox],\n[type=radio] {\n padding: 0;\n}\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=search] {\n -webkit-appearance: textfield;\n outline-offset: -2px;\n}\n\n[type=search]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n -webkit-appearance: button;\n font: inherit;\n}\n\n::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\n:-moz-focusring {\n outline: none;\n}\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n::-ms-expand {\n display: none;\n}\n\n[type=file],\n[type=range] {\n padding: 0;\n border-width: 0;\n}\n\ninput:not([type=checkbox], [type=radio], [type=range]) {\n height: calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2);\n}\n\nfieldset {\n margin: 0;\n margin-bottom: var(--spacing);\n padding: 0;\n border: 0;\n}\n\nlabel,\nfieldset legend {\n display: block;\n margin-bottom: calc(var(--spacing) * 0.25);\n font-weight: var(--form-label-font-weight, var(--font-weight));\n}\n\ninput:not([type=checkbox], [type=radio]),\nselect,\ntextarea {\n width: 100%;\n}\n\ninput:not([type=checkbox], [type=radio], [type=range], [type=file]),\nselect,\ntextarea {\n appearance: none;\n padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);\n}\n\ninput,\nselect,\ntextarea {\n --background-color: var(--form-element-background-color);\n --border-color: var(--form-element-border-color);\n --color: var(--form-element-color);\n --box-shadow: none;\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\n\ninput:not([type=submit], [type=button], [type=reset], [type=checkbox], [type=radio], [readonly]):is(:active, :focus),\n:where(select, textarea):is(:active, :focus) {\n --background-color: var(--form-element-active-background-color);\n}\n\ninput:not([type=submit], [type=button], [type=reset], [role=switch], [readonly]):is(:active, :focus),\n:where(select, textarea):is(:active, :focus) {\n --border-color: var(--form-element-active-border-color);\n}\n\ninput:not([type=submit], [type=button], [type=reset], [type=range], [type=file], [readonly]):focus,\nselect:focus,\ntextarea:focus {\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color);\n}\n\ninput:not([type=submit], [type=button], [type=reset])[disabled],\nselect[disabled],\ntextarea[disabled],\n:where(fieldset[disabled]) :is(input:not([type=submit], [type=button], [type=reset]), select, textarea) {\n --background-color: var(--form-element-disabled-background-color);\n --border-color: var(--form-element-disabled-border-color);\n opacity: var(--form-element-disabled-opacity);\n pointer-events: none;\n}\n\n:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid] {\n padding-right: calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;\n padding-left: var(--form-element-spacing-horizontal);\n padding-inline-start: var(--form-element-spacing-horizontal) !important;\n padding-inline-end: calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;\n background-position: center right 0.75rem;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n}\n:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid=false] {\n background-image: var(--icon-valid);\n}\n:where(input, select, textarea):not([type=checkbox], [type=radio], [type=date], [type=datetime-local], [type=month], [type=time], [type=week])[aria-invalid=true] {\n background-image: var(--icon-invalid);\n}\n:where(input, select, textarea)[aria-invalid=false] {\n --border-color: var(--form-element-valid-border-color);\n}\n:where(input, select, textarea)[aria-invalid=false]:is(:active, :focus) {\n --border-color: var(--form-element-valid-active-border-color) !important;\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important;\n}\n:where(input, select, textarea)[aria-invalid=true] {\n --border-color: var(--form-element-invalid-border-color);\n}\n:where(input, select, textarea)[aria-invalid=true]:is(:active, :focus) {\n --border-color: var(--form-element-invalid-active-border-color) !important;\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important;\n}\n\n[dir=rtl] :where(input, select, textarea):not([type=checkbox], [type=radio]):is([aria-invalid], [aria-invalid=true], [aria-invalid=false]) {\n background-position: center left 0.75rem;\n}\n\ninput::placeholder,\ninput::-webkit-input-placeholder,\ntextarea::placeholder,\ntextarea::-webkit-input-placeholder,\nselect:invalid {\n color: var(--form-element-placeholder-color);\n opacity: 1;\n}\n\ninput:not([type=checkbox], [type=radio]),\nselect,\ntextarea {\n margin-bottom: var(--spacing);\n}\n\nselect::-ms-expand {\n border: 0;\n background-color: transparent;\n}\nselect:not([multiple], [size]) {\n padding-right: calc(var(--form-element-spacing-horizontal) + 1.5rem);\n padding-left: var(--form-element-spacing-horizontal);\n padding-inline-start: var(--form-element-spacing-horizontal);\n padding-inline-end: calc(var(--form-element-spacing-horizontal) + 1.5rem);\n background-image: var(--icon-chevron);\n background-position: center right 0.75rem;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n}\n\n[dir=rtl] select:not([multiple], [size]) {\n background-position: center left 0.75rem;\n}\n\n:where(input, select, textarea) + small {\n display: block;\n width: 100%;\n margin-top: calc(var(--spacing) * -0.75);\n margin-bottom: var(--spacing);\n color: var(--muted-color);\n}\n\nlabel > :where(input, select, textarea) {\n margin-top: calc(var(--spacing) * 0.25);\n}\n\n/**\n * Form elements\n * Checkboxes & Radios\n */\n[type=checkbox],\n[type=radio] {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n width: 1.25em;\n height: 1.25em;\n margin-top: -0.125em;\n margin-right: 0.375em;\n margin-left: 0;\n margin-inline-start: 0;\n margin-inline-end: 0.375em;\n border-width: var(--border-width);\n font-size: inherit;\n vertical-align: middle;\n cursor: pointer;\n}\n[type=checkbox]::-ms-check,\n[type=radio]::-ms-check {\n display: none;\n}\n[type=checkbox]:checked, [type=checkbox]:checked:active, [type=checkbox]:checked:focus,\n[type=radio]:checked,\n[type=radio]:checked:active,\n[type=radio]:checked:focus {\n --background-color: var(--primary);\n --border-color: var(--primary);\n background-image: var(--icon-checkbox);\n background-position: center;\n background-size: 0.75em auto;\n background-repeat: no-repeat;\n}\n[type=checkbox] ~ label,\n[type=radio] ~ label {\n display: inline-block;\n margin-right: 0.375em;\n margin-bottom: 0;\n cursor: pointer;\n}\n\n[type=checkbox]:indeterminate {\n --background-color: var(--primary);\n --border-color: var(--primary);\n background-image: var(--icon-minus);\n background-position: center;\n background-size: 0.75em auto;\n background-repeat: no-repeat;\n}\n\n[type=radio] {\n border-radius: 50%;\n}\n[type=radio]:checked, [type=radio]:checked:active, [type=radio]:checked:focus {\n --background-color: var(--primary-inverse);\n border-width: 0.35em;\n background-image: none;\n}\n\n[type=checkbox][role=switch] {\n --background-color: var(--switch-background-color);\n --border-color: var(--switch-background-color);\n --color: var(--switch-color);\n width: 2.25em;\n height: 1.25em;\n border: var(--border-width) solid var(--border-color);\n border-radius: 1.25em;\n background-color: var(--background-color);\n line-height: 1.25em;\n}\n[type=checkbox][role=switch]:focus {\n --background-color: var(--switch-background-color);\n --border-color: var(--switch-background-color);\n}\n[type=checkbox][role=switch]:checked {\n --background-color: var(--switch-checked-background-color);\n --border-color: var(--switch-checked-background-color);\n}\n[type=checkbox][role=switch]:before {\n display: block;\n width: calc(1.25em - (var(--border-width) * 2));\n height: 100%;\n border-radius: 50%;\n background-color: var(--color);\n content: \"\";\n transition: margin 0.1s ease-in-out;\n}\n[type=checkbox][role=switch]:checked {\n background-image: none;\n}\n[type=checkbox][role=switch]:checked::before {\n margin-left: calc(1.125em - var(--border-width));\n margin-inline-start: calc(1.125em - var(--border-width));\n}\n\n[type=checkbox][aria-invalid=false],\n[type=checkbox]:checked[aria-invalid=false],\n[type=radio][aria-invalid=false],\n[type=radio]:checked[aria-invalid=false],\n[type=checkbox][role=switch][aria-invalid=false],\n[type=checkbox][role=switch]:checked[aria-invalid=false] {\n --border-color: var(--form-element-valid-border-color);\n}\n[type=checkbox][aria-invalid=true],\n[type=checkbox]:checked[aria-invalid=true],\n[type=radio][aria-invalid=true],\n[type=radio]:checked[aria-invalid=true],\n[type=checkbox][role=switch][aria-invalid=true],\n[type=checkbox][role=switch]:checked[aria-invalid=true] {\n --border-color: var(--form-element-invalid-border-color);\n}\n\n/**\n * Form elements\n * Alternatives input types (Not Checkboxes & Radios)\n */\n[type=color]::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n[type=color]::-moz-focus-inner {\n padding: 0;\n}\n[type=color]::-webkit-color-swatch {\n border: 0;\n border-radius: calc(var(--border-radius) * 0.5);\n}\n[type=color]::-moz-color-swatch {\n border: 0;\n border-radius: calc(var(--border-radius) * 0.5);\n}\n\ninput:not([type=checkbox], [type=radio], [type=range], [type=file]):is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) {\n --icon-position: 0.75rem;\n --icon-width: 1rem;\n padding-right: calc(var(--icon-width) + var(--icon-position));\n background-image: var(--icon-date);\n background-position: center right var(--icon-position);\n background-size: var(--icon-width) auto;\n background-repeat: no-repeat;\n}\ninput:not([type=checkbox], [type=radio], [type=range], [type=file])[type=time] {\n background-image: var(--icon-time);\n}\n\n[type=date]::-webkit-calendar-picker-indicator,\n[type=datetime-local]::-webkit-calendar-picker-indicator,\n[type=month]::-webkit-calendar-picker-indicator,\n[type=time]::-webkit-calendar-picker-indicator,\n[type=week]::-webkit-calendar-picker-indicator {\n width: var(--icon-width);\n margin-right: calc(var(--icon-width) * -1);\n margin-left: var(--icon-position);\n opacity: 0;\n}\n\n[dir=rtl] :is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) {\n text-align: right;\n}\n\n[type=file] {\n --color: var(--muted-color);\n padding: calc(var(--form-element-spacing-vertical) * 0.5) 0;\n border: 0;\n border-radius: 0;\n background: none;\n}\n[type=file]::file-selector-button {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n margin-right: calc(var(--spacing) / 2);\n margin-left: 0;\n margin-inline-start: 0;\n margin-inline-end: calc(var(--spacing) / 2);\n padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: 1rem;\n line-height: var(--line-height);\n text-align: center;\n cursor: pointer;\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\n[type=file]::file-selector-button:is(:hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n}\n[type=file]::-webkit-file-upload-button {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n margin-right: calc(var(--spacing) / 2);\n margin-left: 0;\n margin-inline-start: 0;\n margin-inline-end: calc(var(--spacing) / 2);\n padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: 1rem;\n line-height: var(--line-height);\n text-align: center;\n cursor: pointer;\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\n[type=file]::-webkit-file-upload-button:is(:hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n}\n[type=file]::-ms-browse {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n margin-right: calc(var(--spacing) / 2);\n margin-left: 0;\n margin-inline-start: 0;\n margin-inline-end: calc(var(--spacing) / 2);\n padding: calc(var(--form-element-spacing-vertical) * 0.5) calc(var(--form-element-spacing-horizontal) * 0.5);\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: 1rem;\n line-height: var(--line-height);\n text-align: center;\n cursor: pointer;\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\n[type=file]::-ms-browse:is(:hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n}\n\n[type=range] {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n width: 100%;\n height: 1.25rem;\n background: none;\n}\n[type=range]::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.25rem;\n border-radius: var(--border-radius);\n background-color: var(--range-border-color);\n transition: background-color var(--transition), box-shadow var(--transition);\n}\n[type=range]::-moz-range-track {\n width: 100%;\n height: 0.25rem;\n border-radius: var(--border-radius);\n background-color: var(--range-border-color);\n transition: background-color var(--transition), box-shadow var(--transition);\n}\n[type=range]::-ms-track {\n width: 100%;\n height: 0.25rem;\n border-radius: var(--border-radius);\n background-color: var(--range-border-color);\n transition: background-color var(--transition), box-shadow var(--transition);\n}\n[type=range]::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: -0.5rem;\n border: 2px solid var(--range-thumb-border-color);\n border-radius: 50%;\n background-color: var(--range-thumb-color);\n cursor: pointer;\n transition: background-color var(--transition), transform var(--transition);\n}\n[type=range]::-moz-range-thumb {\n -webkit-appearance: none;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: -0.5rem;\n border: 2px solid var(--range-thumb-border-color);\n border-radius: 50%;\n background-color: var(--range-thumb-color);\n cursor: pointer;\n transition: background-color var(--transition), transform var(--transition);\n}\n[type=range]::-ms-thumb {\n -webkit-appearance: none;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: -0.5rem;\n border: 2px solid var(--range-thumb-border-color);\n border-radius: 50%;\n background-color: var(--range-thumb-color);\n cursor: pointer;\n transition: background-color var(--transition), transform var(--transition);\n}\n[type=range]:hover, [type=range]:focus {\n --range-border-color: var(--range-active-border-color);\n --range-thumb-color: var(--range-thumb-hover-color);\n}\n[type=range]:active {\n --range-thumb-color: var(--range-thumb-active-color);\n}\n[type=range]:active::-webkit-slider-thumb {\n transform: scale(1.25);\n}\n[type=range]:active::-moz-range-thumb {\n transform: scale(1.25);\n}\n[type=range]:active::-ms-thumb {\n transform: scale(1.25);\n}\n\ninput:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] {\n padding-inline-start: calc(var(--form-element-spacing-horizontal) + 1.75rem);\n border-radius: 5rem;\n background-image: var(--icon-search);\n background-position: center left 1.125rem;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n}\ninput:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] {\n padding-inline-start: calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;\n background-position: center left 1.125rem, center right 0.75rem;\n}\ninput:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=false] {\n background-image: var(--icon-search), var(--icon-valid);\n}\ninput:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=true] {\n background-image: var(--icon-search), var(--icon-invalid);\n}\n\n[type=search]::-webkit-search-cancel-button {\n -webkit-appearance: none;\n display: none;\n}\n\n[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] {\n background-position: center right 1.125rem;\n}\n[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] {\n background-position: center right 1.125rem, center left 0.75rem;\n}\n\n/**\n * Table\n */\n:where(table) {\n width: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n text-indent: 0;\n}\n\nth,\ntd {\n padding: calc(var(--spacing) / 2) var(--spacing);\n border-bottom: var(--border-width) solid var(--table-border-color);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n text-align: left;\n text-align: start;\n}\n\ntfoot th,\ntfoot td {\n border-top: var(--border-width) solid var(--table-border-color);\n border-bottom: 0;\n}\n\ntable[role=grid] tbody tr:nth-child(odd) {\n background-color: var(--table-row-stripped-background-color);\n}\n\n/**\n * Code\n */\npre,\ncode,\nkbd,\nsamp {\n font-size: 0.875em;\n font-family: var(--font-family);\n}\n\npre {\n -ms-overflow-style: scrollbar;\n overflow: auto;\n}\n\npre,\ncode,\nkbd {\n border-radius: var(--border-radius);\n background: var(--code-background-color);\n color: var(--code-color);\n font-weight: var(--font-weight);\n line-height: initial;\n}\n\ncode,\nkbd {\n display: inline-block;\n padding: 0.375rem 0.5rem;\n}\n\npre {\n display: block;\n margin-bottom: var(--spacing);\n overflow-x: auto;\n}\npre > code {\n display: block;\n padding: var(--spacing);\n background: none;\n font-size: 14px;\n line-height: var(--line-height);\n}\n\ncode b {\n color: var(--code-tag-color);\n font-weight: var(--font-weight);\n}\ncode i {\n color: var(--code-property-color);\n font-style: normal;\n}\ncode u {\n color: var(--code-value-color);\n text-decoration: none;\n}\ncode em {\n color: var(--code-comment-color);\n font-style: normal;\n}\n\nkbd {\n background-color: var(--code-kbd-background-color);\n color: var(--code-kbd-color);\n vertical-align: baseline;\n}\n\n/**\n * Miscs\n */\nhr {\n height: 0;\n border: 0;\n border-top: 1px solid var(--muted-border-color);\n color: inherit;\n}\n\n[hidden],\ntemplate {\n display: none !important;\n}\n\ncanvas {\n display: inline-block;\n}\n\n/**\n * Accordion (
)\n */\ndetails {\n display: block;\n margin-bottom: var(--spacing);\n padding-bottom: var(--spacing);\n border-bottom: var(--border-width) solid var(--accordion-border-color);\n}\ndetails summary {\n line-height: 1rem;\n list-style-type: none;\n cursor: pointer;\n transition: color var(--transition);\n}\ndetails summary:not([role]) {\n color: var(--accordion-close-summary-color);\n}\ndetails summary::-webkit-details-marker {\n display: none;\n}\ndetails summary::marker {\n display: none;\n}\ndetails summary::-moz-list-bullet {\n list-style-type: none;\n}\ndetails summary::after {\n display: block;\n width: 1rem;\n height: 1rem;\n margin-inline-start: calc(var(--spacing, 1rem) * 0.5);\n float: right;\n transform: rotate(-90deg);\n background-image: var(--icon-chevron);\n background-position: right center;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n content: \"\";\n transition: transform var(--transition);\n}\ndetails summary:focus {\n outline: none;\n}\ndetails summary:focus:not([role=button]) {\n color: var(--accordion-active-summary-color);\n}\ndetails summary[role=button] {\n width: 100%;\n text-align: left;\n}\ndetails summary[role=button]::after {\n height: calc(1rem * var(--line-height, 1.5));\n background-image: var(--icon-chevron-button);\n}\ndetails[open] > summary {\n margin-bottom: calc(var(--spacing));\n}\ndetails[open] > summary:not([role]):not(:focus) {\n color: var(--accordion-open-summary-color);\n}\ndetails[open] > summary::after {\n transform: rotate(0);\n}\n\n[dir=rtl] details summary {\n text-align: right;\n}\n[dir=rtl] details summary::after {\n float: left;\n background-position: left center;\n}\n\n/**\n * Card (
)\n */\narticle {\n margin: var(--block-spacing-vertical) 0;\n padding: var(--block-spacing-vertical) var(--block-spacing-horizontal);\n border-radius: var(--border-radius);\n background: var(--card-background-color);\n box-shadow: var(--card-box-shadow);\n}\narticle > header,\narticle > footer {\n margin-right: calc(var(--block-spacing-horizontal) * -1);\n margin-left: calc(var(--block-spacing-horizontal) * -1);\n padding: calc(var(--block-spacing-vertical) * 0.66) var(--block-spacing-horizontal);\n background-color: var(--card-sectionning-background-color);\n}\narticle > header {\n margin-top: calc(var(--block-spacing-vertical) * -1);\n margin-bottom: var(--block-spacing-vertical);\n border-bottom: var(--border-width) solid var(--card-border-color);\n border-top-right-radius: var(--border-radius);\n border-top-left-radius: var(--border-radius);\n}\narticle > footer {\n margin-top: var(--block-spacing-vertical);\n margin-bottom: calc(var(--block-spacing-vertical) * -1);\n border-top: var(--border-width) solid var(--card-border-color);\n border-bottom-right-radius: var(--border-radius);\n border-bottom-left-radius: var(--border-radius);\n}\n\n/**\n * Modal ()\n */\n:root {\n --scrollbar-width: 0px;\n}\n\ndialog {\n display: flex;\n z-index: 999;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n align-items: center;\n justify-content: center;\n width: inherit;\n min-width: 100%;\n height: inherit;\n min-height: 100%;\n padding: var(--spacing);\n border: 0;\n backdrop-filter: var(--modal-overlay-backdrop-filter);\n background-color: var(--modal-overlay-background-color);\n color: var(--color);\n}\ndialog article {\n max-height: calc(100vh - var(--spacing) * 2);\n overflow: auto;\n}\n@media (min-width: 576px) {\n dialog article {\n max-width: 510px;\n }\n}\n@media (min-width: 768px) {\n dialog article {\n max-width: 700px;\n }\n}\ndialog article > header,\ndialog article > footer {\n padding: calc(var(--block-spacing-vertical) * 0.5) var(--block-spacing-horizontal);\n}\ndialog article > header .close {\n margin: 0;\n margin-left: var(--spacing);\n float: right;\n}\ndialog article > footer {\n text-align: right;\n}\ndialog article > footer [role=button] {\n margin-bottom: 0;\n}\ndialog article > footer [role=button]:not(:first-of-type) {\n margin-left: calc(var(--spacing) * 0.5);\n}\ndialog article p:last-of-type {\n margin: 0;\n}\ndialog:not([open]), dialog[open=false] {\n display: none;\n}\n\n/**\n * Nav\n */\n:where(nav li)::before {\n float: left;\n content: \"​\";\n}\n\nnav,\nnav ul {\n display: flex;\n}\n\nnav {\n justify-content: space-between;\n}\nnav ol,\nnav ul {\n align-items: center;\n margin-bottom: 0;\n padding: 0;\n list-style: none;\n}\nnav ol:first-of-type,\nnav ul:first-of-type {\n margin-left: calc(var(--nav-element-spacing-horizontal) * -1);\n}\nnav ol:last-of-type,\nnav ul:last-of-type {\n margin-right: calc(var(--nav-element-spacing-horizontal) * -1);\n}\nnav li {\n display: inline-block;\n margin: 0;\n padding: var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal);\n}\nnav li > * {\n --spacing: 0;\n}\nnav :where(a, [role=link]) {\n display: inline-block;\n margin: calc(var(--nav-link-spacing-vertical) * -1) calc(var(--nav-link-spacing-horizontal) * -1);\n padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);\n border-radius: var(--border-radius);\n text-decoration: none;\n}\nnav :where(a, [role=link]):is([aria-current], :hover, :active, :focus) {\n text-decoration: none;\n}\nnav[aria-label=breadcrumb] {\n align-items: center;\n justify-content: start;\n}\nnav[aria-label=breadcrumb] ul li:not(:first-child) {\n margin-inline-start: var(--nav-link-spacing-horizontal);\n}\nnav[aria-label=breadcrumb] ul li:not(:last-child) ::after {\n position: absolute;\n width: calc(var(--nav-link-spacing-horizontal) * 2);\n margin-inline-start: calc(var(--nav-link-spacing-horizontal) / 2);\n content: \"/\";\n color: var(--muted-color);\n text-align: center;\n}\nnav[aria-label=breadcrumb] a[aria-current] {\n background-color: transparent;\n color: inherit;\n text-decoration: none;\n pointer-events: none;\n}\nnav [role=button] {\n margin-right: inherit;\n margin-left: inherit;\n padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);\n}\n\naside nav,\naside ol,\naside ul,\naside li {\n display: block;\n}\naside li {\n padding: calc(var(--nav-element-spacing-vertical) * 0.5) var(--nav-element-spacing-horizontal);\n}\naside li a {\n display: block;\n}\naside li [role=button] {\n margin: inherit;\n}\n\n[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after {\n content: \"\\\\\";\n}\n\n/**\n * Progress\n */\nprogress {\n display: inline-block;\n vertical-align: baseline;\n}\n\nprogress {\n -webkit-appearance: none;\n -moz-appearance: none;\n display: inline-block;\n appearance: none;\n width: 100%;\n height: 0.5rem;\n margin-bottom: calc(var(--spacing) * 0.5);\n overflow: hidden;\n border: 0;\n border-radius: var(--border-radius);\n background-color: var(--progress-background-color);\n color: var(--progress-color);\n}\nprogress::-webkit-progress-bar {\n border-radius: var(--border-radius);\n background: none;\n}\nprogress[value]::-webkit-progress-value {\n background-color: var(--progress-color);\n}\nprogress::-moz-progress-bar {\n background-color: var(--progress-color);\n}\n@media (prefers-reduced-motion: no-preference) {\n progress:indeterminate {\n background: var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat;\n animation: progress-indeterminate 1s linear infinite;\n }\n progress:indeterminate[value]::-webkit-progress-value {\n background-color: transparent;\n }\n progress:indeterminate::-moz-progress-bar {\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n [dir=rtl] progress:indeterminate {\n animation-direction: reverse;\n }\n}\n\n@keyframes progress-indeterminate {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n/**\n * Dropdown ([role=\"list\"])\n */\ndetails[role=list],\nli[role=list] {\n position: relative;\n}\n\ndetails[role=list] summary + ul,\nli[role=list] > ul {\n display: flex;\n z-index: 99;\n position: absolute;\n top: auto;\n right: 0;\n left: 0;\n flex-direction: column;\n margin: 0;\n padding: 0;\n border: var(--border-width) solid var(--dropdown-border-color);\n border-radius: var(--border-radius);\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n background-color: var(--dropdown-background-color);\n box-shadow: var(--card-box-shadow);\n color: var(--dropdown-color);\n white-space: nowrap;\n}\ndetails[role=list] summary + ul li,\nli[role=list] > ul li {\n width: 100%;\n margin-bottom: 0;\n padding: calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal);\n list-style: none;\n}\ndetails[role=list] summary + ul li:first-of-type,\nli[role=list] > ul li:first-of-type {\n margin-top: calc(var(--form-element-spacing-vertical) * 0.5);\n}\ndetails[role=list] summary + ul li:last-of-type,\nli[role=list] > ul li:last-of-type {\n margin-bottom: calc(var(--form-element-spacing-vertical) * 0.5);\n}\ndetails[role=list] summary + ul li a,\nli[role=list] > ul li a {\n display: block;\n margin: calc(var(--form-element-spacing-vertical) * -0.5) calc(var(--form-element-spacing-horizontal) * -1);\n padding: calc(var(--form-element-spacing-vertical) * 0.5) var(--form-element-spacing-horizontal);\n overflow: hidden;\n color: var(--dropdown-color);\n text-decoration: none;\n text-overflow: ellipsis;\n}\ndetails[role=list] summary + ul li a:hover,\nli[role=list] > ul li a:hover {\n background-color: var(--dropdown-hover-background-color);\n}\n\ndetails[role=list] summary::after,\nli[role=list] > a::after {\n display: block;\n width: 1rem;\n height: calc(1rem * var(--line-height, 1.5));\n margin-inline-start: 0.5rem;\n float: right;\n transform: rotate(0deg);\n background-position: right center;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n content: \"\";\n}\n\ndetails[role=list] {\n padding: 0;\n border-bottom: none;\n}\ndetails[role=list] summary {\n margin-bottom: 0;\n}\ndetails[role=list] summary:not([role]) {\n height: calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2);\n padding: var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);\n border: var(--border-width) solid var(--form-element-border-color);\n border-radius: var(--border-radius);\n background-color: var(--form-element-background-color);\n color: var(--form-element-placeholder-color);\n line-height: inherit;\n cursor: pointer;\n transition: background-color var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);\n}\ndetails[role=list] summary:not([role]):active, details[role=list] summary:not([role]):focus {\n border-color: var(--form-element-active-border-color);\n background-color: var(--form-element-active-background-color);\n}\ndetails[role=list] summary:not([role]):focus {\n box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color);\n}\ndetails[role=list][open] summary {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\ndetails[role=list][open] summary::before {\n display: block;\n z-index: 1;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background: none;\n content: \"\";\n cursor: default;\n}\n\nnav details[role=list] summary,\nnav li[role=list] a {\n display: flex;\n direction: ltr;\n}\n\nnav details[role=list] summary + ul,\nnav li[role=list] > ul {\n min-width: fit-content;\n border-radius: var(--border-radius);\n}\nnav details[role=list] summary + ul li a,\nnav li[role=list] > ul li a {\n border-radius: 0;\n}\n\nnav details[role=list] summary,\nnav details[role=list] summary:not([role]) {\n height: auto;\n padding: var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);\n}\nnav details[role=list][open] summary {\n border-radius: var(--border-radius);\n}\nnav details[role=list] summary + ul {\n margin-top: var(--outline-width);\n margin-inline-start: 0;\n}\nnav details[role=list] summary[role=link] {\n margin-bottom: calc(var(--nav-link-spacing-vertical) * -1);\n line-height: var(--line-height);\n}\nnav details[role=list] summary[role=link] + ul {\n margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width));\n margin-inline-start: calc(var(--nav-link-spacing-horizontal) * -1);\n}\n\nli[role=list]:hover > ul,\nli[role=list] a:active ~ ul,\nli[role=list] a:focus ~ ul {\n display: flex;\n}\nli[role=list] > ul {\n display: none;\n margin-top: calc(var(--nav-link-spacing-vertical) + var(--outline-width));\n margin-inline-start: calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal));\n}\nli[role=list] > a::after {\n background-image: var(--icon-chevron);\n}\n\nlabel > details[role=list] {\n margin-top: calc(var(--spacing) * 0.25);\n margin-bottom: var(--spacing);\n}\n\n/**\n * Loading ([aria-busy=true])\n */\n[aria-busy=true] {\n cursor: progress;\n}\n\n[aria-busy=true]:not(input, select, textarea)::before {\n display: inline-block;\n width: 1em;\n height: 1em;\n border: 0.1875em solid currentColor;\n border-radius: 1em;\n border-right-color: transparent;\n content: \"\";\n vertical-align: text-bottom;\n vertical-align: -0.125em;\n animation: spinner 0.75s linear infinite;\n opacity: var(--loading-spinner-opacity);\n}\n[aria-busy=true]:not(input, select, textarea):not(:empty)::before {\n margin-right: calc(var(--spacing) * 0.5);\n margin-left: 0;\n margin-inline-start: 0;\n margin-inline-end: calc(var(--spacing) * 0.5);\n}\n[aria-busy=true]:not(input, select, textarea):empty {\n text-align: center;\n}\n\nbutton[aria-busy=true],\ninput[type=submit][aria-busy=true],\ninput[type=button][aria-busy=true],\ninput[type=reset][aria-busy=true],\na[aria-busy=true] {\n pointer-events: none;\n}\n\n@keyframes spinner {\n to {\n transform: rotate(360deg);\n }\n}\n/**\n * Tooltip ([data-tooltip])\n */\n[data-tooltip] {\n position: relative;\n}\n[data-tooltip]:not(a, button, input) {\n border-bottom: 1px dotted;\n text-decoration: none;\n cursor: help;\n}\n[data-tooltip][data-placement=top]::before, [data-tooltip][data-placement=top]::after, [data-tooltip]::before, [data-tooltip]::after {\n display: block;\n z-index: 99;\n position: absolute;\n bottom: 100%;\n left: 50%;\n padding: 0.25rem 0.5rem;\n overflow: hidden;\n transform: translate(-50%, -0.25rem);\n border-radius: var(--border-radius);\n background: var(--tooltip-background-color);\n content: attr(data-tooltip);\n color: var(--tooltip-color);\n font-style: normal;\n font-weight: var(--font-weight);\n font-size: 0.875rem;\n text-decoration: none;\n text-overflow: ellipsis;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n}\n[data-tooltip][data-placement=top]::after, [data-tooltip]::after {\n padding: 0;\n transform: translate(-50%, 0rem);\n border-top: 0.3rem solid;\n border-right: 0.3rem solid transparent;\n border-left: 0.3rem solid transparent;\n border-radius: 0;\n background-color: transparent;\n content: \"\";\n color: var(--tooltip-background-color);\n}\n[data-tooltip][data-placement=bottom]::before, [data-tooltip][data-placement=bottom]::after {\n top: 100%;\n bottom: auto;\n transform: translate(-50%, 0.25rem);\n}\n[data-tooltip][data-placement=bottom]:after {\n transform: translate(-50%, -0.3rem);\n border: 0.3rem solid transparent;\n border-bottom: 0.3rem solid;\n}\n[data-tooltip][data-placement=left]::before, [data-tooltip][data-placement=left]::after {\n top: 50%;\n right: 100%;\n bottom: auto;\n left: auto;\n transform: translate(-0.25rem, -50%);\n}\n[data-tooltip][data-placement=left]:after {\n transform: translate(0.3rem, -50%);\n border: 0.3rem solid transparent;\n border-left: 0.3rem solid;\n}\n[data-tooltip][data-placement=right]::before, [data-tooltip][data-placement=right]::after {\n top: 50%;\n right: auto;\n bottom: auto;\n left: 100%;\n transform: translate(0.25rem, -50%);\n}\n[data-tooltip][data-placement=right]:after {\n transform: translate(-0.3rem, -50%);\n border: 0.3rem solid transparent;\n border-right: 0.3rem solid;\n}\n[data-tooltip]:focus::before, [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after {\n opacity: 1;\n}\n@media (hover: hover) and (pointer: fine) {\n [data-tooltip][data-placement=bottom]:focus::before, [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::before, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after, [data-tooltip]:hover::before, [data-tooltip]:hover::after {\n animation-duration: 0.2s;\n animation-name: tooltip-slide-top;\n }\n [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after, [data-tooltip]:hover::after {\n animation-name: tooltip-caret-slide-top;\n }\n [data-tooltip][data-placement=bottom]:focus::before, [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::before, [data-tooltip][data-placement=bottom]:hover::after {\n animation-duration: 0.2s;\n animation-name: tooltip-slide-bottom;\n }\n [data-tooltip][data-placement=bottom]:focus::after, [data-tooltip][data-placement=bottom]:hover::after {\n animation-name: tooltip-caret-slide-bottom;\n }\n [data-tooltip][data-placement=left]:focus::before, [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::before, [data-tooltip][data-placement=left]:hover::after {\n animation-duration: 0.2s;\n animation-name: tooltip-slide-left;\n }\n [data-tooltip][data-placement=left]:focus::after, [data-tooltip][data-placement=left]:hover::after {\n animation-name: tooltip-caret-slide-left;\n }\n [data-tooltip][data-placement=right]:focus::before, [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::before, [data-tooltip][data-placement=right]:hover::after {\n animation-duration: 0.2s;\n animation-name: tooltip-slide-right;\n }\n [data-tooltip][data-placement=right]:focus::after, [data-tooltip][data-placement=right]:hover::after {\n animation-name: tooltip-caret-slide-right;\n }\n}\n@keyframes tooltip-slide-top {\n from {\n transform: translate(-50%, 0.75rem);\n opacity: 0;\n }\n to {\n transform: translate(-50%, -0.25rem);\n opacity: 1;\n }\n}\n@keyframes tooltip-caret-slide-top {\n from {\n opacity: 0;\n }\n 50% {\n transform: translate(-50%, -0.25rem);\n opacity: 0;\n }\n to {\n transform: translate(-50%, 0rem);\n opacity: 1;\n }\n}\n@keyframes tooltip-slide-bottom {\n from {\n transform: translate(-50%, -0.75rem);\n opacity: 0;\n }\n to {\n transform: translate(-50%, 0.25rem);\n opacity: 1;\n }\n}\n@keyframes tooltip-caret-slide-bottom {\n from {\n opacity: 0;\n }\n 50% {\n transform: translate(-50%, -0.5rem);\n opacity: 0;\n }\n to {\n transform: translate(-50%, -0.3rem);\n opacity: 1;\n }\n}\n@keyframes tooltip-slide-left {\n from {\n transform: translate(0.75rem, -50%);\n opacity: 0;\n }\n to {\n transform: translate(-0.25rem, -50%);\n opacity: 1;\n }\n}\n@keyframes tooltip-caret-slide-left {\n from {\n opacity: 0;\n }\n 50% {\n transform: translate(0.05rem, -50%);\n opacity: 0;\n }\n to {\n transform: translate(0.3rem, -50%);\n opacity: 1;\n }\n}\n@keyframes tooltip-slide-right {\n from {\n transform: translate(-0.75rem, -50%);\n opacity: 0;\n }\n to {\n transform: translate(0.25rem, -50%);\n opacity: 1;\n }\n}\n@keyframes tooltip-caret-slide-right {\n from {\n opacity: 0;\n }\n 50% {\n transform: translate(-0.05rem, -50%);\n opacity: 0;\n }\n to {\n transform: translate(-0.3rem, -50%);\n opacity: 1;\n }\n}\n\n/**\n * Accessibility & User interaction\n */\n[aria-controls] {\n cursor: pointer;\n}\n\n[aria-disabled=true],\n[disabled] {\n cursor: not-allowed;\n}\n\n[aria-hidden=false][hidden] {\n display: initial;\n}\n\n[aria-hidden=false][hidden]:not(:focus) {\n clip: rect(0, 0, 0, 0);\n position: absolute;\n}\n\na,\narea,\nbutton,\ninput,\nlabel,\nselect,\nsummary,\ntextarea,\n[tabindex] {\n -ms-touch-action: manipulation;\n}\n\n[dir=rtl] {\n direction: rtl;\n}\n\n/**\n* Reduce Motion Features\n*/\n@media (prefers-reduced-motion: reduce) {\n *:not([aria-busy=true]),\n :not([aria-busy=true])::before,\n :not([aria-busy=true])::after {\n background-attachment: initial !important;\n animation-duration: 1ms !important;\n animation-delay: -1ms !important;\n animation-iteration-count: 1 !important;\n scroll-behavior: auto !important;\n transition-delay: 0s !important;\n transition-duration: 0s !important;\n }\n}\n\n/*# sourceMappingURL=pico.classless.css.map */\n","/*!\n * Pico CSS v1.5.9 (https://picocss.com)\n * Copyright 2019-2023 - Licensed under MIT\n */\n\n// Config\n@import \"variables\";\n\n// Theming\n@import \"themes/default\";\n\n// Layout\n@import \"layout/document\"; // html\n@import \"layout/sectioning\"; // body, header, main, footer\n@import \"layout/container\"; // .container, .container-fluid\n@import \"layout/section\"; // section\n@import \"layout/grid\"; // .grid\n@import \"layout/scroller\"; // figure\n\n// Content\n@import \"content/typography\"; // a, headings, p, ul, blockquote, ...\n@import \"content/embedded\"; // audio, canvas, iframe, img, svg, video\n@import \"content/button\"; // button, a[role=button], type=button, type=submit ...\n@import \"content/form\"; // input, select, textarea, label, fieldset, legend\n@import \"content/form-checkbox-radio\"; // type=checkbox, type=radio, role=switch\n@import \"content/form-alt-input-types\"; // type=color, type=date, type=file, type=search, ...\n@import \"content/table\"; // table, tr, td, ...\n@import \"content/code\"; // pre, code, ...\n@import \"content/miscs\"; // hr, template, [hidden], dialog, canvas\n\n// Components\n@import \"components/accordion\"; // details, summary\n@import \"components/card\"; // article\n@import \"components/modal\"; // dialog\n@import \"components/nav\"; // nav\n@import \"components/progress\"; // progress\n@import \"components/dropdown\"; // dropdown\n\n// Utilities\n@import \"utilities/loading\"; // aria-busy=true\n@import \"utilities/tooltip\"; // data-tooltip\n@import \"utilities/accessibility\"; // -ms-touch-action, aria-*\n@import \"utilities/reduce-motion\"; // prefers-reduced-motion\n","/**\n * Theme: default\n */\n\n// Variables\n@import \"../variables\";\n@import \"default/colors\";\n\n// Commons styles\n@import \"default/styles\";\n\n// Light theme (Default)\n// Can be forced with data-theme=\"light\"\n@import \"default/light\";\n\n// Dark theme (Auto)\n// Automatically enabled if user has Dark mode enabled\n@import \"default/dark\";\n@media only screen and (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n @include dark;\n }\n}\n\n// Dark theme (Forced)\n// Enabled if forced with data-theme=\"dark\"\n[data-theme=\"dark\"] {\n @include dark;\n}\n\n// Accent-color\nprogress,\n[type=\"checkbox\"],\n[type=\"radio\"],\n[type=\"range\"] {\n accent-color: var(--primary);\n}\n","// Commons Styles\n:root {\n // Typography\n --font-family: system-ui, -apple-system, \"Segoe UI\", \"Roboto\", \"Ubuntu\",\n \"Cantarell\", \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\",\n \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --line-height: 1.5;\n --font-weight: 400;\n --font-size: 16px;\n\n // Responsive typography\n @if $enable-responsive-typography {\n @if map-get($breakpoints, \"sm\") {\n @media (min-width: map-get($breakpoints, \"sm\")) {\n --font-size: 17px;\n }\n }\n\n @if map-get($breakpoints, \"md\") {\n @media (min-width: map-get($breakpoints, \"md\")) {\n --font-size: 18px;\n }\n }\n\n @if map-get($breakpoints, \"lg\") {\n @media (min-width: map-get($breakpoints, \"lg\")) {\n --font-size: 19px;\n }\n }\n\n @if map-get($breakpoints, \"xl\") {\n @media (min-width: map-get($breakpoints, \"xl\")) {\n --font-size: 20px;\n }\n }\n }\n\n // Borders\n --border-radius: 0.25rem;\n --border-width: 1px;\n --outline-width: 3px;\n\n // Spacings\n --spacing: 1rem;\n\n // Spacings for typography elements\n --typography-spacing-vertical: 1.5rem;\n\n // Spacings for body > header, body > main, body > footer, section, article\n --block-spacing-vertical: calc(var(--spacing) * 2);\n --block-spacing-horizontal: var(--spacing);\n\n @if ($enable-classes and $enable-grid) {\n --grid-spacing-vertical: 0;\n --grid-spacing-horizontal: var(--spacing);\n }\n\n // Spacings for form elements and button\n --form-element-spacing-vertical: 0.75rem;\n --form-element-spacing-horizontal: 1rem;\n\n // Spacings for nav component\n --nav-element-spacing-vertical: 1rem;\n --nav-element-spacing-horizontal: 0.5rem;\n --nav-link-spacing-vertical: 0.5rem;\n --nav-link-spacing-horizontal: 0.5rem;\n\n // Font weight for form labels & fieldsets legend\n --form-label-font-weight: var(--font-weight);\n\n // Transitions\n --transition: 0.2s ease-in-out;\n\n // Modal ()\n --modal-overlay-backdrop-filter: blur(0.25rem);\n}\n\n// Responsives spacings\n@if $enable-responsive-spacings {\n // Sectioning\n #{$semantic-root-element} > header,\n #{$semantic-root-element} > main,\n #{$semantic-root-element} > footer,\n section {\n @if map-get($breakpoints, \"sm\") {\n @media (min-width: map-get($breakpoints, \"sm\")) {\n --block-spacing-vertical: calc(var(--spacing) * 2.5);\n }\n }\n\n @if map-get($breakpoints, \"md\") {\n @media (min-width: map-get($breakpoints, \"md\")) {\n --block-spacing-vertical: calc(var(--spacing) * 3);\n }\n }\n\n @if map-get($breakpoints, \"lg\") {\n @media (min-width: map-get($breakpoints, \"lg\")) {\n --block-spacing-vertical: calc(var(--spacing) * 3.5);\n }\n }\n\n @if map-get($breakpoints, \"xl\") {\n @media (min-width: map-get($breakpoints, \"xl\")) {\n --block-spacing-vertical: calc(var(--spacing) * 4);\n }\n }\n }\n\n // Card (
)\n article {\n @if map-get($breakpoints, \"sm\") {\n @media (min-width: map-get($breakpoints, \"sm\")) {\n --block-spacing-horizontal: calc(var(--spacing) * 1.25);\n }\n }\n\n @if map-get($breakpoints, \"md\") {\n @media (min-width: map-get($breakpoints, \"md\")) {\n --block-spacing-horizontal: calc(var(--spacing) * 1.5);\n }\n }\n\n @if map-get($breakpoints, \"lg\") {\n @media (min-width: map-get($breakpoints, \"lg\")) {\n --block-spacing-horizontal: calc(var(--spacing) * 1.75);\n }\n }\n\n @if map-get($breakpoints, \"xl\") {\n @media (min-width: map-get($breakpoints, \"xl\")) {\n --block-spacing-horizontal: calc(var(--spacing) * 2);\n }\n }\n }\n\n // Modal\n dialog > article {\n\n --block-spacing-vertical: calc(var(--spacing) * 2);\n --block-spacing-horizontal: var(--spacing);\n\n @if map-get($breakpoints, \"sm\") {\n @media (min-width: map-get($breakpoints, \"sm\")) {\n --block-spacing-vertical: calc(var(--spacing) * 2.5);\n --block-spacing-horizontal: calc(var(--spacing) * 1.25);\n }\n }\n\n @if map-get($breakpoints, \"md\") {\n @media (min-width: map-get($breakpoints, \"md\")) {\n --block-spacing-vertical: calc(var(--spacing) * 3);\n --block-spacing-horizontal: calc(var(--spacing) * 1.5);\n }\n }\n }\n}\n\n// Link\na {\n --text-decoration: none;\n\n // Secondary & Contrast\n @if $enable-classes {\n &.secondary,\n &.contrast {\n --text-decoration: underline;\n }\n }\n}\n\n// Small\nsmall {\n --font-size: 0.875em;\n}\n\n// Headings\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n --font-weight: 700;\n}\n\nh1 {\n --font-size: 2rem;\n --typography-spacing-vertical: 3rem;\n}\n\nh2 {\n --font-size: 1.75rem;\n --typography-spacing-vertical: 2.625rem;\n}\n\nh3 {\n --font-size: 1.5rem;\n --typography-spacing-vertical: 2.25rem;\n}\n\nh4 {\n --font-size: 1.25rem;\n --typography-spacing-vertical: 1.874rem;\n}\n\nh5 {\n --font-size: 1.125rem;\n --typography-spacing-vertical: 1.6875rem;\n}\n\n// Forms elements\n[type=\"checkbox\"],\n[type=\"radio\"] {\n --border-width: 2px;\n}\n\n[type=\"checkbox\"][role=\"switch\"] {\n --border-width: 3px;\n}\n\n// Table\nthead,\ntfoot {\n th,\n td {\n --border-width: 3px;\n }\n}\n\n:not(thead, tfoot) > * > td {\n --font-size: 0.875em;\n}\n\n// Code\npre,\ncode,\nkbd,\nsamp {\n --font-family: \"Menlo\", \"Consolas\", \"Roboto Mono\", \"Ubuntu Monospace\",\n \"Noto Mono\", \"Oxygen Mono\", \"Liberation Mono\", monospace,\n \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n}\n\nkbd {\n --font-weight: bolder;\n}\n","@import \"../../functions\";\n\n// Default: Light theme\n[data-theme=\"light\"],\n:root:not([data-theme=\"dark\"]) {\n --background-color: #{$white};\n\n // Texts colors\n --color: #{$grey-700};\n --h1-color: #{$grey-900};\n --h2-color: #{mix($grey-900, $grey-800)};\n --h3-color: #{$grey-800};\n --h4-color: #{mix($grey-800, $grey-700)};\n --h5-color: #{$grey-700};\n --h6-color: #{mix($grey-700, $grey-600)};\n\n // Muted colors\n --muted-color: #{$grey-500};\n --muted-border-color: #{$grey-50};\n\n // Primary colors\n --primary: #{$primary-600};\n --primary-hover: #{$primary-700};\n --primary-focus: #{rgba($primary-600, 0.125)};\n --primary-inverse: #{$white};\n\n // Secondary colors\n --secondary: #{$grey-600};\n --secondary-hover: #{$grey-700};\n --secondary-focus: #{rgba($grey-600, 0.125)};\n --secondary-inverse: #{$white};\n\n // Contrast colors\n --contrast: #{$grey-900};\n --contrast-hover: #{$black};\n --contrast-focus: #{rgba($grey-600, 0.125)};\n --contrast-inverse: #{$white};\n\n // Highlighted text ()\n --mark-background-color: #{mix($amber-100, $amber-50)};\n --mark-color: #{mix($grey-900, $amber-900, 75%)};\n\n // Inserted () & Deleted ()\n --ins-color: #{$green-700};\n --del-color: #{$red-800};\n\n // Blockquote\n --blockquote-border-color: var(--muted-border-color);\n --blockquote-footer-color: var(--muted-color);\n\n // Button\n // To disable box-shadow, remove the var or set to '0 0 0 rgba(0, 0, 0, 0)'\n // Don't use, 'none, 'false, 'null', '0', etc.\n --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n\n // Form elements\n --form-element-background-color: transparent;\n --form-element-border-color: #{$grey-300};\n --form-element-color: var(--color);\n --form-element-placeholder-color: var(--muted-color);\n --form-element-active-background-color: transparent;\n --form-element-active-border-color: var(--primary);\n --form-element-focus-color: var(--primary-focus);\n --form-element-disabled-background-color: #{$grey-100};\n --form-element-disabled-border-color: #{$grey-300};\n --form-element-disabled-opacity: 0.5;\n --form-element-invalid-border-color: #{$red-800};\n --form-element-invalid-active-border-color: #{$red-700};\n --form-element-invalid-focus-color: #{rgba($red-700, 0.125)};\n --form-element-valid-border-color: #{$green-700};\n --form-element-valid-active-border-color: #{$green-600};\n --form-element-valid-focus-color: #{rgba($green-600, 0.125)};\n\n // Switch (input[type=\"checkbox\"][role=\"switch\"])\n --switch-background-color: #{$grey-200};\n --switch-color: var(--primary-inverse);\n --switch-checked-background-color: var(--primary);\n\n // Range (input[type=\"range\"])\n --range-border-color: #{$grey-100};\n --range-active-border-color: #{$grey-200};\n --range-thumb-border-color: var(--background-color);\n --range-thumb-color: var(--secondary);\n --range-thumb-hover-color: var(--secondary-hover);\n --range-thumb-active-color: var(--primary);\n\n // Table\n --table-border-color: var(--muted-border-color);\n --table-row-stripped-background-color: #{mix($grey-50, $white)};\n\n // Code\n --code-background-color: #{$grey-50};\n --code-color: var(--muted-color);\n --code-kbd-background-color: var(--contrast);\n --code-kbd-color: var(--contrast-inverse);\n --code-tag-color: #{hsl(330, 40%, 50%)};\n --code-property-color: #{hsl(185, 40%, 40%)};\n --code-value-color: #{hsl(40, 20%, 50%)};\n --code-comment-color: #{$grey-300};\n\n // Accordion (
)\n --accordion-border-color: var(--muted-border-color);\n --accordion-close-summary-color: var(--color);\n --accordion-open-summary-color: var(--muted-color);\n\n // Card (
)\n $box-shadow-elevation: 1rem;\n $box-shadow-blur-strengh: 6rem;\n $box-shadow-opacity: 0.06;\n --card-background-color: var(--background-color);\n --card-border-color: var(--muted-border-color);\n --card-box-shadow:\n #{($box-shadow-elevation * 0.5 * 0.029)} #{($box-shadow-elevation * 0.029)} #{($box-shadow-blur-strengh * 0.029)} #{rgba($grey-900, ($box-shadow-opacity * 0.283))},\n #{($box-shadow-elevation * 0.5 * 0.067)} #{($box-shadow-elevation * 0.067)} #{($box-shadow-blur-strengh * 0.067)} #{rgba($grey-900, ($box-shadow-opacity * 0.4))},\n #{($box-shadow-elevation * 0.5 * 0.125)} #{($box-shadow-elevation * 0.125)} #{($box-shadow-blur-strengh * 0.125)} #{rgba($grey-900, ($box-shadow-opacity * 0.5))},\n #{($box-shadow-elevation * 0.5 * 0.225)} #{($box-shadow-elevation * 0.225)} #{($box-shadow-blur-strengh * 0.225)} #{rgba($grey-900, ($box-shadow-opacity * 0.6))},\n #{($box-shadow-elevation * 0.5 * 0.417)} #{($box-shadow-elevation * 0.417)} #{($box-shadow-blur-strengh * 0.417)} #{rgba($grey-900, ($box-shadow-opacity * 0.717))},\n #{($box-shadow-elevation * 0.5)} #{$box-shadow-elevation} #{$box-shadow-blur-strengh} #{rgba($grey-900, $box-shadow-opacity)},\n 0 0 0 0.0625rem #{rgba($grey-900, ($box-shadow-opacity * 0.25) )};\n --card-sectionning-background-color: #{mix($grey-50, $white, 25%)};\n\n // Dropdown (
)\n --dropdown-background-color: #{mix($grey-50, $white, 25%)};\n --dropdown-border-color: #{mix($grey-100, $grey-50)};\n --dropdown-box-shadow: var(--card-box-shadow);\n --dropdown-color: var(--color);\n --dropdown-hover-background-color: #{$grey-50};\n\n // Modal ()\n --modal-overlay-background-color: #{rgba($grey-100, 0.7)};\n\n // Progress\n --progress-background-color: #{$grey-100};\n --progress-color: var(--primary);\n\n // Loading ([aria-busy=true])\n --loading-spinner-opacity: 0.5;\n\n // Tooltip ([data-tooltip])\n --tooltip-background-color: var(--contrast);\n --tooltip-color: var(--contrast-inverse);\n\n // Icons\n --icon-checkbox: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-700)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button-inverse: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-close: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-500)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");\n --icon-date: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-700)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");\n --icon-invalid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($red-800)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\");\n --icon-minus: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");\n --icon-search: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-700)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");\n --icon-time: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-700)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\"); \n --icon-valid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($green-700)}' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n \n // Document\n color-scheme: light;\n}\n","@import \"../../functions\";\n\n// Default: Dark theme\n@mixin dark {\n --background-color: #{mix($black, $grey-900, 37.5%)};\n\n // Texts colors\n --color: #{$grey-200};\n --h1-color: #{$grey-50};\n --h2-color: #{mix($grey-100, $grey-50)};\n --h3-color: #{$grey-100};\n --h4-color: #{mix($grey-200, $grey-100)};\n --h5-color: #{$grey-200};\n --h6-color: #{mix($grey-300, $grey-200)};\n\n // Muted colors\n --muted-color: #{$grey-500};\n --muted-border-color: #{mix($grey-900, $grey-800, 75%)};\n\n // Primary colors\n --primary: #{$primary-600};\n --primary-hover: #{$primary-500};\n --primary-focus: #{rgba($primary-600, 0.25)};\n --primary-inverse: #{$white};\n\n // Secondary colors\n --secondary: #{$grey-600};\n --secondary-hover: #{$grey-500};\n --secondary-focus: #{rgba($grey-500, 0.25)};\n --secondary-inverse: #{$white};\n\n // Contrast colors\n --contrast: #{$grey-50};\n --contrast-hover: #{$white};\n --contrast-focus: #{rgba($grey-500, 0.25)};\n --contrast-inverse: #{$black};\n\n // Highlighted text ()\n --mark-background-color: #{mix($grey-300, $amber-300)};\n --mark-color: #{mix($black, $grey-900, 37.5%)};\n\n // Inserted () & Deleted ()\n --ins-color: #{$green-700};\n --del-color: #{$red-800};\n\n // Blockquote\n --blockquote-border-color: var(--muted-border-color);\n --blockquote-footer-color: var(--muted-color);\n\n // Button\n // To disable box-shadow, remove the var or set to '0 0 0 rgba(0, 0, 0, 0)'\n // Don't use, 'none, 'false, 'null', '0', etc.\n --button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n --button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);\n\n // Form elements\n --form-element-background-color: #{mix($black, $grey-900, 37.5%)};\n --form-element-border-color: #{mix($grey-800, $grey-700)};\n --form-element-color: var(--color);\n --form-element-placeholder-color: var(--muted-color);\n --form-element-active-background-color: var(--form-element-background-color);\n --form-element-active-border-color: var(--primary);\n --form-element-focus-color: var(--primary-focus);\n --form-element-disabled-background-color: #{$grey-800};\n --form-element-disabled-border-color: #{$grey-700};\n --form-element-disabled-opacity: 0.5;\n --form-element-invalid-border-color: #{$red-900};\n --form-element-invalid-active-border-color: #{$red-800};\n --form-element-invalid-focus-color: #{rgba($red-800, 0.25)};\n --form-element-valid-border-color: #{$green-800};\n --form-element-valid-active-border-color: #{$green-700};\n --form-element-valid-focus-color: #{rgba($green-700, 0.25)};\n\n // Switch (input[type=\"checkbox\"][role=\"switch\"])\n --switch-background-color: #{mix($grey-800, $grey-700)};\n --switch-color: var(--primary-inverse);\n --switch-checked-background-color: var(--primary);\n\n // Range (input[type=\"range\"])\n --range-border-color: #{mix($grey-900, $grey-800)};\n --range-active-border-color: #{$grey-800};\n --range-thumb-border-color: var(--background-color);\n --range-thumb-color: var(--secondary);\n --range-thumb-hover-color: var(--secondary-hover);\n --range-thumb-active-color: var(--primary);\n\n // Table\n --table-border-color: var(--muted-border-color);\n --table-row-stripped-background-color: #{rgba($grey-500, 0.05)};\n\n // Code\n --code-background-color: #{mix($black, $grey-900, 12.5%)};\n --code-color: var(--muted-color);\n --code-kbd-background-color: var(--contrast);\n --code-kbd-color: var(--contrast-inverse);\n --code-tag-color: #{hsl(330, 30%, 50%)};\n --code-property-color: #{hsl(185, 30%, 50%)};\n --code-value-color: #{hsl(40, 10%, 50%)};\n --code-comment-color: #{mix($grey-700, $grey-600)};\n\n // Accordion (
)\n --accordion-border-color: var(--muted-border-color);\n --accordion-active-summary-color: var(--primary);\n --accordion-close-summary-color: var(--color);\n --accordion-open-summary-color: var(--muted-color);\n\n // Card (
)\n $box-shadow-elevation: 1rem;\n $box-shadow-blur-strengh: 6rem;\n $box-shadow-opacity: 0.06;\n --card-background-color: #{mix($black, $grey-900, 25%)};\n --card-border-color: var(--card-background-color);\n --card-box-shadow:\n #{($box-shadow-elevation * 0.5 * 0.029)} #{($box-shadow-elevation * 0.029)} #{($box-shadow-blur-strengh * 0.029)} #{rgba($black, ($box-shadow-opacity * 0.283))},\n #{($box-shadow-elevation * 0.5 * 0.067)} #{($box-shadow-elevation * 0.067)} #{($box-shadow-blur-strengh * 0.067)} #{rgba($black, ($box-shadow-opacity * 0.4))},\n #{($box-shadow-elevation * 0.5 * 0.125)} #{($box-shadow-elevation * 0.125)} #{($box-shadow-blur-strengh * 0.125)} #{rgba($black, ($box-shadow-opacity * 0.5))},\n #{($box-shadow-elevation * 0.5 * 0.225)} #{($box-shadow-elevation * 0.225)} #{($box-shadow-blur-strengh * 0.225)} #{rgba($black, ($box-shadow-opacity * 0.6))},\n #{($box-shadow-elevation * 0.5 * 0.417)} #{($box-shadow-elevation * 0.417)} #{($box-shadow-blur-strengh * 0.417)} #{rgba($black, ($box-shadow-opacity * 0.717))},\n #{($box-shadow-elevation * 0.5)} #{$box-shadow-elevation} #{$box-shadow-blur-strengh} #{rgba($black, $box-shadow-opacity)},\n 0 0 0 0.0625rem #{rgba($black, ($box-shadow-opacity * 0.25) )};\n --card-sectionning-background-color: #{mix($black, $grey-900, 12.5%)};\n\n // Dropdown (
)\n --dropdown-background-color: #{$grey-900};\n --dropdown-border-color: #{mix($grey-900, $grey-800)};\n --dropdown-box-shadow: var(--card-box-shadow);\n --dropdown-color: var(--color);\n --dropdown-hover-background-color: #{rgba(mix($grey-900, $grey-800), 0.75)};\n\n // Modal ()\n --modal-overlay-background-color: #{rgba(mix($grey-900, $grey-800), 0.8)};\n\n // Progress\n --progress-background-color: #{mix($grey-900, $grey-800)};\n --progress-color: var(--primary);\n\n // Loading ([aria-busy=true])\n --loading-spinner-opacity: 0.5;\n\n // Tooltip ([data-tooltip])\n --tooltip-background-color: var(--contrast);\n --tooltip-color: var(--contrast-inverse);\n\n // Icons\n --icon-checkbox: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-300)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-chevron-button-inverse: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($black)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-close: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-500)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E\");\n --icon-date: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-300)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E\");\n --icon-invalid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($red-900)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E\");\n --icon-minus: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E\");\n --icon-search: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-300)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E\");\n --icon-time: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($grey-300)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E\");\n --icon-valid: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{to-rgb($green-800)}' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\");\n \n // Document\n color-scheme: dark;\n}\n","/**\n * Document\n * Content-box & Responsive typography\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// 1. Add border box sizing in all browsers (opinionated)\n// 2. Backgrounds do not repeat by default (opinionated)\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n background-repeat: no-repeat; // 2\n}\n\n// 1. Add text decoration inheritance in all browsers (opinionated)\n// 2. Add vertical alignment inheritance in all browsers (opinionated)\n::before,\n::after {\n text-decoration: inherit; // 1\n vertical-align: inherit; // 2\n}\n\n// 1. Use the default cursor in all browsers (opinionated)\n// 2. Change the line height in all browsers (opinionated)\n// 3. Breaks words to prevent overflow in all browsers (opinionated)\n// 4. Use a 4-space tab width in all browsers (opinionated)\n// 5. Remove the grey highlight on links in iOS (opinionated)\n// 6. Prevent adjustments of font size after orientation changes in iOS\n:where(:root) {\n -webkit-tap-highlight-color: transparent; // 5\n -webkit-text-size-adjust: 100%; // 6\n text-size-adjust: 100%; // 6\n background-color: var(--background-color);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n line-height: var(--line-height); // 2\n font-family: var(--font-family);\n text-rendering: optimizeLegibility;\n overflow-wrap: break-word; // 3\n cursor: default; // 1\n tab-size: 4; // 4\n}\n","/**\n * Sectioning\n * Container and responsive spacings for header, main, footer\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// Render the `main` element consistently in IE\nmain {\n display: block;\n}\n\n// Pico\n// ––––––––––––––––––––\n\n// 1. Remove the margin in all browsers (opinionated)\n#{$semantic-root-element} {\n width: 100%;\n margin: 0; // 1\n\n > header,\n > main,\n > footer {\n width: 100%;\n margin-right: auto;\n margin-left: auto;\n\n // Semantic container\n @if $enable-semantic-container {\n padding: var(--block-spacing-vertical) var(--block-spacing-horizontal);\n\n // Centered viewport\n @if $enable-viewport {\n @if map-get($breakpoints, \"sm\") and $enable-viewport {\n @media (min-width: map-get($breakpoints, \"sm\")) {\n max-width: map-get($viewports, \"sm\");\n padding-right: 0;\n padding-left: 0;\n }\n }\n\n @if map-get($breakpoints, \"md\") and $enable-viewport {\n @media (min-width: map-get($breakpoints, \"md\")) {\n max-width: map-get($viewports, \"md\");\n }\n }\n\n @if map-get($breakpoints, \"lg\") and $enable-viewport {\n @media (min-width: map-get($breakpoints, \"lg\")) {\n max-width: map-get($viewports, \"lg\");\n }\n }\n\n @if map-get($breakpoints, \"xl\") and $enable-viewport {\n @media (min-width: map-get($breakpoints, \"xl\")) {\n max-width: map-get($viewports, \"xl\");\n }\n }\n }\n }\n\n // Semantic container\n @else {\n padding: var(--block-spacing-vertical) 0;\n }\n }\n}\n","/**\n * Section\n * Responsive spacings for section\n */\n\nsection {\n margin-bottom: var(--block-spacing-vertical);\n}\n","/**\n * Horizontal scroller (
)\n */\n\n// Wrapper to make any content responsive across all viewports\nfigure {\n display: block;\n margin: 0;\n padding: 0;\n overflow-x: auto;\n\n figcaption {\n padding: calc(var(--spacing) * 0.5) 0;\n color: var(--muted-color);\n }\n}\n","/**\n * Typography\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// Add the correct font weight in Chrome, Edge, and Safari\nb,\nstrong {\n font-weight: bolder;\n}\n\n// Prevent `sub` and `sup` elements from affecting the line height in all browsers\nsub,\nsup {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\n\n// Pico\n// ––––––––––––––––––––\n\naddress,\nblockquote,\ndl,\nfigure,\nform,\nol,\np,\npre,\ntable,\nul {\n margin-top: 0;\n margin-bottom: var(--typography-spacing-vertical);\n color: var(--color);\n font-style: normal;\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n}\n\n// Links\n// 1. Remove the gray background on active links in IE 10\na,\n[role=\"link\"] {\n --color: var(--primary);\n --background-color: transparent;\n outline: none;\n background-color: var(--background-color); // 1\n color: var(--color);\n text-decoration: var(--text-decoration);\n\n @if $enable-transitions {\n transition: background-color var(--transition), color var(--transition),\n text-decoration var(--transition), box-shadow var(--transition);\n }\n\n &:is([aria-current], :hover, :active, :focus) {\n --color: var(--primary-hover);\n --text-decoration: underline;\n }\n\n &:focus {\n --background-color: var(--primary-focus);\n }\n\n @if $enable-classes {\n // Secondary\n &.secondary {\n --color: var(--secondary);\n\n &:is([aria-current], :hover, :active, :focus) {\n --color: var(--secondary-hover);\n }\n\n &:focus {\n --background-color: var(--secondary-focus);\n }\n }\n\n // Contrast\n &.contrast {\n --color: var(--contrast);\n\n &:is([aria-current], :hover, :active, :focus) {\n --color: var(--contrast-hover);\n }\n\n &:focus {\n --background-color: var(--contrast-focus);\n }\n }\n }\n}\n\n// Headings\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n margin-top: 0;\n margin-bottom: var(--typography-spacing-vertical);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: var(--font-size);\n font-family: var(--font-family);\n}\n\nh1 {\n --color: var(--h1-color);\n}\nh2 {\n --color: var(--h2-color);\n}\nh3 {\n --color: var(--h3-color);\n}\nh4 {\n --color: var(--h4-color);\n}\nh5 {\n --color: var(--h5-color);\n}\nh6 {\n --color: var(--h6-color);\n}\n\n// Margin-top for headings after a typography block\n:where(address, blockquote, dl, figure, form, ol, p, pre, table, ul) {\n ~ :is(h1, h2, h3, h4, h5, h6) {\n margin-top: var(--typography-spacing-vertical);\n }\n}\n\n// Heading group\n@if $enable-classes == false {\n hgroup {\n margin-bottom: var(--typography-spacing-vertical);\n\n > * {\n margin-bottom: 0;\n }\n\n > *:last-child {\n --color: var(--muted-color);\n --font-weight: unset;\n font-size: 1rem;\n font-family: unset;\n }\n }\n}\n\n@if $enable-classes {\n hgroup,\n .headings {\n margin-bottom: var(--typography-spacing-vertical);\n\n > * {\n margin-bottom: 0;\n }\n\n > *:last-child {\n --color: var(--muted-color);\n --font-weight: unset;\n font-size: 1rem;\n font-family: unset;\n }\n }\n}\n\n// Paragraphs\np {\n margin-bottom: var(--typography-spacing-vertical);\n}\n\n// Small\nsmall {\n font-size: var(--font-size);\n}\n\n// Lists\n:where(dl, ol, ul) {\n padding-right: 0;\n padding-left: var(--spacing);\n padding-inline-start: var(--spacing);\n padding-inline-end: 0;\n\n li {\n margin-bottom: calc(var(--typography-spacing-vertical) * 0.25);\n }\n}\n\n// Margin-top for nested lists\n// 1. Remove the margin on nested lists in Chrome, Edge, IE, and Safari\n:where(dl, ol, ul) {\n :is(dl, ol, ul) {\n margin: 0; // 1\n margin-top: calc(var(--typography-spacing-vertical) * 0.25);\n }\n}\n\nul li {\n list-style: square;\n}\n\n// Highlighted text\nmark {\n padding: 0.125rem 0.25rem;\n background-color: var(--mark-background-color);\n color: var(--mark-color);\n vertical-align: baseline;\n}\n\n// Blockquote\nblockquote {\n display: block;\n margin: var(--typography-spacing-vertical) 0;\n padding: var(--spacing);\n border-right: none;\n border-left: 0.25rem solid var(--blockquote-border-color);\n border-inline-start: 0.25rem solid var(--blockquote-border-color);\n border-inline-end: none;\n\n footer {\n margin-top: calc(var(--typography-spacing-vertical) * 0.5);\n color: var(--blockquote-footer-color);\n }\n}\n\n// Abbreviations\n// 1. Remove underline decoration in Chrome, Edge, IE, Opera, and Safari\nabbr[title] {\n border-bottom: 1px dotted;\n text-decoration: none; // 1\n cursor: help;\n}\n\n// Ins\nins {\n color: var(--ins-color);\n text-decoration: none;\n}\n\n// del\ndel {\n color: var(--del-color);\n}\n\n// selection\n::selection {\n background-color: var(--primary-focus);\n}\n","/**\n * Embedded content\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// Change the alignment on media elements in all browsers (opinionated)\n:where(audio, canvas, iframe, img, svg, video) {\n vertical-align: middle;\n}\n\n// Add the correct display in IE 9-\naudio,\nvideo {\n display: inline-block;\n}\n\n// Add the correct display in iOS 4-7\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n// Remove the border on iframes in all browsers (opinionated)\n:where(iframe) {\n border-style: none;\n}\n\n// 1. Remove the border on images inside links in IE 10.\n// 2. Responsive by default\nimg {\n max-width: 100%; // 2\n height: auto; // 2\n border-style: none; // 1\n}\n\n// Change the fill color to match the text color in all browsers (opinionated)\n:where(svg:not([fill])) {\n fill: currentColor;\n}\n\n// Hide the overflow in IE\nsvg:not(:root) {\n overflow: hidden;\n}\n","/**\n * Button\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// 1. Change the font styles in all browsers\n// 2. Remove the margin on controls in Safari\n// 3. Show the overflow in Edge\nbutton {\n margin: 0; // 2\n overflow: visible; // 3\n font-family: inherit; // 1\n text-transform: none; // 1\n}\n\n// Correct the inability to style buttons in iOS and Safari\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n// Pico\n// ––––––––––––––––––––\n\nbutton {\n display: block;\n width: 100%;\n margin-bottom: var(--spacing);\n}\n\n[role=\"button\"] {\n display: inline-block;\n text-decoration: none;\n}\n\nbutton,\ninput[type=\"submit\"],\ninput[type=\"button\"],\ninput[type=\"reset\"],\n[role=\"button\"] {\n --background-color: var(--primary);\n --border-color: var(--primary);\n --color: var(--primary-inverse);\n --box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));\n padding: var(--form-element-spacing-vertical)\n var(--form-element-spacing-horizontal);\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n font-size: 1rem;\n line-height: var(--line-height);\n text-align: center;\n cursor: pointer;\n\n @if $enable-transitions {\n transition: background-color var(--transition),\n border-color var(--transition), color var(--transition),\n box-shadow var(--transition);\n }\n\n &:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--primary-hover);\n --border-color: var(--primary-hover);\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));\n --color: var(--primary-inverse);\n }\n\n &:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--primary-focus);\n }\n}\n\n// .secondary, .contrast & .outline\n@if $enable-classes {\n\n // Secondary\n :is(button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"]).secondary,\n input[type=\"reset\"] {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n cursor: pointer;\n\n &:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n --color: var(--secondary-inverse);\n }\n\n &:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--secondary-focus);\n }\n }\n\n // Contrast\n :is(button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"]).contrast {\n --background-color: var(--contrast);\n --border-color: var(--contrast);\n --color: var(--contrast-inverse);\n\n &:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--contrast-hover);\n --border-color: var(--contrast-hover);\n --color: var(--contrast-inverse);\n }\n\n &:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--contrast-focus);\n }\n }\n\n // Outline (primary)\n :is(button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"]).outline,\n input[type=\"reset\"].outline {\n --background-color: transparent;\n --color: var(--primary);\n\n &:is([aria-current], :hover, :active, :focus) {\n --background-color: transparent;\n --color: var(--primary-hover);\n }\n }\n\n // Outline (secondary)\n :is(button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"]).outline.secondary,\n input[type=\"reset\"].outline {\n --color: var(--secondary);\n\n &:is([aria-current], :hover, :active, :focus) {\n --color: var(--secondary-hover);\n }\n }\n\n // Outline (contrast)\n :is(button, input[type=\"submit\"], input[type=\"button\"], [role=\"button\"]).outline.contrast {\n --color: var(--contrast);\n\n &:is([aria-current], :hover, :active, :focus) {\n --color: var(--contrast-hover);\n }\n }\n} \n@else {\n // Secondary button without .class\n input[type=\"reset\"] {\n --background-color: var(--secondary);\n --border-color: var(--secondary);\n --color: var(--secondary-inverse);\n cursor: pointer;\n\n &:is([aria-current], :hover, :active, :focus) {\n --background-color: var(--secondary-hover);\n --border-color: var(--secondary-hover);\n }\n\n &:focus {\n --box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),\n 0 0 0 var(--outline-width) var(--secondary-focus);\n }\n }\n}\n\n// Button [disabled]\n// Links without href are disabled by default\n:where(button, [type=\"submit\"], [type=\"button\"], [type=\"reset\"], [role=\"button\"])[disabled],\n:where(fieldset[disabled]) :is(button, [type=\"submit\"], [type=\"button\"], [type=\"reset\"], [role=\"button\"]),\na[role=\"button\"]:not([href]) {\n opacity: 0.5;\n pointer-events: none;\n}\n","/**\n * Form elements\n */\n\n// Reboot based on :\n// - normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css\n// - sanitize.css v13.0.0 | CC0 1.0 Universal | github.com/csstools/sanitize.css\n// ––––––––––––––––––––\n\n// 1. Change the font styles in all browsers\n// 2. Remove the margin in Firefox and Safari\ninput,\noptgroup,\nselect,\ntextarea {\n margin: 0; // 2\n font-size: 1rem; // 1\n line-height: var(--line-height); // 1\n font-family: inherit; // 1\n letter-spacing: inherit; // 2\n}\n\n// Show the overflow in IE.\ninput {\n overflow: visible;\n}\n\n// Remove the inheritance of text transform in Edge, Firefox, and IE\nselect {\n text-transform: none;\n}\n\n// 1. Correct the text wrapping in Edge and IE\n// 2. Correct the color inheritance from `fieldset` elements in IE\n// 3. Remove the padding so developers are not caught out when they zero out\n// `fieldset` elements in all browsers\nlegend {\n max-width: 100%; // 1\n padding: 0; // 3\n color: inherit; // 2\n white-space: normal; // 1\n}\n\n// 1. Remove the default vertical scrollbar in IE\ntextarea {\n overflow: auto; // 1\n}\n\n// Remove the padding in IE 10\n[type=\"checkbox\"],\n[type=\"radio\"] {\n padding: 0;\n}\n\n// Correct the cursor style of increment and decrement buttons in Safari\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n// 1. Correct the odd appearance in Chrome and Safari\n// 2. Correct the outline style in Safari\n[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n outline-offset: -2px; // 2\n}\n\n// Remove the inner padding in Chrome and Safari on macOS\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n// 1. Correct the inability to style clickable types in iOS and Safari\n// 2. Change font properties to `inherit` in Safari\n::-webkit-file-upload-button {\n -webkit-appearance: button; // 1\n font: inherit; // 2\n}\n\n// Remove the inner border and padding of focus outlines in Firefox\n::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\n// Remove the focus outline in Firefox\n:-moz-focusring {\n outline: none;\n}\n\n// Remove the additional :invalid styles in Firefox\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n// Change the inconsistent appearance in IE (opinionated)\n::-ms-expand {\n display: none;\n}\n\n// Remove the border and padding in all browsers (opinionated)\n[type=\"file\"],\n[type=\"range\"] {\n padding: 0;\n border-width: 0;\n}\n\n// Pico\n// ––––––––––––––––––––\n\n// Force height for alternatives input types\ninput:not([type=\"checkbox\"], [type=\"radio\"], [type=\"range\"]) {\n height: calc(\n (1rem * var(--line-height)) + (var(--form-element-spacing-vertical) * 2) +\n (var(--border-width) * 2)\n );\n}\n\n// Fieldset\nfieldset {\n margin: 0;\n margin-bottom: var(--spacing);\n padding: 0;\n border: 0;\n}\n\n// Label & legend\nlabel,\nfieldset legend {\n display: block;\n margin-bottom: calc(var(--spacing) * 0.25);\n font-weight: var(--form-label-font-weight, var(--font-weight));\n}\n\n// Blocks, 100%\ninput:not([type=\"checkbox\"], [type=\"radio\"]),\nselect,\ntextarea {\n width: 100%;\n}\n\n// Reset appearance (Not Checkboxes, Radios, Range and File)\ninput:not([type=\"checkbox\"], [type=\"radio\"], [type=\"range\"], [type=\"file\"]),\nselect,\ntextarea {\n appearance: none;\n padding: var(--form-element-spacing-vertical)\n var(--form-element-spacing-horizontal);\n}\n\n// Commons styles\ninput,\nselect,\ntextarea {\n --background-color: var(--form-element-background-color);\n --border-color: var(--form-element-border-color);\n --color: var(--form-element-color);\n --box-shadow: none;\n border: var(--border-width) solid var(--border-color);\n border-radius: var(--border-radius);\n outline: none;\n background-color: var(--background-color);\n box-shadow: var(--box-shadow);\n color: var(--color);\n font-weight: var(--font-weight);\n\n @if $enable-transitions {\n transition: background-color var(--transition),\n border-color var(--transition), color var(--transition),\n box-shadow var(--transition);\n }\n}\n\n// Active & Focus\ninput:not([type=\"submit\"], [type=\"button\"], [type=\"reset\"], [type=\"checkbox\"], [type=\"radio\"], [readonly]),\n:where(select, textarea) {\n &:is(:active, :focus) {\n --background-color: var(--form-element-active-background-color);\n }\n}\n\n// Active & Focus\ninput:not([type=\"submit\"], [type=\"button\"], [type=\"reset\"], [role=\"switch\"], [readonly]),\n:where(select, textarea) {\n &:is(:active, :focus) {\n --border-color: var(--form-element-active-border-color);\n }\n}\n\n// Focus\ninput:not([type=\"submit\"], [type=\"button\"], [type=\"reset\"], [type=\"range\"], [type=\"file\"], [readonly]),\nselect,\ntextarea {\n &:focus {\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color);\n }\n}\n\n// Disabled\ninput:not([type=\"submit\"], [type=\"button\"], [type=\"reset\"])[disabled],\nselect[disabled],\ntextarea[disabled],\n:where(fieldset[disabled]) :is(input:not([type=\"submit\"], [type=\"button\"], [type=\"reset\"]), select, textarea) {\n --background-color: var(--form-element-disabled-background-color);\n --border-color: var(--form-element-disabled-border-color);\n opacity: var(--form-element-disabled-opacity);\n pointer-events: none;\n}\n\n// Aria-invalid\n:where(input, select, textarea) {\n &:not([type=\"checkbox\"], [type=\"radio\"], [type=\"date\"], [type=\"datetime-local\"], [type=\"month\"], [type=\"time\"], [type=\"week\"]) {\n &[aria-invalid] {\n @if $enable-important {\n padding-right: calc(\n var(--form-element-spacing-horizontal) + 1.5rem\n ) !important;\n padding-left: var(--form-element-spacing-horizontal);\n padding-inline-start: var(--form-element-spacing-horizontal) !important;\n padding-inline-end: calc(\n var(--form-element-spacing-horizontal) + 1.5rem\n ) !important;\n }\n @else {\n padding-right: calc(var(--form-element-spacing-horizontal) + 1.5rem);\n padding-left: var(--form-element-spacing-horizontal);\n padding-inline-start: var(--form-element-spacing-horizontal);\n padding-inline-end: calc(var(--form-element-spacing-horizontal) + 1.5rem);\n }\n background-position: center right 0.75rem;\n background-size: 1rem auto;\n background-repeat: no-repeat;\n }\n\n &[aria-invalid=\"false\"] {\n background-image: var(--icon-valid);\n }\n\n &[aria-invalid=\"true\"] {\n background-image: var(--icon-invalid);\n }\n }\n\n &[aria-invalid=\"false\"] {\n --border-color: var(--form-element-valid-border-color);\n\n &:is(:active, :focus) {\n @if $enable-important {\n --border-color: var(--form-element-valid-active-border-color) !important;\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important;\n }\n @else {\n --border-color: var(--form-element-valid-active-border-color);\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color);\n }\n }\n }\n\n &[aria-invalid=\"true\"] {\n --border-color: var(--form-element-invalid-border-color);\n\n &:is(:active, :focus) {\n @if $enable-important {\n --border-color: var(--form-element-invalid-active-border-color) !important;\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important;\n }\n @else {\n --border-color: var(--form-element-invalid-active-border-color);\n --box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color);\n }\n }\n }\n}\n\n[dir=\"rtl\"] {\n :where(input, select, textarea) {\n &:not([type=\"checkbox\"], [type=\"radio\"]) {\n &:is([aria-invalid], [aria-invalid=\"true\"], [aria-invalid=\"false\"] ){\n background-position: center left 0.75rem;\n }\n }\n }\n}\n\n// Placeholder\ninput::placeholder,\ninput::-webkit-input-placeholder,\ntextarea::placeholder,\ntextarea::-webkit-input-placeholder,\nselect:invalid {\n color: var(--form-element-placeholder-color);\n opacity: 1;\n}\n\n// Margin bottom (Not Checkboxes and Radios)\ninput:not([type=\"checkbox\"], [type=\"radio\"]),\nselect,\ntextarea {\n margin-bottom: var(--spacing);\n}\n\n// Select\nselect {\n // Unstyle the caret on `\n summary {\n margin-bottom: 0;\n\n &:not([role]) {\n height: calc(\n 1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 +\n var(--border-width) * 2\n );\n padding: var(--form-element-spacing-vertical)\n var(--form-element-spacing-horizontal);\n border: var(--border-width) solid var(--form-element-border-color);\n border-radius: var(--border-radius);\n background-color: var(--form-element-background-color);\n color: var(--form-element-placeholder-color);\n line-height: inherit;\n cursor: pointer;\n\n @if $enable-transitions {\n transition: background-color var(--transition),\n border-color var(--transition), color var(--transition),\n box-shadow var(--transition);\n }\n\n &:active,\n &:focus {\n border-color: var(--form-element-active-border-color);\n background-color: var(--form-element-active-background-color);\n }\n\n &:focus {\n box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color);\n }\n }\n }\n\n // Close for details[role=\"list\"]\n &[open] summary {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n\n &::before {\n display: block;\n z-index: 1;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background: none;\n content: \"\";\n cursor: default;\n }\n }\n}\n\n// All Dropdowns inside

- - - -