From f5c5af6bbd2dc9c49c5891882006b4529901c144 Mon Sep 17 00:00:00 2001
From: masudaso
Date: Tue, 5 May 2026 00:20:39 +0900
Subject: [PATCH 1/2] refactor: rebrand as Ethereum-native holding, simplify to
Vercel Django template
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Repositions the site around three pillars (Ethereum holdings / Software /
Culture) per the company charter, adds bilingual JA/EN coverage, and
collapses the Cookiecutter-Django bootstrap to the minimal layout that
Vercel's Python Runtime auto-detects from manage.py.
What changed:
- Information architecture: new Vision, Holdings, Software, Business,
legal/Privacy, legal/Cookies pages (replacing Services / About).
- Software page features GratefulMoments
(https://github.com/masuda-so/GratefulMoments) as the flagship product.
- Company page now lists representative member, founding date, capital,
corporate number 4011103016903, and registered office.
- Real contact email so.masuda.2003@ether-llc.com everywhere; segmented
contact channels (partnerships, software, press, company info).
- OGP, Organization JSON-LD, /robots.txt, /sitemap.xml.
- Full English mirror under /en/ with hreflang alternates and a language
switcher in the header.
Architecture cleanup:
- Drop scripts/export_static_site.py and scripts/static_export_urls.py
along with vercel.json — Vercel auto-detects manage.py.
- Drop allauth, users app, anymail, crispy-forms, redis, postgres,
whitenoise, mypy, sphinx, pytest from dependencies.
- Replace config/settings/{base,local,production,test}.py with a single
config/settings.py modelled on Vercel's Django template.
- Remove Docker compose, devcontainer, justfile, ReadTheDocs, locale po
files, and tests/ scaffolding.
---
.devcontainer/bashrc.override.sh | 20 -
.devcontainer/devcontainer.json | 68 -
.dockerignore | 12 -
.gitignore | 8 +
.readthedocs.yml | 21 -
README.md | 93 +-
compose/local/django/Dockerfile | 59 -
compose/local/django/start | 9 -
compose/local/docs/Dockerfile | 65 -
compose/local/docs/start | 7 -
compose/production/django/Dockerfile | 81 -
compose/production/django/entrypoint | 17 -
compose/production/django/start | 10 -
compose/production/nginx/Dockerfile | 2 -
compose/production/nginx/default.conf | 7 -
compose/production/postgres/Dockerfile | 6 -
.../maintenance/_sourced/constants.sh | 5 -
.../maintenance/_sourced/countdown.sh | 12 -
.../postgres/maintenance/_sourced/messages.sh | 41 -
.../postgres/maintenance/_sourced/yes_no.sh | 16 -
.../production/postgres/maintenance/backup | 38 -
.../production/postgres/maintenance/backups | 22 -
.../production/postgres/maintenance/restore | 55 -
.../production/postgres/maintenance/rmbackup | 36 -
compose/production/traefik/Dockerfile | 5 -
compose/production/traefik/traefik.yml | 73 -
config/settings.py | 91 +
config/settings/__init__.py | 0
config/settings/base.py | 283 ---
config/settings/local.py | 71 -
config/settings/production.py | 154 --
config/settings/test.py | 38 -
config/urls.py | 152 +-
config/wsgi.py | 25 +-
docker-compose.docs.yml | 17 -
docker-compose.local.yml | 42 -
docker-compose.production.yml | 62 -
docs/Makefile | 29 -
docs/__init__.py | 1 -
docs/conf.py | 64 -
docs/howto.rst | 38 -
docs/index.rst | 23 -
docs/make.bat | 46 -
docs/users.rst | 15 -
ether/conftest.py | 14 -
ether/contrib/__init__.py | 5 -
ether/contrib/sites/__init__.py | 5 -
.../contrib/sites/migrations/0001_initial.py | 43 -
.../migrations/0002_alter_domain_unique.py | 21 -
.../0003_set_site_domain_and_name.py | 63 -
.../0004_alter_options_ordering_domain.py | 21 -
ether/contrib/sites/migrations/__init__.py | 5 -
ether/static/css/project.css | 481 ++++-
ether/templates/base.html | 275 ++-
ether/templates/pages/about.html | 1 -
ether/templates/pages/business.html | 198 ++
ether/templates/pages/company.html | 88 +-
ether/templates/pages/contact.html | 57 +-
ether/templates/pages/en/business.html | 203 ++
ether/templates/pages/en/company.html | 138 ++
ether/templates/pages/en/contact.html | 106 ++
ether/templates/pages/en/holdings.html | 139 ++
ether/templates/pages/en/home.html | 192 ++
ether/templates/pages/en/legal/cookies.html | 79 +
ether/templates/pages/en/legal/privacy.html | 107 ++
ether/templates/pages/en/software.html | 211 +++
ether/templates/pages/en/vision.html | 111 ++
ether/templates/pages/holdings.html | 139 ++
ether/templates/pages/home.html | 127 +-
ether/templates/pages/legal/cookies.html | 81 +
ether/templates/pages/legal/privacy.html | 102 +
ether/templates/pages/services.html | 75 -
ether/templates/pages/software.html | 210 +++
ether/templates/pages/vision.html | 110 ++
ether/templates/robots.txt | 7 +
ether/templates/sitemap.xml | 67 +
ether/users/__init__.py | 0
ether/users/adapters.py | 48 -
ether/users/admin.py | 50 -
ether/users/apps.py | 12 -
ether/users/context_processors.py | 8 -
ether/users/forms.py | 44 -
ether/users/managers.py | 42 -
ether/users/migrations/0001_initial.py | 112 --
ether/users/migrations/__init__.py | 0
ether/users/models.py | 38 -
ether/users/tests/__init__.py | 0
ether/users/tests/factories.py | 35 -
ether/users/tests/test_admin.py | 65 -
ether/users/tests/test_forms.py | 35 -
ether/users/tests/test_managers.py | 55 -
ether/users/tests/test_models.py | 5 -
ether/users/tests/test_urls.py | 19 -
ether/users/tests/test_views.py | 101 -
ether/users/urls.py | 12 -
ether/users/views.py | 46 -
justfile | 38 -
locale/README.md | 32 -
locale/en_US/LC_MESSAGES/django.po | 12 -
locale/fr_FR/LC_MESSAGES/django.po | 335 ----
locale/pt_BR/LC_MESSAGES/django.po | 315 ----
manage.py | 13 +-
merge_production_dotenvs_in_dotenv.py | 25 -
pyproject.toml | 114 +-
scripts/__init__.py | 0
scripts/export_static_site.py | 173 --
scripts/static_export_urls.py | 28 -
tests/__init__.py | 0
...test_merge_production_dotenvs_in_dotenv.py | 34 -
tests/test_public_pages.py | 52 -
uv.lock | 1634 ++---------------
vercel.json | 7 -
112 files changed, 3534 insertions(+), 5425 deletions(-)
delete mode 100644 .devcontainer/bashrc.override.sh
delete mode 100644 .devcontainer/devcontainer.json
delete mode 100644 .dockerignore
delete mode 100644 .readthedocs.yml
delete mode 100644 compose/local/django/Dockerfile
delete mode 100644 compose/local/django/start
delete mode 100644 compose/local/docs/Dockerfile
delete mode 100644 compose/local/docs/start
delete mode 100644 compose/production/django/Dockerfile
delete mode 100644 compose/production/django/entrypoint
delete mode 100644 compose/production/django/start
delete mode 100644 compose/production/nginx/Dockerfile
delete mode 100644 compose/production/nginx/default.conf
delete mode 100644 compose/production/postgres/Dockerfile
delete mode 100644 compose/production/postgres/maintenance/_sourced/constants.sh
delete mode 100644 compose/production/postgres/maintenance/_sourced/countdown.sh
delete mode 100644 compose/production/postgres/maintenance/_sourced/messages.sh
delete mode 100644 compose/production/postgres/maintenance/_sourced/yes_no.sh
delete mode 100644 compose/production/postgres/maintenance/backup
delete mode 100644 compose/production/postgres/maintenance/backups
delete mode 100644 compose/production/postgres/maintenance/restore
delete mode 100644 compose/production/postgres/maintenance/rmbackup
delete mode 100644 compose/production/traefik/Dockerfile
delete mode 100644 compose/production/traefik/traefik.yml
create mode 100644 config/settings.py
delete mode 100644 config/settings/__init__.py
delete mode 100644 config/settings/base.py
delete mode 100644 config/settings/local.py
delete mode 100644 config/settings/production.py
delete mode 100644 config/settings/test.py
delete mode 100644 docker-compose.docs.yml
delete mode 100644 docker-compose.local.yml
delete mode 100644 docker-compose.production.yml
delete mode 100644 docs/Makefile
delete mode 100644 docs/__init__.py
delete mode 100644 docs/conf.py
delete mode 100644 docs/howto.rst
delete mode 100644 docs/index.rst
delete mode 100644 docs/make.bat
delete mode 100644 docs/users.rst
delete mode 100644 ether/conftest.py
delete mode 100644 ether/contrib/__init__.py
delete mode 100644 ether/contrib/sites/__init__.py
delete mode 100644 ether/contrib/sites/migrations/0001_initial.py
delete mode 100644 ether/contrib/sites/migrations/0002_alter_domain_unique.py
delete mode 100644 ether/contrib/sites/migrations/0003_set_site_domain_and_name.py
delete mode 100644 ether/contrib/sites/migrations/0004_alter_options_ordering_domain.py
delete mode 100644 ether/contrib/sites/migrations/__init__.py
delete mode 100644 ether/templates/pages/about.html
create mode 100644 ether/templates/pages/business.html
create mode 100644 ether/templates/pages/en/business.html
create mode 100644 ether/templates/pages/en/company.html
create mode 100644 ether/templates/pages/en/contact.html
create mode 100644 ether/templates/pages/en/holdings.html
create mode 100644 ether/templates/pages/en/home.html
create mode 100644 ether/templates/pages/en/legal/cookies.html
create mode 100644 ether/templates/pages/en/legal/privacy.html
create mode 100644 ether/templates/pages/en/software.html
create mode 100644 ether/templates/pages/en/vision.html
create mode 100644 ether/templates/pages/holdings.html
create mode 100644 ether/templates/pages/legal/cookies.html
create mode 100644 ether/templates/pages/legal/privacy.html
delete mode 100644 ether/templates/pages/services.html
create mode 100644 ether/templates/pages/software.html
create mode 100644 ether/templates/pages/vision.html
create mode 100644 ether/templates/robots.txt
create mode 100644 ether/templates/sitemap.xml
delete mode 100644 ether/users/__init__.py
delete mode 100644 ether/users/adapters.py
delete mode 100644 ether/users/admin.py
delete mode 100644 ether/users/apps.py
delete mode 100644 ether/users/context_processors.py
delete mode 100644 ether/users/forms.py
delete mode 100644 ether/users/managers.py
delete mode 100644 ether/users/migrations/0001_initial.py
delete mode 100644 ether/users/migrations/__init__.py
delete mode 100644 ether/users/models.py
delete mode 100644 ether/users/tests/__init__.py
delete mode 100644 ether/users/tests/factories.py
delete mode 100644 ether/users/tests/test_admin.py
delete mode 100644 ether/users/tests/test_forms.py
delete mode 100644 ether/users/tests/test_managers.py
delete mode 100644 ether/users/tests/test_models.py
delete mode 100644 ether/users/tests/test_urls.py
delete mode 100644 ether/users/tests/test_views.py
delete mode 100644 ether/users/urls.py
delete mode 100644 ether/users/views.py
delete mode 100644 justfile
delete mode 100644 locale/README.md
delete mode 100644 locale/en_US/LC_MESSAGES/django.po
delete mode 100644 locale/fr_FR/LC_MESSAGES/django.po
delete mode 100644 locale/pt_BR/LC_MESSAGES/django.po
delete mode 100644 merge_production_dotenvs_in_dotenv.py
delete mode 100644 scripts/__init__.py
delete mode 100644 scripts/export_static_site.py
delete mode 100644 scripts/static_export_urls.py
delete mode 100644 tests/__init__.py
delete mode 100644 tests/test_merge_production_dotenvs_in_dotenv.py
delete mode 100644 tests/test_public_pages.py
delete mode 100644 vercel.json
diff --git a/.devcontainer/bashrc.override.sh b/.devcontainer/bashrc.override.sh
deleted file mode 100644
index bedddf6..0000000
--- a/.devcontainer/bashrc.override.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-
-#
-# .bashrc.override.sh
-#
-
-# persistent bash history
-HISTFILE=~/.bash_history
-PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
-
-# set some django env vars
-source /entrypoint
-
-# restore default shell options
-set +o errexit
-set +o pipefail
-set +o nounset
-
-# start ssh-agent
-# https://code.visualstudio.com/docs/remote/troubleshooting
-eval "$(ssh-agent -s)"
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index e79d44d..0000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,68 +0,0 @@
-// For format details, see https://containers.dev/implementors/json_reference/
-{
- "name": "ether_dev",
- "dockerComposeFile": [
- "../docker-compose.local.yml"
- ],
- "init": true,
- "mounts": [
- {
- "source": "./.devcontainer/bash_history",
- "target": "/home/dev-user/.bash_history",
- "type": "bind"
- },
- {
- "source": "~/.ssh",
- "target": "/home/dev-user/.ssh",
- "type": "bind"
- }
- ],
- // Tells devcontainer.json supporting services / tools whether they should run
- // /bin/sh -c "while sleep 1000; do :; done" when starting the container instead of the container’s default command
- "overrideCommand": false,
- "service": "django",
- // "remoteEnv": {"PATH": "/home/dev-user/.local/bin:${containerEnv:PATH}"},
- "remoteUser": "dev-user",
- "workspaceFolder": "/app",
- // Set *default* container specific settings.json values on container create.
- "customizations": {
- "vscode": {
- "settings": {
- "editor.formatOnSave": true,
- "[python]": {
- "analysis.autoImportCompletions": true,
- "analysis.typeCheckingMode": "basic",
- "defaultInterpreterPath": "/usr/local/bin/python",
- "editor.codeActionsOnSave": {
- "source.organizeImports": "always"
- },
- "editor.defaultFormatter": "charliermarsh.ruff",
- "languageServer": "Pylance",
- "linting.enabled": true,
- "linting.mypyEnabled": true,
- "linting.mypyPath": "/usr/local/bin/mypy",
- }
- },
- // https://code.visualstudio.com/docs/remote/devcontainerjson-reference#_vs-code-specific-properties
- // Add the IDs of extensions you want installed when the container is created.
- "extensions": [
- "davidanson.vscode-markdownlint",
- "mrmlnc.vscode-duplicate",
- "visualstudioexptteam.vscodeintellicode",
- "visualstudioexptteam.intellicode-api-usage-examples",
- // python
- "ms-python.python",
- "ms-python.vscode-pylance",
- "charliermarsh.ruff",
- // django
- "batisteo.vscode-django"
- ]
- }
- },
- // Uncomment the next line if you want start specific services in your Docker Compose config.
- // "runServices": [],
- // Uncomment the next line if you want to keep your containers running after VS Code shuts down.
- // "shutdownAction": "none",
- // Uncomment the next line to run commands after the container is created.
- "postCreateCommand": "cat .devcontainer/bashrc.override.sh >> ~/.bashrc"
-}
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index a602416..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,12 +0,0 @@
-.editorconfig
-.gitattributes
-.github
-.gitignore
-.gitlab-ci.yml
-.idea
-.pre-commit-config.yaml
-.readthedocs.yml
-.travis.yml
-venv
-.git
-.envs/
diff --git a/.gitignore b/.gitignore
index 243cbe5..b108e9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -277,4 +277,12 @@ ether/media/
.envs/*
!.envs/.local/
+# Django
+db.sqlite3
+db.sqlite3-journal
+staticfiles/
+
+# Vercel
+.vercel
+
/.claude/worktrees
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644
index c237a0b..0000000
--- a/.readthedocs.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# Read the Docs configuration file
-# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
-
-version: 2
-
-build:
- os: ubuntu-24.04
- tools:
- python: "3.13"
- jobs:
- pre_create_environment:
- - asdf plugin add uv
- - asdf install uv latest
- - asdf global uv latest
- create_environment:
- - uv venv "${READTHEDOCS_VIRTUALENV_PATH}"
- install:
- - UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" uv sync --frozen --no-dev --only-group docs
-
-sphinx:
- configuration: docs/conf.py
diff --git a/README.md b/README.md
index 44e7f8c..f6da51d 100644
--- a/README.md
+++ b/README.md
@@ -1,61 +1,66 @@
-# Ether Organization Website
+# Ether合同会社 / Ether LLC — Website
-Organization website for Ether LLC
+[https://ether-llc.com](https://ether-llc.com) で公開している、Ether合同会社の組織サイトです。
-[](https://github.com/cookiecutter/cookiecutter-django/)
-[](https://github.com/astral-sh/ruff)
+## スタック
-## Settings
+- Python 3.12+ / Django 6
+- uv (依存管理)
+- Vercel Python Runtime (`manage.py` 自動検出によるデプロイ)
+- Bootstrap 5 + 自前 CSS (Material 3 風)
-Moved to [settings](https://cookiecutter-django.readthedocs.io/en/latest/1-getting-started/settings.html).
+## ローカル開発
-## Basic Commands
+```bash
+uv sync
+uv run python manage.py migrate
+uv run python manage.py runserver
+```
-### Setting Up Your Users
+`http://localhost:8000/` にアクセス。
-- To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go.
+## デプロイ (Vercel)
-- To create a **superuser account**, use this command:
+`vercel.json` は不要です。Vercel が `manage.py` を自動検出し、WSGI と静的ファイルを構成します。
- uv run python manage.py createsuperuser
+初回のみ、Django 用の Secret Key を環境変数として登録します:
-For convenience, you can keep your normal user logged in on Chrome and your superuser logged in on Firefox (or similar), so that you can see how the site behaves for both kinds of users.
+```bash
+uv run python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())' | vercel env add -y DJANGO_SECRET_KEY prod
+```
-### Type checks
+以降は `git push` で `main` ブランチがマージされるたびに自動デプロイされます。
-Running type checks with mypy:
+## ディレクトリ構成
- uv run mypy ether
+```
+config/ — Django プロジェクト (settings.py, urls.py, wsgi.py)
+ether/
+ templates/ — JA テンプレート + en/ サブディレクトリ
+ static/ — CSS/JS/画像
+manage.py
+pyproject.toml — 依存定義 (django のみ、dev に djlint と ruff)
+```
-### Test coverage
+## URL 構成
-To run the tests, check your test coverage, and generate an HTML coverage report:
+| パス | 内容 |
+|---|---|
+| `/`, `/en/` | ホーム |
+| `/vision/`, `/en/vision/` | 会社のビジョン |
+| `/holdings/`, `/en/holdings/` | Ethereum 保有方針 |
+| `/software/`, `/en/software/` | プロダクト紹介 (GratefulMoments) |
+| `/business/`, `/en/business/` | 定款の事業目的 23 項目 |
+| `/company/`, `/en/company/` | 法定の会社情報 |
+| `/contact/`, `/en/contact/` | お問い合わせ窓口 |
+| `/legal/privacy/`, `/legal/cookies/` | プライバシー/Cookieポリシー |
+| `/robots.txt`, `/sitemap.xml` | クローラ向け |
+| `/admin/` | Django 管理画面 |
- uv run coverage run -m pytest
- uv run coverage html
- uv run open htmlcov/index.html
+## Lint / Format
-#### Running tests with pytest
-
- uv run pytest
-
-### Live reloading and Sass CSS compilation
-
-Moved to [Live reloading and SASS compilation](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally.html#using-webpack-or-gulp).
-
-### Email Server
-
-In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server [Mailpit](https://github.com/axllent/mailpit) with a web interface is available as docker container.
-
-Container mailpit will start automatically when you will run all docker containers.
-Please check [cookiecutter-django Docker documentation](https://cookiecutter-django.readthedocs.io/en/latest/2-local-development/developing-locally-docker.html) for more details how to start all containers.
-
-With Mailpit running, to view messages that are sent by your application, open your browser and go to `http://127.0.0.1:8025`
-
-## Deployment
-
-The following details how to deploy this application.
-
-### Docker
-
-See detailed [cookiecutter-django Docker documentation](https://cookiecutter-django.readthedocs.io/en/latest/3-deployment/deployment-with-docker.html).
+```bash
+uv run ruff check . # Python
+uv run djlint ether/templates/ # Django テンプレート
+uv run djlint ether/templates/ --reformat
+```
diff --git a/compose/local/django/Dockerfile b/compose/local/django/Dockerfile
deleted file mode 100644
index b714f62..0000000
--- a/compose/local/django/Dockerfile
+++ /dev/null
@@ -1,59 +0,0 @@
-# define an alias for the specific python version used in this file.
-FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS python
-
-# Python build stage
-FROM python AS python-build-stage
-
-ARG APP_HOME=/app
-
-WORKDIR ${APP_HOME}
-
-# we need to move the virtualenv outside of the $APP_HOME directory because it will be overriden by the docker compose mount
-ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=0
-
-# Install apt packages
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev \
- gettext \
- wait-for-it
-
-# Requirements are installed here to ensure they will be cached.
-RUN --mount=type=cache,target=/root/.cache/uv \
- --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
- --mount=type=bind,source=uv.lock,target=uv.lock:rw \
- uv sync --no-install-project
-
-COPY . ${APP_HOME}
-
-RUN --mount=type=cache,target=/root/.cache/uv \
- --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
- --mount=type=bind,source=uv.lock,target=uv.lock:rw \
- uv sync
-
-# devcontainer dependencies and utils
-RUN apt-get update && apt-get install --no-install-recommends -y \
- sudo git bash-completion nano ssh
-
-# Create devcontainer user and add it to sudoers
-RUN groupadd --gid 1000 dev-user \
- && useradd --uid 1000 --gid dev-user --shell /bin/bash --create-home dev-user \
- && echo dev-user ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/dev-user \
- && chmod 0440 /etc/sudoers.d/dev-user
-
-ENV PATH="/${APP_HOME}/.venv/bin:$PATH"
-ENV PYTHONPATH="${APP_HOME}/.venv/lib/python3.13/site-packages:$PYTHONPATH"
-
-COPY ./compose/production/django/entrypoint /entrypoint
-RUN sed -i 's/\r$//g' /entrypoint
-RUN chmod +x /entrypoint
-
-COPY ./compose/local/django/start /start
-RUN sed -i 's/\r$//g' /start
-RUN chmod +x /start
-
-
-
-ENTRYPOINT ["/entrypoint"]
diff --git a/compose/local/django/start b/compose/local/django/start
deleted file mode 100644
index ba96db4..0000000
--- a/compose/local/django/start
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-python manage.py migrate
-exec python manage.py runserver_plus 0.0.0.0:8000
diff --git a/compose/local/docs/Dockerfile b/compose/local/docs/Dockerfile
deleted file mode 100644
index 82dbeb1..0000000
--- a/compose/local/docs/Dockerfile
+++ /dev/null
@@ -1,65 +0,0 @@
-# define an alias for the specific python version used in this file.
-FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS python
-
-
-# Python build stage
-FROM python AS python-build-stage
-
-ARG APP_HOME=/app
-
-WORKDIR ${APP_HOME}
-
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# Requirements are installed here to ensure they will be cached.
-RUN --mount=type=cache,target=/root/.cache/uv \
- --mount=type=bind,source=uv.lock,target=uv.lock \
- --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
- uv sync --no-install-project
-
-COPY . ${APP_HOME}
-
-RUN --mount=type=cache,target=/root/.cache/uv \
- uv sync
-
-
-# Python 'run' stage
-FROM python AS python-run-stage
-
-ARG BUILD_ENVIRONMENT
-ENV PYTHONUNBUFFERED=1
-ENV PYTHONDONTWRITEBYTECODE=1
-
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # To run the Makefile
- make \
- # psycopg dependencies
- libpq-dev \
- # Translations dependencies
- gettext \
- # Uncomment below lines to enable Sphinx output to latex and pdf
- # texlive-latex-recommended \
- # texlive-fonts-recommended \
- # texlive-latex-extra \
- # latexmk \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# copy python dependency wheels from python-build-stage
-COPY --from=python-build-stage --chown=app:app /app /app
-
-COPY ./compose/local/docs/start /start-docs
-RUN sed -i 's/\r$//g' /start-docs
-RUN chmod +x /start-docs
-
-ENV PATH="/app/.venv/bin:$PATH"
-
-WORKDIR /docs
diff --git a/compose/local/docs/start b/compose/local/docs/start
deleted file mode 100644
index 96a94f5..0000000
--- a/compose/local/docs/start
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-exec make livehtml
diff --git a/compose/production/django/Dockerfile b/compose/production/django/Dockerfile
deleted file mode 100644
index 7768727..0000000
--- a/compose/production/django/Dockerfile
+++ /dev/null
@@ -1,81 +0,0 @@
-
-# define an alias for the specific python version used in this file.
-FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS python-build-stage
-
-ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=0
-
-ARG APP_HOME=/app
-
-WORKDIR ${APP_HOME}
-
-# Install apt packages
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev
-
-
-# Requirements are installed here to ensure they will be cached.
-RUN --mount=type=cache,target=/root/.cache/uv \
- --mount=type=bind,source=uv.lock,target=uv.lock \
- --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
- uv sync --locked --no-install-project --no-dev
-COPY . ${APP_HOME}
-
-RUN --mount=type=cache,target=/root/.cache/uv \
- --mount=type=bind,source=uv.lock,target=uv.lock \
- --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
- uv sync --locked --no-dev
-
-# Python 'run' stage
-FROM python:3.13.13-slim-bookworm AS python-run-stage
-
-ARG APP_HOME=/app
-
-WORKDIR ${APP_HOME}
-
-RUN addgroup --system django \
- && adduser --system --ingroup django django
-
-
-# Install required system dependencies
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # psycopg dependencies
- libpq-dev \
- # Translations dependencies
- gettext \
- # entrypoint
- wait-for-it \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-
-COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint
-RUN sed -i 's/\r$//g' /entrypoint
-RUN chmod +x /entrypoint
-
-
-COPY --chown=django:django ./compose/production/django/start /start
-RUN sed -i 's/\r$//g' /start
-RUN chmod +x /start
-
-# Copy the application from the builder
-COPY --from=python-build-stage --chown=django:django ${APP_HOME} ${APP_HOME}
-# explicitly create the media folder before changing ownership below
-RUN mkdir -p ${APP_HOME}/ether/media
-
-# make django owner of the WORKDIR directory as well.
-RUN chown django:django ${APP_HOME}
-
-# Place executables in the environment at the front of the path
-ENV PATH="/app/.venv/bin:$PATH"
-
-USER django
-
-RUN DATABASE_URL="" \
- DJANGO_SETTINGS_MODULE="config.settings.test" \
- python manage.py compilemessages
-
-ENTRYPOINT ["/entrypoint"]
diff --git a/compose/production/django/entrypoint b/compose/production/django/entrypoint
deleted file mode 100644
index fe9a013..0000000
--- a/compose/production/django/entrypoint
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-if [ -z "${POSTGRES_USER}" ]; then
- base_postgres_image_default_user='postgres'
- export POSTGRES_USER="${base_postgres_image_default_user}"
-fi
-export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
-
-wait-for-it "${POSTGRES_HOST}:${POSTGRES_PORT}" -t 30
-
->&2 echo 'PostgreSQL is available'
-
-exec "$@"
diff --git a/compose/production/django/start b/compose/production/django/start
deleted file mode 100644
index 53bf12a..0000000
--- a/compose/production/django/start
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-python /app/manage.py collectstatic --noinput
-
-exec gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
diff --git a/compose/production/nginx/Dockerfile b/compose/production/nginx/Dockerfile
deleted file mode 100644
index 412c6a7..0000000
--- a/compose/production/nginx/Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM docker.io/nginx:1.29.8-alpine
-COPY ./compose/production/nginx/default.conf /etc/nginx/conf.d/default.conf
diff --git a/compose/production/nginx/default.conf b/compose/production/nginx/default.conf
deleted file mode 100644
index 562dba8..0000000
--- a/compose/production/nginx/default.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-server {
- listen 80;
- server_name localhost;
- location /media/ {
- alias /usr/share/nginx/media/;
- }
-}
diff --git a/compose/production/postgres/Dockerfile b/compose/production/postgres/Dockerfile
deleted file mode 100644
index a6ac0eb..0000000
--- a/compose/production/postgres/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM docker.io/postgres:18
-
-COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance
-RUN chmod +x /usr/local/bin/maintenance/*
-RUN mv /usr/local/bin/maintenance/* /usr/local/bin \
- && rmdir /usr/local/bin/maintenance
diff --git a/compose/production/postgres/maintenance/_sourced/constants.sh b/compose/production/postgres/maintenance/_sourced/constants.sh
deleted file mode 100644
index 6ca4f0c..0000000
--- a/compose/production/postgres/maintenance/_sourced/constants.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-
-BACKUP_DIR_PATH='/backups'
-BACKUP_FILE_PREFIX='backup'
diff --git a/compose/production/postgres/maintenance/_sourced/countdown.sh b/compose/production/postgres/maintenance/_sourced/countdown.sh
deleted file mode 100644
index e6cbfb6..0000000
--- a/compose/production/postgres/maintenance/_sourced/countdown.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-
-countdown() {
- declare desc="A simple countdown. Source: https://superuser.com/a/611582"
- local seconds="${1}"
- local d=$(($(date +%s) + "${seconds}"))
- while [ "$d" -ge `date +%s` ]; do
- echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r";
- sleep 0.1
- done
-}
diff --git a/compose/production/postgres/maintenance/_sourced/messages.sh b/compose/production/postgres/maintenance/_sourced/messages.sh
deleted file mode 100644
index f6be756..0000000
--- a/compose/production/postgres/maintenance/_sourced/messages.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-
-message_newline() {
- echo
-}
-
-message_debug()
-{
- echo -e "DEBUG: ${@}"
-}
-
-message_welcome()
-{
- echo -e "\e[1m${@}\e[0m"
-}
-
-message_warning()
-{
- echo -e "\e[33mWARNING\e[0m: ${@}"
-}
-
-message_error()
-{
- echo -e "\e[31mERROR\e[0m: ${@}"
-}
-
-message_info()
-{
- echo -e "\e[37mINFO\e[0m: ${@}"
-}
-
-message_suggestion()
-{
- echo -e "\e[33mSUGGESTION\e[0m: ${@}"
-}
-
-message_success()
-{
- echo -e "\e[32mSUCCESS\e[0m: ${@}"
-}
diff --git a/compose/production/postgres/maintenance/_sourced/yes_no.sh b/compose/production/postgres/maintenance/_sourced/yes_no.sh
deleted file mode 100644
index fd9cae1..0000000
--- a/compose/production/postgres/maintenance/_sourced/yes_no.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-
-yes_no() {
- declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message."
- local arg1="${1}"
-
- local response=
- read -r -p "${arg1} (y/[n])? " response
- if [[ "${response}" =~ ^[Yy]$ ]]
- then
- exit 0
- else
- exit 1
- fi
-}
diff --git a/compose/production/postgres/maintenance/backup b/compose/production/postgres/maintenance/backup
deleted file mode 100644
index f72304c..0000000
--- a/compose/production/postgres/maintenance/backup
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-
-### Create a database backup.
-###
-### Usage:
-### $ docker compose -f .yml (exec |run --rm) postgres backup
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-message_welcome "Backing up the '${POSTGRES_DB}' database..."
-
-
-if [[ "${POSTGRES_USER}" == "postgres" ]]; then
- message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
- exit 1
-fi
-
-export PGHOST="${POSTGRES_HOST}"
-export PGPORT="${POSTGRES_PORT}"
-export PGUSER="${POSTGRES_USER}"
-export PGPASSWORD="${POSTGRES_PASSWORD}"
-export PGDATABASE="${POSTGRES_DB}"
-
-backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz"
-pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}"
-
-
-message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'."
diff --git a/compose/production/postgres/maintenance/backups b/compose/production/postgres/maintenance/backups
deleted file mode 100644
index a18937d..0000000
--- a/compose/production/postgres/maintenance/backups
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-
-### View backups.
-###
-### Usage:
-### $ docker compose -f .yml (exec |run --rm) postgres backups
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-message_welcome "These are the backups you have got:"
-
-ls -lht "${BACKUP_DIR_PATH}"
diff --git a/compose/production/postgres/maintenance/restore b/compose/production/postgres/maintenance/restore
deleted file mode 100644
index c68f17d..0000000
--- a/compose/production/postgres/maintenance/restore
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env bash
-
-
-### Restore database from a backup.
-###
-### Parameters:
-### <1> filename of an existing backup.
-###
-### Usage:
-### $ docker compose -f .yml (exec |run --rm) postgres restore <1>
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-if [[ -z ${1+x} ]]; then
- message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
- exit 1
-fi
-backup_filename="${BACKUP_DIR_PATH}/${1}"
-if [[ ! -f "${backup_filename}" ]]; then
- message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
- exit 1
-fi
-
-message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..."
-
-if [[ "${POSTGRES_USER}" == "postgres" ]]; then
- message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
- exit 1
-fi
-
-export PGHOST="${POSTGRES_HOST}"
-export PGPORT="${POSTGRES_PORT}"
-export PGUSER="${POSTGRES_USER}"
-export PGPASSWORD="${POSTGRES_PASSWORD}"
-export PGDATABASE="${POSTGRES_DB}"
-
-message_info "Dropping the database..."
-dropdb "${PGDATABASE}"
-
-message_info "Creating a new database..."
-createdb --owner="${POSTGRES_USER}"
-
-message_info "Applying the backup to the new database..."
-gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}"
-
-message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup."
diff --git a/compose/production/postgres/maintenance/rmbackup b/compose/production/postgres/maintenance/rmbackup
deleted file mode 100644
index fdfd20e..0000000
--- a/compose/production/postgres/maintenance/rmbackup
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-
-### Remove a database backup.
-###
-### Parameters:
-### <1> filename of a backup to remove.
-###
-### Usage:
-### $ docker-compose -f .yml (exec |run --rm) postgres rmbackup <1>
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-if [[ -z ${1+x} ]]; then
- message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
- exit 1
-fi
-backup_filename="${BACKUP_DIR_PATH}/${1}"
-if [[ ! -f "${backup_filename}" ]]; then
- message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
- exit 1
-fi
-
-message_welcome "Removing the '${backup_filename}' backup file..."
-
-rm -r "${backup_filename}"
-
-message_success "The '${backup_filename}' database backup has been removed."
diff --git a/compose/production/traefik/Dockerfile b/compose/production/traefik/Dockerfile
deleted file mode 100644
index b774dca..0000000
--- a/compose/production/traefik/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM docker.io/traefik:v3.6.15
-RUN mkdir -p /etc/traefik/acme \
- && touch /etc/traefik/acme/acme.json \
- && chmod 600 /etc/traefik/acme/acme.json
-COPY ./compose/production/traefik/traefik.yml /etc/traefik
diff --git a/compose/production/traefik/traefik.yml b/compose/production/traefik/traefik.yml
deleted file mode 100644
index 5136741..0000000
--- a/compose/production/traefik/traefik.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-log:
- level: INFO
-
-entryPoints:
- web:
- # http
- address: ':80'
- http:
- # https://doc.traefik.io/traefik/routing/entrypoints/#entrypoint
- redirections:
- entryPoint:
- to: web-secure
-
- web-secure:
- # https
- address: ':443'
-
-certificatesResolvers:
- letsencrypt:
- # https://doc.traefik.io/traefik/https/acme/#lets-encrypt
- acme:
- email: 'info@ether.local'
- storage: /etc/traefik/acme/acme.json
- # https://doc.traefik.io/traefik/https/acme/#httpchallenge
- httpChallenge:
- entryPoint: web
-
-http:
- routers:
- web-secure-router:
- rule: 'Host(`ether.local`) || Host(`www.ether.local`)'
- entryPoints:
- - web-secure
- middlewares:
- - csrf
- service: django
- tls:
- # https://doc.traefik.io/traefik/routing/routers/#certresolver
- certResolver: letsencrypt
-
- web-media-router:
- rule: '(Host(`ether.local`) || Host(`www.ether.local`)) && PathPrefix(`/media/`)'
- entryPoints:
- - web-secure
- middlewares:
- - csrf
- service: django-media
- tls:
- certResolver: letsencrypt
-
- middlewares:
- csrf:
- # https://doc.traefik.io/traefik/master/middlewares/http/headers/#hostsproxyheaders
- # https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
- headers:
- hostsProxyHeaders: ['X-CSRFToken']
-
- services:
- django:
- loadBalancer:
- servers:
- - url: http://django:5000
-
- django-media:
- loadBalancer:
- servers:
- - url: http://nginx:80
-
-providers:
- # https://doc.traefik.io/traefik/master/providers/file/
- file:
- filename: /etc/traefik/traefik.yml
- watch: true
diff --git a/config/settings.py b/config/settings.py
new file mode 100644
index 0000000..25b6a25
--- /dev/null
+++ b/config/settings.py
@@ -0,0 +1,91 @@
+"""
+Django settings for the Ether合同会社 website.
+
+Designed to deploy on Vercel via the Python Runtime, which detects manage.py
+to find the WSGI entrypoint and the configuration for static files.
+"""
+
+import os
+from pathlib import Path
+
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+SECRET_KEY = os.environ.get(
+ "DJANGO_SECRET_KEY",
+ "django-insecure-change-me-in-production",
+)
+
+DEBUG = os.environ.get("DJANGO_DEBUG") == "1"
+
+ALLOWED_HOSTS = [
+ "127.0.0.1",
+ "localhost",
+ ".vercel.app",
+ "ether-llc.com",
+ "www.ether-llc.com",
+]
+
+INSTALLED_APPS = [
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "ether",
+]
+
+MIDDLEWARE = [
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+]
+
+ROOT_URLCONF = "config.urls"
+
+TEMPLATES = [
+ {
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [BASE_DIR / "ether" / "templates"],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = "config.wsgi.application"
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": BASE_DIR / "db.sqlite3",
+ },
+}
+
+_PW_PREFIX = "django.contrib.auth.password_validation"
+AUTH_PASSWORD_VALIDATORS = [
+ {"NAME": f"{_PW_PREFIX}.UserAttributeSimilarityValidator"},
+ {"NAME": f"{_PW_PREFIX}.MinimumLengthValidator"},
+ {"NAME": f"{_PW_PREFIX}.CommonPasswordValidator"},
+ {"NAME": f"{_PW_PREFIX}.NumericPasswordValidator"},
+]
+
+LANGUAGE_CODE = "ja"
+TIME_ZONE = "Asia/Tokyo"
+USE_I18N = True
+USE_TZ = True
+
+STATIC_URL = "/static/"
+STATIC_ROOT = BASE_DIR / "staticfiles"
+STATICFILES_DIRS = [BASE_DIR / "ether" / "static"]
+
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
diff --git a/config/settings/__init__.py b/config/settings/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/config/settings/base.py b/config/settings/base.py
deleted file mode 100644
index 2bfe444..0000000
--- a/config/settings/base.py
+++ /dev/null
@@ -1,283 +0,0 @@
-# ruff: noqa: ERA001, E501
-"""Base settings to build other settings files upon."""
-
-from pathlib import Path
-
-import environ
-
-BASE_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
-# ether/
-APPS_DIR = BASE_DIR / "ether"
-env = environ.Env()
-
-READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)
-if READ_DOT_ENV_FILE:
- # OS environment variables take precedence over variables from .env
- env.read_env(str(BASE_DIR / ".env"))
-
-# GENERAL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#debug
-DEBUG = env.bool("DJANGO_DEBUG", False)
-# Local time zone. Choices are
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# though not all of them may be available with every OS.
-# In Windows, this must be set to your system time zone.
-TIME_ZONE = "Asia/Tokyo"
-# https://docs.djangoproject.com/en/dev/ref/settings/#language-code
-LANGUAGE_CODE = "ja"
-# https://docs.djangoproject.com/en/dev/ref/settings/#languages
-# from django.utils.translation import gettext_lazy as _
-# LANGUAGES = [
-# ('en', _('English')),
-# ('fr-fr', _('French')),
-# ('pt-br', _('Portuguese')),
-# ]
-# https://docs.djangoproject.com/en/dev/ref/settings/#site-id
-SITE_ID = 1
-# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
-USE_I18N = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
-USE_TZ = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths
-LOCALE_PATHS = [str(BASE_DIR / "locale")]
-
-# DATABASES
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#databases
-DATABASES = {"default": env.db("DATABASE_URL")}
-DATABASES["default"]["ATOMIC_REQUESTS"] = True
-
-# URLS
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
-ROOT_URLCONF = "config.urls"
-# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
-WSGI_APPLICATION = "config.wsgi.application"
-
-# APPS
-# ------------------------------------------------------------------------------
-DJANGO_APPS = [
- "django.contrib.auth",
- "django.contrib.contenttypes",
- "django.contrib.sessions",
- "django.contrib.sites",
- "django.contrib.messages",
- "django.contrib.staticfiles",
- # "django.contrib.humanize", # Handy template tags
- "django.contrib.admin",
- "django.forms",
-]
-THIRD_PARTY_APPS = [
- "crispy_forms",
- "crispy_bootstrap5",
- "allauth",
- "allauth.account",
- "allauth.mfa",
- "allauth.socialaccount",
-]
-
-LOCAL_APPS = [
- "ether.users",
- # Your stuff: custom apps go here
-]
-# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
-INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
-
-# MIGRATIONS
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules
-MIGRATION_MODULES = {"sites": "ether.contrib.sites.migrations"}
-
-# AUTHENTICATION
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends
-AUTHENTICATION_BACKENDS = [
- "django.contrib.auth.backends.ModelBackend",
- "allauth.account.auth_backends.AuthenticationBackend",
-]
-# https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model
-AUTH_USER_MODEL = "users.User"
-# https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url
-LOGIN_REDIRECT_URL = "users:redirect"
-# https://docs.djangoproject.com/en/dev/ref/settings/#login-url
-LOGIN_URL = "account_login"
-
-# PASSWORDS
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
-PASSWORD_HASHERS = [
- # https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django
- "django.contrib.auth.hashers.Argon2PasswordHasher",
- "django.contrib.auth.hashers.PBKDF2PasswordHasher",
- "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
- "django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
-]
-# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators
-AUTH_PASSWORD_VALIDATORS = [
- {
- "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
- },
- {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
- {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
- {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
-]
-
-# MIDDLEWARE
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#middleware
-MIDDLEWARE = [
- "django.middleware.security.SecurityMiddleware",
- "whitenoise.middleware.WhiteNoiseMiddleware",
- "django.contrib.sessions.middleware.SessionMiddleware",
- "django.middleware.locale.LocaleMiddleware",
- "django.middleware.common.CommonMiddleware",
- "django.middleware.csrf.CsrfViewMiddleware",
- "django.contrib.auth.middleware.AuthenticationMiddleware",
- "django.contrib.messages.middleware.MessageMiddleware",
- "django.middleware.clickjacking.XFrameOptionsMiddleware",
- "allauth.account.middleware.AccountMiddleware",
-]
-
-# STATIC
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#static-root
-STATIC_ROOT = str(BASE_DIR / "staticfiles")
-# https://docs.djangoproject.com/en/dev/ref/settings/#static-url
-STATIC_URL = "/static/"
-# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
-STATICFILES_DIRS = [str(APPS_DIR / "static")]
-# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
-STATICFILES_FINDERS = [
- "django.contrib.staticfiles.finders.FileSystemFinder",
- "django.contrib.staticfiles.finders.AppDirectoriesFinder",
-]
-
-# MEDIA
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#media-root
-MEDIA_ROOT = str(APPS_DIR / "media")
-# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
-MEDIA_URL = "/media/"
-
-# TEMPLATES
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#templates
-TEMPLATES = [
- {
- # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND
- "BACKEND": "django.template.backends.django.DjangoTemplates",
- # https://docs.djangoproject.com/en/dev/ref/settings/#dirs
- "DIRS": [str(APPS_DIR / "templates")],
- # https://docs.djangoproject.com/en/dev/ref/settings/#app-dirs
- "APP_DIRS": True,
- "OPTIONS": {
- # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
- "context_processors": [
- "django.template.context_processors.debug",
- "django.template.context_processors.request",
- "django.contrib.auth.context_processors.auth",
- "django.template.context_processors.i18n",
- "django.template.context_processors.media",
- "django.template.context_processors.static",
- "django.template.context_processors.tz",
- "django.contrib.messages.context_processors.messages",
- "ether.users.context_processors.allauth_settings",
- ],
- },
- },
-]
-
-# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer
-FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
-
-# http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs
-CRISPY_TEMPLATE_PACK = "bootstrap5"
-CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
-
-# FIXTURES
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs
-FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)
-
-# SECURITY
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
-SESSION_COOKIE_HTTPONLY = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
-CSRF_COOKIE_HTTPONLY = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
-X_FRAME_OPTIONS = "DENY"
-
-# EMAIL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
-EMAIL_BACKEND = env(
- "DJANGO_EMAIL_BACKEND",
- default="django.core.mail.backends.smtp.EmailBackend",
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-timeout
-EMAIL_TIMEOUT = 5
-
-# ADMIN
-# ------------------------------------------------------------------------------
-# Django Admin URL.
-ADMIN_URL = "admin/"
-# https://docs.djangoproject.com/en/dev/ref/settings/#admins
-ADMINS = ['"Ether LLC" ']
-# https://docs.djangoproject.com/en/dev/ref/settings/#managers
-MANAGERS = ADMINS
-# https://cookiecutter-django.readthedocs.io/en/latest/settings.html#other-environment-settings
-# Force the `admin` sign in process to go through the `django-allauth` workflow
-DJANGO_ADMIN_FORCE_ALLAUTH = env.bool("DJANGO_ADMIN_FORCE_ALLAUTH", default=False)
-
-# LOGGING
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#logging
-# See https://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
- "version": 1,
- "disable_existing_loggers": False,
- "formatters": {
- "verbose": {
- "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s",
- },
- },
- "handlers": {
- "console": {
- "level": "DEBUG",
- "class": "logging.StreamHandler",
- "formatter": "verbose",
- },
- },
- "root": {"level": "INFO", "handlers": ["console"]},
-}
-
-REDIS_URL = env("REDIS_URL", default="redis://redis:6379/0")
-REDIS_SSL = REDIS_URL.startswith("rediss://")
-
-
-# django-allauth
-# ------------------------------------------------------------------------------
-ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
-# https://docs.allauth.org/en/latest/account/configuration.html
-ACCOUNT_LOGIN_METHODS = {"email"}
-# https://docs.allauth.org/en/latest/account/configuration.html
-ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"]
-# https://docs.allauth.org/en/latest/account/configuration.html
-ACCOUNT_USER_MODEL_USERNAME_FIELD = None
-# https://docs.allauth.org/en/latest/account/configuration.html
-ACCOUNT_EMAIL_VERIFICATION = "mandatory"
-# https://docs.allauth.org/en/latest/account/configuration.html
-ACCOUNT_ADAPTER = "ether.users.adapters.AccountAdapter"
-# https://docs.allauth.org/en/latest/account/forms.html
-ACCOUNT_FORMS = {"signup": "ether.users.forms.UserSignupForm"}
-# https://docs.allauth.org/en/latest/socialaccount/configuration.html
-SOCIALACCOUNT_ADAPTER = "ether.users.adapters.SocialAccountAdapter"
-# https://docs.allauth.org/en/latest/socialaccount/configuration.html
-SOCIALACCOUNT_FORMS = {"signup": "ether.users.forms.UserSocialSignupForm"}
-
-
-# Your stuff...
-# ------------------------------------------------------------------------------
diff --git a/config/settings/local.py b/config/settings/local.py
deleted file mode 100644
index ad6b796..0000000
--- a/config/settings/local.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from .base import * # noqa: F403
-from .base import INSTALLED_APPS
-from .base import MIDDLEWARE
-from .base import env
-
-# GENERAL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#debug
-DEBUG = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
-SECRET_KEY = env(
- "DJANGO_SECRET_KEY",
- default="k3B7WAkzq0MhHG3ATI4Jm6cj1HM0q4zSHGh3mZ76PXBtMq1iXIjPlyMymA02bR8t",
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
-ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] # noqa: S104
-
-# CACHES
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#caches
-CACHES = {
- "default": {
- "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
- "LOCATION": "",
- },
-}
-
-# EMAIL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-host
-EMAIL_HOST = env("EMAIL_HOST", default="mailpit")
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-port
-EMAIL_PORT = 1025
-
-# WhiteNoise
-# ------------------------------------------------------------------------------
-# http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development
-INSTALLED_APPS = ["whitenoise.runserver_nostatic", *INSTALLED_APPS]
-
-
-# django-debug-toolbar
-# ------------------------------------------------------------------------------
-# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
-INSTALLED_APPS += ["debug_toolbar"]
-# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
-MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
-# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config
-DEBUG_TOOLBAR_CONFIG = {
- "DISABLE_PANELS": [
- "debug_toolbar.panels.redirects.RedirectsPanel",
- # Disable profiling panel due to an issue with Python 3.12+:
- # https://github.com/jazzband/django-debug-toolbar/issues/1875
- "debug_toolbar.panels.profiling.ProfilingPanel",
- ],
- "SHOW_TEMPLATE_CONTEXT": True,
-}
-# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips
-INTERNAL_IPS = ["127.0.0.1", "10.0.2.2"]
-if env("USE_DOCKER") == "yes":
- import socket
-
- hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
- INTERNAL_IPS += [".".join([*ip.split(".")[:-1], "1"]) for ip in ips]
-
-# django-extensions
-# ------------------------------------------------------------------------------
-# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
-INSTALLED_APPS += ["django_extensions"]
-
-# Your stuff...
-# ------------------------------------------------------------------------------
diff --git a/config/settings/production.py b/config/settings/production.py
deleted file mode 100644
index b3a9852..0000000
--- a/config/settings/production.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# ruff: noqa: E501
-from .base import * # noqa: F403
-from .base import DATABASES
-from .base import INSTALLED_APPS
-from .base import REDIS_URL
-from .base import env
-
-# GENERAL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
-SECRET_KEY = env("DJANGO_SECRET_KEY")
-# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
-ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["ether.local"])
-
-# DATABASES
-# ------------------------------------------------------------------------------
-DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60)
-
-# CACHES
-# ------------------------------------------------------------------------------
-CACHES = {
- "default": {
- "BACKEND": "django_redis.cache.RedisCache",
- "LOCATION": REDIS_URL,
- "OPTIONS": {
- "CLIENT_CLASS": "django_redis.client.DefaultClient",
- # Mimicking memcache behavior.
- # https://github.com/jazzband/django-redis#memcached-exceptions-behavior
- "IGNORE_EXCEPTIONS": True,
- },
- },
-}
-
-# SECURITY
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
-SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
-# https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect
-SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True)
-# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure
-SESSION_COOKIE_SECURE = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-name
-SESSION_COOKIE_NAME = "__Secure-sessionid"
-# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure
-CSRF_COOKIE_SECURE = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-name
-CSRF_COOKIE_NAME = "__Secure-csrftoken"
-# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https
-# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds
-# TODO: set this to 60 seconds first and then to 518400 once you prove the former works
-SECURE_HSTS_SECONDS = 60
-# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains
-SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
- "DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS",
- default=True,
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload
-SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True)
-# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
-SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
- "DJANGO_SECURE_CONTENT_TYPE_NOSNIFF",
- default=True,
-)
-
-# STATIC & MEDIA
-# ------------------------
-STORAGES = {
- "default": {
- "BACKEND": "django.core.files.storage.FileSystemStorage",
- },
- "staticfiles": {
- "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
- },
-}
-
-# EMAIL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email
-DEFAULT_FROM_EMAIL = env(
- "DJANGO_DEFAULT_FROM_EMAIL",
- default="Ether Organization Website ",
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#server-email
-SERVER_EMAIL = env("DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL)
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
-EMAIL_SUBJECT_PREFIX = env(
- "DJANGO_EMAIL_SUBJECT_PREFIX",
- default="[Ether Organization Website] ",
-)
-ACCOUNT_EMAIL_SUBJECT_PREFIX = EMAIL_SUBJECT_PREFIX
-
-# ADMIN
-# ------------------------------------------------------------------------------
-# Django Admin URL regex.
-ADMIN_URL = env("DJANGO_ADMIN_URL")
-
-# Anymail
-# ------------------------------------------------------------------------------
-# https://anymail.readthedocs.io/en/stable/installation/#installing-anymail
-INSTALLED_APPS += ["anymail"]
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
-# https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
-# https://anymail.readthedocs.io/en/stable/esps
-EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
-ANYMAIL = {}
-
-
-# LOGGING
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#logging
-# See https://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error when DEBUG=False.
-LOGGING = {
- "version": 1,
- "disable_existing_loggers": False,
- "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
- "formatters": {
- "verbose": {
- "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s",
- },
- },
- "handlers": {
- "mail_admins": {
- "level": "ERROR",
- "filters": ["require_debug_false"],
- "class": "django.utils.log.AdminEmailHandler",
- },
- "console": {
- "level": "DEBUG",
- "class": "logging.StreamHandler",
- "formatter": "verbose",
- },
- },
- "root": {"level": "INFO", "handlers": ["console"]},
- "loggers": {
- "django.request": {
- "handlers": ["mail_admins"],
- "level": "ERROR",
- "propagate": True,
- },
- "django.security.DisallowedHost": {
- "level": "ERROR",
- "handlers": ["console", "mail_admins"],
- "propagate": True,
- },
- },
-}
-
-
-# Your stuff...
-# ------------------------------------------------------------------------------
diff --git a/config/settings/test.py b/config/settings/test.py
deleted file mode 100644
index cf26dfd..0000000
--- a/config/settings/test.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-With these settings, tests run faster.
-"""
-
-from .base import * # noqa: F403
-from .base import TEMPLATES
-from .base import env
-
-# GENERAL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
-SECRET_KEY = env(
- "DJANGO_SECRET_KEY",
- default="ZORKsoJTGuQ8UzeUL2sl6MiwSUDqxS1ny7FmEW2ElkBAtWQRCsSeFgfa3fUy9b6V",
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
-TEST_RUNNER = "django.test.runner.DiscoverRunner"
-
-# PASSWORDS
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
-PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
-
-# EMAIL
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
-EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
-
-# DEBUGGING FOR TEMPLATES
-# ------------------------------------------------------------------------------
-TEMPLATES[0]["OPTIONS"]["debug"] = True # type: ignore[index]
-
-# MEDIA
-# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
-MEDIA_URL = "http://media.testserver/"
-# Your stuff...
-# ------------------------------------------------------------------------------
diff --git a/config/urls.py b/config/urls.py
index fe48b61..ba42593 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -1,70 +1,118 @@
-from django.conf import settings
-from django.conf.urls.static import static
from django.contrib import admin
-from django.urls import include
from django.urls import path
-from django.views import defaults as default_views
from django.views.generic import TemplateView
+
+def _page(template: str, lang: str, page_name: str, alt_url: str) -> TemplateView:
+ return TemplateView.as_view(
+ template_name=template,
+ extra_context={
+ "lang": lang,
+ "page_name": page_name,
+ "alt_lang_url": alt_url,
+ },
+ )
+
+
urlpatterns = [
- path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
+ path("", _page("pages/home.html", "ja", "home", "/en/"), name="home"),
path(
- "services/",
- TemplateView.as_view(template_name="pages/services.html"),
- name="services",
+ "vision/",
+ _page("pages/vision.html", "ja", "vision", "/en/vision/"),
+ name="vision",
),
path(
- "company/",
- TemplateView.as_view(template_name="pages/company.html"),
- name="company",
+ "software/",
+ _page("pages/software.html", "ja", "software", "/en/software/"),
+ name="software",
),
path(
- "about/",
- TemplateView.as_view(template_name="pages/company.html"),
- name="about",
+ "holdings/",
+ _page("pages/holdings.html", "ja", "holdings", "/en/holdings/"),
+ name="holdings",
+ ),
+ path(
+ "business/",
+ _page("pages/business.html", "ja", "business", "/en/business/"),
+ name="business",
+ ),
+ path(
+ "company/",
+ _page("pages/company.html", "ja", "company", "/en/company/"),
+ name="company",
),
path(
"contact/",
- TemplateView.as_view(template_name="pages/contact.html"),
+ _page("pages/contact.html", "ja", "contact", "/en/contact/"),
name="contact",
),
- # Django Admin, use {% url 'admin:index' %}
- path(settings.ADMIN_URL, admin.site.urls),
- # User management
- path("users/", include("ether.users.urls", namespace="users")),
- path("accounts/", include("allauth.urls")),
- # Your stuff: custom urls includes go here
- # ...
- # Media files
- *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
-]
-
-
-if settings.DEBUG:
- # This allows the error pages to be debugged during development, just visit
- # these url in browser to see how these error pages look like.
- urlpatterns += [
- path(
- "400/",
- default_views.bad_request,
- kwargs={"exception": Exception("Bad Request!")},
- ),
- path(
- "403/",
- default_views.permission_denied,
- kwargs={"exception": Exception("Permission Denied")},
+ path(
+ "legal/privacy/",
+ _page("pages/legal/privacy.html", "ja", "privacy", "/en/legal/privacy/"),
+ name="privacy",
+ ),
+ path(
+ "legal/cookies/",
+ _page("pages/legal/cookies.html", "ja", "cookies", "/en/legal/cookies/"),
+ name="cookies",
+ ),
+ path("en/", _page("pages/en/home.html", "en", "home", "/"), name="home_en"),
+ path(
+ "en/vision/",
+ _page("pages/en/vision.html", "en", "vision", "/vision/"),
+ name="vision_en",
+ ),
+ path(
+ "en/software/",
+ _page("pages/en/software.html", "en", "software", "/software/"),
+ name="software_en",
+ ),
+ path(
+ "en/holdings/",
+ _page("pages/en/holdings.html", "en", "holdings", "/holdings/"),
+ name="holdings_en",
+ ),
+ path(
+ "en/business/",
+ _page("pages/en/business.html", "en", "business", "/business/"),
+ name="business_en",
+ ),
+ path(
+ "en/company/",
+ _page("pages/en/company.html", "en", "company", "/company/"),
+ name="company_en",
+ ),
+ path(
+ "en/contact/",
+ _page("pages/en/contact.html", "en", "contact", "/contact/"),
+ name="contact_en",
+ ),
+ path(
+ "en/legal/privacy/",
+ _page("pages/en/legal/privacy.html", "en", "privacy", "/legal/privacy/"),
+ name="privacy_en",
+ ),
+ path(
+ "en/legal/cookies/",
+ _page("pages/en/legal/cookies.html", "en", "cookies", "/legal/cookies/"),
+ name="cookies_en",
+ ),
+ path(
+ "robots.txt",
+ TemplateView.as_view(
+ template_name="robots.txt",
+ content_type="text/plain",
),
- path(
- "404/",
- default_views.page_not_found,
- kwargs={"exception": Exception("Page not Found")},
+ name="robots",
+ ),
+ path(
+ "sitemap.xml",
+ TemplateView.as_view(
+ template_name="sitemap.xml",
+ content_type="application/xml",
),
- path("500/", default_views.server_error),
- ]
- if "debug_toolbar" in settings.INSTALLED_APPS:
- import debug_toolbar
+ name="sitemap",
+ ),
+ path("admin/", admin.site.urls),
+]
- urlpatterns = [
- path("__debug__/", include(debug_toolbar.urls)),
- *urlpatterns,
- ]
diff --git a/config/wsgi.py b/config/wsgi.py
index 826b2d7..a390bbe 100644
--- a/config/wsgi.py
+++ b/config/wsgi.py
@@ -1,32 +1,13 @@
"""
-WSGI config for Ether Organization Website project.
-
-This module contains the WSGI application used by Django's development server
-and any production WSGI deployments. It should expose a module-level variable
-named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
-this application via the ``WSGI_APPLICATION`` setting.
-
-Usually you will have the standard Django WSGI application here, but it also
-might make sense to replace the whole Django WSGI application with a custom one
-that later delegates to the Django one. For example, you could introduce WSGI
-middleware here, or combine a Django application with an application of another
-framework.
+WSGI config for Ether合同会社 website.
+It exposes the WSGI callable as a module-level variable named ``application``.
"""
import os
-import sys
-from pathlib import Path
from django.core.wsgi import get_wsgi_application
-# This allows easy placement of apps within the interior
-# ether directory.
-BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
-sys.path.append(str(BASE_DIR / "ether"))
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
-# This application object is used by any WSGI server configured to use this
-# file. This includes Django's development server, if the WSGI_APPLICATION
-# setting points here.
application = get_wsgi_application()
diff --git a/docker-compose.docs.yml b/docker-compose.docs.yml
deleted file mode 100644
index cf7de53..0000000
--- a/docker-compose.docs.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- docs:
- image: ether_local_docs
- container_name: ether_local_docs
- build:
- context: .
- dockerfile: ./compose/local/docs/Dockerfile
- env_file:
- - ./.envs/.local/.django
- volumes:
- - /app/.venv
- - ./docs:/docs:z
- - ./config:/app/config:z
- - ./ether:/app/ether:z
- ports:
- - '9000:9000'
- command: /start-docs
diff --git a/docker-compose.local.yml b/docker-compose.local.yml
deleted file mode 100644
index 41104d9..0000000
--- a/docker-compose.local.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-volumes:
- ether_local_postgres_data: {}
- ether_local_postgres_data_backups: {}
-
-
-services:
- django:
- build:
- context: .
- dockerfile: ./compose/local/django/Dockerfile
- image: ether_local_django
- container_name: ether_local_django
- depends_on:
- - postgres
- - mailpit
- volumes:
- - /app/.venv
- - .:/app:z
- env_file:
- - ./.envs/.local/.django
- - ./.envs/.local/.postgres
- ports:
- - '8000:8000'
- command: /start
-
- postgres:
- build:
- context: .
- dockerfile: ./compose/production/postgres/Dockerfile
- image: ether_production_postgres
- container_name: ether_local_postgres
- volumes:
- - ether_local_postgres_data:/var/lib/postgresql/18/docker
- - ether_local_postgres_data_backups:/backups
- env_file:
- - ./.envs/.local/.postgres
-
- mailpit:
- image: docker.io/axllent/mailpit:latest
- container_name: ether_local_mailpit
- ports:
- - "8025:8025"
diff --git a/docker-compose.production.yml b/docker-compose.production.yml
deleted file mode 100644
index d1e3275..0000000
--- a/docker-compose.production.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-volumes:
- production_postgres_data: {}
- production_postgres_data_backups: {}
- production_traefik: {}
- production_django_media: {}
-
-
-
-services:
- django:
- build:
- context: .
- dockerfile: ./compose/production/django/Dockerfile
-
- image: ether_production_django
- volumes:
- - production_django_media:/app/ether/media
- depends_on:
- - postgres
- - redis
- env_file:
- - ./.envs/.production/.django
- - ./.envs/.production/.postgres
- command: /start
-
- postgres:
- build:
- context: .
- dockerfile: ./compose/production/postgres/Dockerfile
- image: ether_production_postgres
- volumes:
- - production_postgres_data:/var/lib/postgresql/18/docker
- - production_postgres_data_backups:/backups
- env_file:
- - ./.envs/.production/.postgres
-
- traefik:
- build:
- context: .
- dockerfile: ./compose/production/traefik/Dockerfile
- image: ether_production_traefik
- depends_on:
- - django
- volumes:
- - production_traefik:/etc/traefik/acme
- ports:
- - '0.0.0.0:80:80'
- - '0.0.0.0:443:443'
-
- redis:
- image: docker.io/redis:8.6
-
-
- nginx:
- build:
- context: .
- dockerfile: ./compose/production/nginx/Dockerfile
- image: ether_production_nginx
- depends_on:
- - django
- volumes:
- - production_django_media:/usr/share/nginx/media:ro
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index 6957700..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line, and also
-# from the environment for the first two.
-SPHINXOPTS ?=
-SPHINXBUILD ?= sphinx-build
-SOURCEDIR = .
-BUILDDIR = ./_build
-APP = /app
-
-.PHONY: help livehtml apidocs Makefile
-
-# Put it first so that "make" without argument is like "make help".
-help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -c .
-
-# Build, watch and serve docs with live reload
-livehtml:
- sphinx-autobuild -b html --host 0.0.0.0 --port 9000 --watch $(APP) -c . $(SOURCEDIR) $(BUILDDIR)/html
-
-# Outputs rst files from django application code
-apidocs:
- sphinx-apidoc -o $(SOURCEDIR)/api $(APP)
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -c .
diff --git a/docs/__init__.py b/docs/__init__.py
deleted file mode 100644
index 8772c82..0000000
--- a/docs/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Included so that Django's startproject comment runs against the docs directory
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index 4dec010..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# ruff: noqa: ERA001, PTH100
-# Configuration file for the Sphinx documentation builder.
-#
-# This file only contains a selection of the most common options. For a full
-# list see the documentation:
-# https://www.sphinx-doc.org/en/master/usage/configuration.html
-
-# -- Path setup --------------------------------------------------------------
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-
-import os
-import sys
-
-import django
-
-if os.getenv("READTHEDOCS", default="False") == "True":
- sys.path.insert(0, os.path.abspath(".."))
- os.environ["DJANGO_READ_DOT_ENV_FILE"] = "True"
- os.environ["USE_DOCKER"] = "no"
-else:
- sys.path.insert(0, os.path.abspath("/app"))
-os.environ["DATABASE_URL"] = "sqlite:///readthedocs.db"
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
-django.setup()
-
-# -- Project information -----------------------------------------------------
-
-project = "Ether Organization Website"
-copyright = """2026, Ether LLC""" # noqa: A001
-author = "Ether LLC"
-
-
-# -- General configuration ---------------------------------------------------
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
- "sphinx.ext.autodoc",
- "sphinx.ext.napoleon",
-]
-
-# Add any paths that contain templates here, relative to this directory.
-# templates_path = ["_templates"]
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
-
-# -- Options for HTML output -------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-#
-html_theme = "alabaster"
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ["_static"]
diff --git a/docs/howto.rst b/docs/howto.rst
deleted file mode 100644
index 20a0bb4..0000000
--- a/docs/howto.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-How To - Project Documentation
-======================================================================
-
-Get Started
-----------------------------------------------------------------------
-
-Documentation can be written as rst files in `ether/docs`.
-
-
-To build and serve docs, use the commands::
-
- docker compose -f docker-compose.docs.yml up
-
-
-
-Changes to files in `docs/_source` will be picked up and reloaded automatically.
-
-`Sphinx `_ is the tool used to build documentation.
-
-Docstrings to Documentation
-----------------------------------------------------------------------
-
-The sphinx extension `apidoc `_ is used to automatically document code using signatures and docstrings.
-
-Numpy or Google style docstrings will be picked up from project files and available for documentation. See the `Napoleon `_ extension for details.
-
-For an in-use example, see the `page source <_sources/users.rst.txt>`_ for :ref:`users`.
-
-To compile all docstrings automatically into documentation source files, use the command:
- ::
-
- uv run make apidocs
-
-
-This can be done in the docker container:
- ::
-
- docker run --rm docs make apidocs
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 5291c7a..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. Ether Organization Website documentation master file, created by
- sphinx-quickstart.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to Ether Organization Website's documentation!
-======================================================================
-
-.. toctree::
- :maxdepth: 2
- :caption: Contents:
-
- howto
- users
-
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 72db9e6..0000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,46 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build -c .
-)
-set SOURCEDIR=_source
-set BUILDDIR=_build
-set APP=..\ether
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.Install sphinx-autobuild for live serving.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-%SPHINXBUILD% -b %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:livehtml
-sphinx-autobuild -b html --open-browser -p 9000 --watch %APP% -c . %SOURCEDIR% %BUILDDIR%/html
-GOTO :EOF
-
-:apidocs
-sphinx-apidoc -o %SOURCEDIR%/api %APP%
-GOTO :EOF
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/docs/users.rst b/docs/users.rst
deleted file mode 100644
index 2f543c3..0000000
--- a/docs/users.rst
+++ /dev/null
@@ -1,15 +0,0 @@
- .. _users:
-
-Users
-======================================================================
-
-Starting a new project, it’s highly recommended to set up a custom user model,
-even if the default User model is sufficient for you.
-
-This model behaves identically to the default user model,
-but you’ll be able to customize it in the future if the need arises.
-
-.. automodule:: ether.users.models
- :members:
- :noindex:
-
diff --git a/ether/conftest.py b/ether/conftest.py
deleted file mode 100644
index 75e0b2e..0000000
--- a/ether/conftest.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import pytest
-
-from ether.users.models import User
-from ether.users.tests.factories import UserFactory
-
-
-@pytest.fixture(autouse=True)
-def _media_storage(settings, tmpdir) -> None:
- settings.MEDIA_ROOT = tmpdir.strpath
-
-
-@pytest.fixture
-def user(db) -> User:
- return UserFactory.create()
diff --git a/ether/contrib/__init__.py b/ether/contrib/__init__.py
deleted file mode 100644
index 6b59124..0000000
--- a/ether/contrib/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-To understand why this file is here, please read:
-
-https://cookiecutter-django.readthedocs.io/en/latest/5-help/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
-"""
diff --git a/ether/contrib/sites/__init__.py b/ether/contrib/sites/__init__.py
deleted file mode 100644
index 6b59124..0000000
--- a/ether/contrib/sites/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-To understand why this file is here, please read:
-
-https://cookiecutter-django.readthedocs.io/en/latest/5-help/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
-"""
diff --git a/ether/contrib/sites/migrations/0001_initial.py b/ether/contrib/sites/migrations/0001_initial.py
deleted file mode 100644
index fd76afb..0000000
--- a/ether/contrib/sites/migrations/0001_initial.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import django.contrib.sites.models
-from django.contrib.sites.models import _simple_domain_name_validator
-from django.db import migrations
-from django.db import models
-
-
-class Migration(migrations.Migration):
-
- dependencies = []
-
- operations = [
- migrations.CreateModel(
- name="Site",
- fields=[
- (
- "id",
- models.AutoField(
- verbose_name="ID",
- serialize=False,
- auto_created=True,
- primary_key=True,
- ),
- ),
- (
- "domain",
- models.CharField(
- max_length=100,
- verbose_name="domain name",
- validators=[_simple_domain_name_validator],
- ),
- ),
- ("name", models.CharField(max_length=50, verbose_name="display name")),
- ],
- options={
- "ordering": ("domain",),
- "db_table": "django_site",
- "verbose_name": "site",
- "verbose_name_plural": "sites",
- },
- bases=(models.Model,),
- managers=[("objects", django.contrib.sites.models.SiteManager())],
- ),
- ]
diff --git a/ether/contrib/sites/migrations/0002_alter_domain_unique.py b/ether/contrib/sites/migrations/0002_alter_domain_unique.py
deleted file mode 100644
index 4a44a6a..0000000
--- a/ether/contrib/sites/migrations/0002_alter_domain_unique.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import django.contrib.sites.models
-from django.db import migrations
-from django.db import models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [("sites", "0001_initial")]
-
- operations = [
- migrations.AlterField(
- model_name="site",
- name="domain",
- field=models.CharField(
- max_length=100,
- unique=True,
- validators=[django.contrib.sites.models._simple_domain_name_validator],
- verbose_name="domain name",
- ),
- )
- ]
diff --git a/ether/contrib/sites/migrations/0003_set_site_domain_and_name.py b/ether/contrib/sites/migrations/0003_set_site_domain_and_name.py
deleted file mode 100644
index 7d42a22..0000000
--- a/ether/contrib/sites/migrations/0003_set_site_domain_and_name.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""
-To understand why this file is here, please read:
-
-https://cookiecutter-django.readthedocs.io/en/latest/5-help/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
-"""
-from django.conf import settings
-from django.db import migrations
-
-
-def _update_or_create_site_with_sequence(site_model, connection, domain, name):
- """Update or create the site with default ID and keep the DB sequence in sync."""
- site, created = site_model.objects.update_or_create(
- id=settings.SITE_ID,
- defaults={
- "domain": domain,
- "name": name,
- },
- )
- if created:
- # We provided the ID explicitly when creating the Site entry, therefore the DB
- # sequence to auto-generate them wasn't used and is now out of sync. If we
- # don't do anything, we'll get a unique constraint violation the next time a
- # site is created.
- # To avoid this, we need to manually update DB sequence and make sure it's
- # greater than the maximum value.
- max_id = site_model.objects.order_by("-id").first().id
- with connection.cursor() as cursor:
- cursor.execute("SELECT last_value from django_site_id_seq")
- (current_id,) = cursor.fetchone()
- if current_id <= max_id:
- cursor.execute(
- "alter sequence django_site_id_seq restart with %s",
- [max_id + 1],
- )
-
-
-def update_site_forward(apps, schema_editor):
- """Set site domain and name."""
- Site = apps.get_model("sites", "Site")
- _update_or_create_site_with_sequence(
- Site,
- schema_editor.connection,
- "ether.local",
- "Ether Organization Website",
- )
-
-
-def update_site_backward(apps, schema_editor):
- """Revert site domain and name to default."""
- Site = apps.get_model("sites", "Site")
- _update_or_create_site_with_sequence(
- Site,
- schema_editor.connection,
- "example.com",
- "example.com",
- )
-
-
-class Migration(migrations.Migration):
-
- dependencies = [("sites", "0002_alter_domain_unique")]
-
- operations = [migrations.RunPython(update_site_forward, update_site_backward)]
diff --git a/ether/contrib/sites/migrations/0004_alter_options_ordering_domain.py b/ether/contrib/sites/migrations/0004_alter_options_ordering_domain.py
deleted file mode 100644
index f7118ca..0000000
--- a/ether/contrib/sites/migrations/0004_alter_options_ordering_domain.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.1.7 on 2021-02-04 14:49
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("sites", "0003_set_site_domain_and_name"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="site",
- options={
- "ordering": ["domain"],
- "verbose_name": "site",
- "verbose_name_plural": "sites",
- },
- ),
- ]
diff --git a/ether/contrib/sites/migrations/__init__.py b/ether/contrib/sites/migrations/__init__.py
deleted file mode 100644
index 6b59124..0000000
--- a/ether/contrib/sites/migrations/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-To understand why this file is here, please read:
-
-https://cookiecutter-django.readthedocs.io/en/latest/5-help/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
-"""
diff --git a/ether/static/css/project.css b/ether/static/css/project.css
index 545f151..ad63895 100644
--- a/ether/static/css/project.css
+++ b/ether/static/css/project.css
@@ -1082,8 +1082,8 @@ h4 {
.footer-grid {
display: grid;
- grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
- gap: clamp(1.5rem, 4vw, 4rem);
+ grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
+ gap: clamp(1.5rem, 4vw, 3.5rem);
align-items: start;
}
@@ -1304,3 +1304,480 @@ h4 {
align-items: flex-start;
}
}
+
+/* ----------------------------------------------------------------
+ Additions: language switcher, product card, approach, roadmap,
+ policy grid, active grid, charter list, narrow-prose, contact-note
+ ---------------------------------------------------------------- */
+
+.nav-lang-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ font-size: 0.9rem;
+ letter-spacing: 0.01em;
+ opacity: 0.85;
+}
+
+.nav-lang-link:hover,
+.nav-lang-link:focus-visible {
+ opacity: 1;
+}
+
+.nav-lang-link .material-symbols-rounded {
+ font-size: 1rem;
+}
+
+.service-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ margin-top: var(--space-3);
+ color: var(--md-primary);
+ font-weight: 600;
+ text-decoration: none;
+ border-bottom: 1px solid transparent;
+ transition: border-color 180ms ease;
+}
+
+.service-link:hover,
+.service-link:focus-visible {
+ border-bottom-color: currentColor;
+}
+
+.service-link .material-symbols-rounded {
+ font-size: 1.05rem;
+}
+
+/* Narrow prose for vision/legal/holdings */
+.narrow-prose {
+ max-width: 760px;
+}
+
+.prose + .prose {
+ margin-top: var(--space-7);
+}
+
+.prose h2 {
+ font-family: var(--font-display);
+ font-size: clamp(1.4rem, 2.6vw, 1.85rem);
+ font-weight: 700;
+ margin-bottom: var(--space-4);
+ letter-spacing: -0.01em;
+}
+
+.prose h3 {
+ font-family: var(--font-display);
+ font-size: 1.1rem;
+ font-weight: 700;
+ margin: var(--space-5) 0 var(--space-2);
+}
+
+.prose p,
+.prose li {
+ font-size: 1rem;
+ line-height: 1.85;
+ color: var(--md-on-surface-variant);
+}
+
+.prose p + p {
+ margin-top: var(--space-3);
+}
+
+.prose ul {
+ padding-left: 1.2rem;
+ margin: var(--space-2) 0;
+}
+
+.prose a {
+ color: var(--md-primary);
+ text-decoration: underline;
+ text-decoration-thickness: 1px;
+ text-underline-offset: 3px;
+}
+
+.prose a:hover,
+.prose a:focus-visible {
+ text-decoration-thickness: 2px;
+}
+
+/* Software product card */
+.product-card {
+ background: var(--md-surface-container-low);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-xl);
+ overflow: hidden;
+ box-shadow: 0 12px 36px rgba(0, 60, 64, 0.08);
+}
+
+.product-card-body {
+ padding: clamp(1.5rem, 4vw, 3rem);
+ display: grid;
+ gap: var(--space-4);
+}
+
+.product-tagline {
+ display: inline-flex;
+ align-self: start;
+ align-items: center;
+ gap: 0.4rem;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--md-primary);
+ background: var(--md-primary-container);
+ padding: 0.45rem 0.8rem;
+ border-radius: 999px;
+}
+
+.product-card-body h3 {
+ font-family: var(--font-display);
+ font-size: clamp(1.4rem, 3vw, 1.9rem);
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ margin: 0;
+}
+
+.product-feature-list {
+ display: grid;
+ gap: var(--space-2);
+ margin: var(--space-3) 0 0;
+ padding: 0;
+ list-style: none;
+}
+
+.product-feature-list li {
+ display: grid;
+ grid-template-columns: 28px minmax(0, 1fr);
+ align-items: start;
+ gap: var(--space-2);
+ font-size: 0.97rem;
+ line-height: 1.6;
+ color: var(--md-on-surface-variant);
+}
+
+.product-feature-list .material-symbols-rounded {
+ color: var(--md-primary);
+ font-size: 1.2rem;
+ margin-top: 2px;
+}
+
+.product-meta {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: var(--space-3);
+ margin-top: var(--space-3);
+ padding-top: var(--space-4);
+ border-top: 1px solid var(--md-outline-variant);
+}
+
+.product-meta dt {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--md-on-surface-variant);
+ margin-bottom: 0.3rem;
+}
+
+.product-meta dd {
+ margin: 0;
+ font-size: 0.95rem;
+ font-weight: 600;
+ color: var(--md-on-surface);
+}
+
+.product-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+ margin-top: var(--space-3);
+}
+
+.btn.disabled {
+ opacity: 0.55;
+ pointer-events: none;
+}
+
+/* Approach grid */
+.approach-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: var(--space-4);
+}
+
+.approach-item {
+ background: var(--md-surface-container-lowest);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-lg);
+ padding: var(--space-5);
+ display: grid;
+ gap: var(--space-2);
+}
+
+.approach-num {
+ font-family: var(--font-display);
+ font-size: 0.85rem;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ color: var(--md-primary);
+}
+
+.approach-item h3 {
+ font-family: var(--font-display);
+ font-size: 1.1rem;
+ font-weight: 700;
+ margin: 0;
+}
+
+.approach-item p {
+ font-size: 0.95rem;
+ line-height: 1.7;
+ color: var(--md-on-surface-variant);
+ margin: 0;
+}
+
+/* Roadmap list */
+.roadmap-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: grid;
+ gap: var(--space-3);
+}
+
+.roadmap-list li {
+ display: grid;
+ grid-template-columns: minmax(110px, max-content) minmax(0, 1fr);
+ align-items: start;
+ gap: var(--space-4);
+ padding: var(--space-4) var(--space-5);
+ background: var(--md-surface-container-lowest);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-lg);
+}
+
+.roadmap-list p {
+ margin: 0;
+ font-size: 0.97rem;
+ line-height: 1.65;
+ color: var(--md-on-surface);
+}
+
+.roadmap-tag {
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--md-primary);
+ background: var(--md-primary-container);
+ padding: 0.35rem 0.7rem;
+ border-radius: 999px;
+ text-align: center;
+ align-self: start;
+}
+
+/* Holdings policy grid */
+.policy-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: var(--space-4);
+}
+
+.policy-item {
+ background: var(--md-surface-container-lowest);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-lg);
+ padding: var(--space-5);
+ display: grid;
+ gap: var(--space-2);
+}
+
+.policy-num {
+ font-family: var(--font-display);
+ font-size: 0.85rem;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ color: var(--md-primary);
+}
+
+.policy-item h3 {
+ font-family: var(--font-display);
+ font-size: 1.1rem;
+ font-weight: 700;
+ margin: 0;
+}
+
+.policy-item p {
+ font-size: 0.95rem;
+ line-height: 1.7;
+ color: var(--md-on-surface-variant);
+ margin: 0;
+}
+
+/* Business page: active items and charter list */
+.active-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: var(--space-4);
+}
+
+.active-item {
+ background: var(--md-surface);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-lg);
+ padding: var(--space-5);
+ display: grid;
+ gap: var(--space-2);
+ position: relative;
+}
+
+.active-icon {
+ display: inline-grid;
+ place-items: center;
+ width: 48px;
+ height: 48px;
+ border-radius: var(--radius-md);
+ background: var(--md-primary-container);
+ color: var(--md-primary);
+}
+
+.active-icon .material-symbols-rounded {
+ font-size: 1.6rem;
+}
+
+.active-item h3 {
+ font-family: var(--font-display);
+ font-size: 1.15rem;
+ font-weight: 700;
+ margin: var(--space-1) 0 0;
+}
+
+.active-item p {
+ font-size: 0.95rem;
+ line-height: 1.65;
+ color: var(--md-on-surface-variant);
+ margin: 0;
+}
+
+.active-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ margin-top: var(--space-2);
+ color: var(--md-primary);
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.active-link:hover,
+.active-link:focus-visible {
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
+
+.charter-list {
+ display: grid;
+ gap: var(--space-4);
+}
+
+.charter-group {
+ background: var(--md-surface-container-lowest);
+ border: 1px solid var(--md-outline-variant);
+ border-radius: var(--radius-lg);
+ padding: var(--space-5);
+}
+
+.charter-group.is-active {
+ border-color: var(--md-primary);
+ box-shadow: 0 0 0 1px var(--md-primary) inset;
+ background: var(--md-surface-container-low);
+}
+
+.charter-group header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ margin-bottom: var(--space-3);
+}
+
+.charter-letter {
+ display: inline-grid;
+ place-items: center;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: var(--md-primary-container);
+ color: var(--md-primary);
+ font-family: var(--font-display);
+ font-weight: 800;
+ font-size: 0.95rem;
+}
+
+.charter-group header h3 {
+ font-family: var(--font-display);
+ font-size: 1.05rem;
+ font-weight: 700;
+ margin: 0;
+ flex: 1;
+}
+
+.charter-badge {
+ font-size: 0.7rem;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: #ffffff;
+ background: var(--md-primary);
+ padding: 0.3rem 0.65rem;
+ border-radius: 999px;
+}
+
+.charter-group ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: grid;
+ gap: var(--space-2);
+}
+
+.charter-group li {
+ font-size: 0.95rem;
+ line-height: 1.7;
+ color: var(--md-on-surface-variant);
+ padding-left: 1.1rem;
+ position: relative;
+}
+
+.charter-group li::before {
+ content: "·";
+ position: absolute;
+ left: 0;
+ top: 0;
+ color: var(--md-primary);
+ font-weight: 700;
+}
+
+.contact-note {
+ margin-top: var(--space-5);
+ padding: var(--space-4) var(--space-5);
+ background: var(--md-surface-container-lowest);
+ border: 1px dashed var(--md-outline-variant);
+ border-radius: var(--radius-md);
+ font-size: 0.9rem;
+ color: var(--md-on-surface-variant);
+ line-height: 1.7;
+}
+
+@media (max-width: 720px) {
+ .roadmap-list li {
+ grid-template-columns: 1fr;
+ gap: var(--space-2);
+ }
+
+ .charter-group header {
+ flex-wrap: wrap;
+ }
+}
+
diff --git a/ether/templates/base.html b/ether/templates/base.html
index adeb74a..05d5fe1 100644
--- a/ether/templates/base.html
+++ b/ether/templates/base.html
@@ -1,22 +1,68 @@
-{% load static i18n %}
+{% load static %}
-{% get_current_language as LANGUAGE_CODE %}
-
+
{% block title %}
- Ether合同会社
+ {% if lang == 'en' %}
+ Ether LLC
+ {% else %}
+ Ether合同会社
+ {% endif %}
{% endblock title %}
+ content="{% block meta_description %}{% if lang == 'en' %}Ether LLC is a Tokyo-based Web3 holding company centered on Ethereum, building software products and supporting culture-meets-technology projects.{% else %}Ether合同会社は、東京を拠点に、Ethereumを基軸資産としてソフトウェアと文化事業を進めるWeb3ホールディングです。{% endif %}{% endblock meta_description %}" />
+ {% if alt_lang_url %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
{% block css %}
@@ -40,15 +86,28 @@
{% block body %}
- 本文へスキップ
+
+ {% if lang == 'en' %}
+ Skip to content
+ {% else %}
+ 本文へスキップ
+ {% endif %}
+
会社概要
- 公開情報として必要な会社基本情報を掲載しています。
+ 登記情報および公開可能な範囲の基本情報を掲載しています。
@@ -26,7 +26,16 @@ 会社概要
商号
- Ether合同会社
+ Ether合同会社 (Ether LLC)
+
+
+
+
+ badge
+ 代表社員 / 業務執行社員
+
+
+ 増田 創 (Soh Masuda)
@@ -35,16 +44,36 @@
会社概要
本店所在地
- 東京都新宿区
+ 〒160-0022 東京都新宿区新宿四丁目3番15号
- campaign
- 公告の方法
+ event
+ 設立年月日
- 官報に掲載
+ 令和7年12月22日 (2025-12-22)
+
+
+
+
+ savings
+ 資本金
+
+
+ 1円
+
+
+
@@ -56,7 +85,54 @@
会社概要
毎年4月1日から翌年3月31日まで
+
+
+ campaign
+ 公告の方法
+
+
+ 官報に掲載
+
+
+
+
+ database
+ 基軸資産
+
+
+ Ethereum (ETH) — 詳細はHoldings
+
+
+
+
+
+
+
+ 事業目的
+
+ 定款 第2条において、23項目の事業目的を定めています。
+ 領域別に整理した一覧は事業内容 のページをご覧ください。
+
+
+
+
{% endblock content %}
diff --git a/ether/templates/pages/contact.html b/ether/templates/pages/contact.html
index 552125f..406ee23 100644
--- a/ether/templates/pages/contact.html
+++ b/ether/templates/pages/contact.html
@@ -14,7 +14,10 @@
Contact
お問い合わせ
- 協業、企画相談、事業に関するご連絡はメールでお送りください。内容を拝見のうえ、担当より折り返しご連絡いたします。
+
+ 協業、企画相談、取材、Ethereumエコシステムでの共同検討など。
+ まずはメールにてご連絡ください。内容を確認のうえ、担当より折り返しご返信します。
+
@@ -25,40 +28,76 @@ お問い合わせ
Email
- info@ether.local
- 本番ドメインと正式なメールアドレスは公開前に差し替える前提です。現在は初期構築用の仮連絡先を表示しています。
+ so.masuda.2003@ether-llc.com
+ 会社の代表メールです。すべてのお問い合わせは、まずこちらのアドレスへお送りください。
-
{% endblock content %}
diff --git a/ether/templates/pages/en/business.html b/ether/templates/pages/en/business.html
new file mode 100644
index 0000000..836ba43
--- /dev/null
+++ b/ether/templates/pages/en/business.html
@@ -0,0 +1,203 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Business | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-business
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Business
+
+
Business areas
+
+ Below is a structured view of the business purposes set out in Article 2 of our charter.
+ Areas with current, active operations are marked.
+
+
+
+
+
+
+
+
+ Active
+
+
Currently active
+
Right now, the company is concretely active in Ethereum holdings and software.
+
+
+
+
+
+
+
+
+
+ Charter
+
+
Business purposes from the charter
+
A wide range of activities is reserved in Article 2 to keep room for future expansion.
+
+
+
+
+ A
+ Planning, production, sale and import/export of goods
+
+
+ Recorded music products (vinyl records, compact discs, etc.)
+ Music records
+ Character merchandise (IP-related goods, apparel, stationery, accessories, etc.)
+ Computer software
+ Apparel, daily goods, bags, leather goods, jewellery
+ Food and beverages (including alcoholic beverages)
+ Dyes, pigments, paints, pharmaceuticals, cosmetics and their raw materials
+ Printed materials, publications, video media
+
+
+
+
+ B
+ Venues and retail
+
+
+ Operation of restaurants and live music venues
+ Retail of records, CDs, video tapes and other audio/video media
+ Sale of audio equipment
+
+
+
+
+ C
+ Logistics, leasing and second-hand goods
+
+
+ Warehousing, freight forwarding and agency services
+ Sale, lease, rental of movable property and brokerage thereof
+ Trade of precious metals, jewels, art and second-hand goods
+
+
+
+
+ D
+ Intellectual property and IT services
+ Active
+
+
+
+ Acquisition, lease, planning, development, maintenance and management of
+ industrial property rights, know-how, copyrights and other intangible property,
+ system engineering, software, Web3 services, blockchain technology, crypto-assets,
+ NFTs and other digital assets
+
+ Information processing/provision, telecommunications, broadcasting and program supply
+ Advertising and advertising agency
+
+
+
+
+ E
+ Culture and education
+
+
+ Planning, organising and execution of events
+ Production and sale of educational books and materials, and operation of cultural classes
+
+
+
+
+ F
+ Professional services
+
+
+ Outsourced accounting, financial document processing, payroll calculation
+ Collection, analysis and provision of credit information
+ Statutory inspection and maintenance of building equipment, and related agency/intermediary services
+ Consulting on corporate management and administration
+
+
+
+
+ G
+ Real estate and construction
+
+
+ Real estate leasing, sales, development and management; warehousing
+ Construction, civil engineering, equipment, interior/exterior works (contracting, design, management)
+
+
+
+
+ H
+ Ethereum and Web3
+ Active
+
+
+ Consulting on Web3 services and blockchain technology
+ Holding of Ether (ETH)
+ Operations related and ancillary to Ether (ETH)
+
+
+
+
+
+ All operations incidental or related to the above
+ Any operations not enumerated above
+
+
+
+
+
+
+
+
+
+
+
+ Next step
+
+
Cross-domain inquiries welcome
+
If your project touches any item in our charter, send us a message and we'll take a look.
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/company.html b/ether/templates/pages/en/company.html
new file mode 100644
index 0000000..8d665d5
--- /dev/null
+++ b/ether/templates/pages/en/company.html
@@ -0,0 +1,138 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Company | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-company
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Company
+
+
Company facts
+
Registered information and publicly disclosable facts about the company.
+
+
+
+
+
+
+
+ domain
+ Legal name
+
+
+ Ether合同会社 (Ether LLC)
+
+
+
+
+ badge
+ Representative / Executive member
+
+
+ Soh Masuda (増田 創)
+
+
+
+
+ location_on
+ Registered office
+
+
+ 4-3-15 Shinjuku, Shinjuku-ku, Tokyo 160-0022, Japan
+
+
+
+
+ event
+ Founded
+
+
+ 22 December 2025 (Reiwa 7)
+
+
+
+
+ savings
+ Capital
+
+
+ JPY 1
+
+
+
+
+
+ calendar_month
+ Fiscal year
+
+
+ April 1 to March 31
+
+
+
+
+ campaign
+ Method of public notice
+
+
+ Publication in the Official Gazette (Kanpo)
+
+
+
+
+ database
+ Reserve asset
+
+
+ Ethereum (ETH) — see Holdings
+
+
+
+
+
+
+
+
+
+
+ Business purposes
+
+ Article 2 of our charter sets out 23 specific business purposes.
+ A grouped overview is available on the Business page.
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/contact.html b/ether/templates/pages/en/contact.html
new file mode 100644
index 0000000..dc62ef5
--- /dev/null
+++ b/ether/templates/pages/en/contact.html
@@ -0,0 +1,106 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Contact | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-contact
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Contact
+
+
Get in touch
+
+ For partnerships, project briefs, press, or Ethereum ecosystem collaboration —
+ please email us. We read each message and reply within a few business days.
+
+
+
+
+
+
+
+
+ Replies may take a few business days.
+ We do not respond to mass-broadcast sales outreach.
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/holdings.html b/ether/templates/pages/en/holdings.html
new file mode 100644
index 0000000..28d8fa7
--- /dev/null
+++ b/ether/templates/pages/en/holdings.html
@@ -0,0 +1,139 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Holdings | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-holdings
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Holdings
+
+
Anchored in Ethereum, held for the long term.
+
+ Ether LLC treats Ethereum (ETH) as the company's reserve asset.
+ We hold it as a long-term commitment to the protocol, not as a trading position.
+ This page documents the policy and the assumptions behind it.
+
+
+
+
+
+
+
+
+ Policy
+
+
Holding policy
+
We don't trade. We hold — as a long-term commitment to the protocol.
+
+
+
+ 01
+ Long-term horizon
+
+ ETH is held as a reserve over multi-year (and ideally multi-decade) horizons.
+ We do not trade against daily or weekly price movements.
+
+
+
+ 02
+ Self-funded only
+
+ Our ETH position is acquired and operated entirely with the company's own capital.
+ We do not custody third-party funds, run an investment vehicle, or operate
+ a crypto exchange business.
+
+
+
+ 03
+ Self-custody first
+
+ Reserves are held in wallets the company controls.
+ Where operationally necessary, we may use trusted custodians on a limited basis.
+
+
+
+ 04
+ Staking and participation
+
+ We evaluate Ethereum staking and participation in related protocols carefully —
+ judged on implementation and on decentralisation,
+ never as pure yield-chasing.
+
+
+
+ 05
+ Transparency
+
+ Material changes to the holding policy will be published on this page.
+ We currently do not publish wallet addresses publicly.
+
+
+
+ 06
+ Relationship to operating business
+
+ ETH is a corporate reserve that supports the long-term continuity
+ of the company's software and cultural work.
+
+
+
+
+
+
+
+
+ Why Ethereum
+
+ Ethereum is a censorship-resistant computational platform that has built
+ credibility for neutrality and reliability over many years.
+ We see its value not as a single-purpose currency,
+ but as a global application substrate.
+
+
+ Holding ETH is not a passive bet — it is the foundation we use
+ to build software products and cultural projects on top of.
+ That is why we call it a reserve.
+
+
+
+ Regulation and compliance
+
+ Our ETH holdings are corporate-treasury holdings funded by the company itself.
+ We do not operate a crypto-asset exchange business and do not custody third-party assets.
+ We comply with applicable Japanese law and tax obligations.
+
+
+ Information on this page is provided for transparency and is not a solicitation
+ to buy, sell, or invest in any security, financial product, or crypto-asset.
+
+
+
+
+
+
+
+
+
+
+ Inquiries
+
+
Ecosystem collaborations
+
Open to collaborations, technical research, and consulting work in the Ethereum ecosystem.
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/home.html b/ether/templates/pages/en/home.html
new file mode 100644
index 0000000..a6f89e1
--- /dev/null
+++ b/ether/templates/pages/en/home.html
@@ -0,0 +1,192 @@
+{% extends "base.html" %}
+
+{% load static %}
+
+{% block title %}
+ Ether LLC | Ethereum, Software, Culture
+{% endblock title %}
+{% block bodyclass %}
+ page-home
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+
+ Tokyo · Ethereum · Software · Culture
+
+
+ Anchored in Ethereum.
+
+ Building software, weaving culture.
+
+
+ Ether LLC is a Tokyo-based company that holds Ethereum as its long-term reserve asset
+ and works at the intersection of software products and cultural projects.
+
+
+
+
+
Founded
+
+ 2025-12-22
+
+
+
+
Reserve Asset
+
+ Ethereum (ETH)
+
+
+
+
Domains
+
+ Software · Web3 · Culture
+
+
+
+
+
+
+
+ database
+
+
+
Ethereum Treasury
+
ETH held as a long-term foundation, not a trading position.
+
+
+
+
+ code
+
+
+
Software
+
Quiet apps for the texture of everyday life.
+
+
+
+
+ graphic_eq
+
+
+
Culture
+
Music, publishing, events and Web3 expression.
+
+
+
+
+
+
+
+
+
+
+ Focus
+
+
Asset, tool, expression — kept on the same line.
+
+
+ Hold Ethereum as a reserve. Build software as the tool. Make music, publish, and host
+ as the expression. We don't treat these as separate departments — they share one
+ company, one direction, one quiet pace.
+
+
+
+
+
+
+
+
+ Three Pillars
+
+
Three pillars
+
The shape of the company is three layers — holdings, software and culture.
+
+
+
+
+ database
+
+ 01 — Holdings
+ Long-term Ethereum holding
+
+ Self-funded ETH reserve. Held as a long-term commitment to the protocol,
+ not as a trading position.
+
+
+ Read the policy
+ arrow_forward
+
+
+
+
+ code
+
+ 02 — Software
+ Building products
+
+ Software for individuals and quiet daily moments. Our flagship is
+ GratefulMoments , a SwiftUI gratitude journal for iOS.
+
+
+ See products
+ arrow_forward
+
+
+
+
+ graphic_eq
+
+ 03 — Culture
+ Cultural projects
+
+ Music, video, publishing, events and venues. The expressive side of a deliberately
+ broad set of business purposes.
+
+
+ See business areas
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+ Contact
+
+
Partnerships, projects and press.
+
+ Reach out for collaboration in the Ethereum ecosystem, software development,
+ or cultural projects. We read every message.
+
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/legal/cookies.html b/ether/templates/pages/en/legal/cookies.html
new file mode 100644
index 0000000..63be0a5
--- /dev/null
+++ b/ether/templates/pages/en/legal/cookies.html
@@ -0,0 +1,79 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Cookie Policy | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-legal
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Legal
+
+
Cookie Policy
+
Effective: 22 December 2025 / Last updated: 4 May 2026
+
+
+
+
+
+ 1. Cookies on this site
+
+ The website https://ether-llc.com
+ ("this Site"), operated by Ether LLC, does not currently use any first-party cookies
+ or local storage for tracking purposes.
+
+
+ However, our static hosting provider (Vercel) may set standard logs and cookies
+ for service operation and basic security.
+
+
+
+ 2. Third-party cookies
+
+ This Site loads fonts and CSS libraries from external providers
+ (such as Google Fonts and a Bootstrap CDN). These providers may collect data
+ according to their own privacy policies.
+
+
+
+
+ 3. Analytics
+
+ This Site does not currently use third-party analytics tools such as Google Analytics.
+ If we introduce them in the future, we will update this policy and our
+ Privacy Policy and notify users on this Site.
+
+
+
+ 4. Managing cookies
+
+ Most browsers let you accept, reject, or delete cookies and configure per-site behaviour.
+ You can use these controls to refuse or remove cookies related to this Site.
+ Disabling cookies will not prevent you from reading the public pages of this Site.
+
+
+
+ 5. Contact
+
+ For questions about this policy, please contact
+ so.masuda.2003@ether-llc.com .
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/legal/privacy.html b/ether/templates/pages/en/legal/privacy.html
new file mode 100644
index 0000000..4bd6593
--- /dev/null
+++ b/ether/templates/pages/en/legal/privacy.html
@@ -0,0 +1,107 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Privacy Policy | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-legal
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Legal
+
+
Privacy Policy
+
Effective: 22 December 2025 / Last updated: 4 May 2026
+
+
+
+
+
+ 1. Operator
+
+ Ether LLC (Ether合同会社; "the Company") complies with the Japanese Act on the
+ Protection of Personal Information and related laws.
+ This Privacy Policy describes how personal information is handled on the website
+ https://ether-llc.com ("this Site").
+
+
+ Operator: Ether LLC, 4-3-15 Shinjuku, Shinjuku-ku, Tokyo 160-0022, Japan.
+ Corporate number: 4011103016903.
+
+
+
+ 2. Information collected
+ This Site may collect the following information:
+
+ Information that you provide via email (e.g. name, email address, organisation)
+ Standard request logs (IP address, user agent, referrer, request time)
+ Standard logs collected automatically by our static hosting provider (Vercel)
+
+
+ This Site does not currently use first-party cookies or third-party analytics.
+ If we adopt them in the future, we will update this policy and our
+ Cookie Policy accordingly.
+
+
+
+ 3. Purposes of use
+ Information collected is used only within the following purposes:
+
+ Responding to inquiries and any necessary follow-up communication
+ Operating, maintaining, improving, and securing this Site
+ Aggregate, non-identifying analysis of usage
+ Compliance with applicable law
+
+
+
+ 4. Disclosure to third parties
+
+ The Company will not disclose personal information to third parties without consent,
+ except as required by law.
+ The Company may, however, engage cloud service providers as data processors
+ to host and deliver this Site.
+
+
+
+ 5. Subprocessors
+ The Company uses the following subprocessor:
+
+ Vercel Inc. (United States) — static hosting and CDN
+
+
+
+ 6. Security
+
+ The Company applies reasonable technical and organisational measures to prevent
+ loss, leakage, and unauthorised use of personal information.
+
+
+
+ 7. Your rights
+
+ You may request disclosure, correction, suspension of use, or deletion of your
+ personal information held by the Company. Please contact us using the details below.
+ We will respond within a reasonable period after verifying your identity.
+
+
+
+ 8. Contact
+
+ Ether LLC — Privacy inquiries
+
+ Email: so.masuda.2003@ether-llc.com
+
+
+
+ 9. Updates
+
+ The Company may update this policy at any time in response to legal changes or
+ operational changes. Material updates will be announced on this Site.
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/software.html b/ether/templates/pages/en/software.html
new file mode 100644
index 0000000..51503e1
--- /dev/null
+++ b/ether/templates/pages/en/software.html
@@ -0,0 +1,211 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Software | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-software
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Software
+
+
Quiet apps for everyday life.
+
+ We treat software products as instruments for testing ideas.
+ Each app is designed, written and operated in-house, and aims to sit gently
+ inside one person's day.
+
+
+
+
+
+
+
+
+ Flagship
+
+
GratefulMoments
+
A SwiftUI gratitude journal for iOS — capture the small good moments of your day.
+
+
+
+
SwiftUI · SwiftData · Apple Intelligence
+
A quiet way to keep small good moments
+
+ Save the moments you're grateful for with a title, a note, and a photo.
+ Streak counts and badges make the habit easy to keep without pressure.
+ On iOS 26 with Apple Intelligence enabled, you can also reflect on your past
+ entries through a chat-based conversation.
+
+
+
+ edit_note
+ Gratitude journal: capture moments with a title, note, and photo
+
+
+ photo_library
+ Photo support: attach images via PhotosPicker
+
+
+ hexagon
+ Hexagon UI: a custom HexagonLayout to visualise entries
+
+
+ local_fire_department
+ Streaks: track consecutive days of journaling
+
+
+ workspace_premium
+ Badge system: unlock badges as you reach milestones
+
+
+ psychology
+ Reflection: chat with your past entries via Apple Intelligence
+
+
+ storage
+ SwiftData persistence: stored locally on device
+
+
+
+
+
Platform
+
+ iOS 18.6+ (Reflection requires iOS 26+)
+
+
+
+
Stack
+
+ Swift / SwiftUI / SwiftData / PhotosUI
+
+
+
+
License
+
+ Apache License 2.0 (open source)
+
+
+
+
Status
+
+ Active development / preparing for App Store release
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Approach
+
+
How we build
+
+
+
+ 01
+ Personal, not platform
+
+ We don't build large-scale platforms. We build apps that try to live quietly
+ inside one person's day.
+
+
+
+ 02
+ Long-term care
+
+ Once an app ships, we tend to it slowly over years.
+ We aim for products that stay rather than products that trend.
+
+
+
+ 03
+ Open source by default
+
+ Where possible, we publish under permissive licenses (e.g. Apache 2.0)
+ and try to give back to the technical community.
+
+
+
+ 04
+ Try new APIs early
+
+ We adopt new platform APIs (such as Apple Intelligence) early — but only
+ for narrow, real use cases.
+
+
+
+
+
+
+
+
+
+
+ Roadmap
+
+
What's next
+
Short-term work in progress. Details land here as they're decided.
+
+
+
+ In progress
+ Polishing GratefulMoments for App Store review
+
+
+ Planned
+ Companion iOS widgets for the gratitude journal experience
+
+
+ Exploring
+ Optional Web3 hooks (on-chain anchoring, attestation-style NFTs)
+
+
+
+
+
+
+
+
+
+
+ Collaborate
+
+
Software collaborations
+
For SwiftUI / Web3 collaborations, licensing, or technical inquiries — please get in touch.
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/en/vision.html b/ether/templates/pages/en/vision.html
new file mode 100644
index 0000000..821661b
--- /dev/null
+++ b/ether/templates/pages/en/vision.html
@@ -0,0 +1,111 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Vision | Ether LLC
+{% endblock title %}
+{% block bodyclass %}
+ page-vision
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Vision
+
+
Ether — Infrastructure with breathing room
+
Why we started Ether, in the language of the company name and our charter.
+
+
+
+
+
+ About the name
+
+ The name Ether carries two meanings at once.
+ One is ETH, the native asset of the Ethereum protocol.
+ The other is aether , the long-discarded medium that classical physics
+ once imagined would carry light and information through space.
+
+
+ The aether was eventually rejected as nonexistent.
+ And yet, in our time, blockchains have quietly reintroduced
+ a substrate that carries information across networks.
+ The company name is a small bow toward that overlap.
+
+
+
+ Three stances
+ 1. Hold the asset for the long term
+
+ We hold Ethereum to keep it, not to trade it.
+ Our position is not a bet on price — it is a multi-year commitment
+ to the problems the protocol is trying to solve.
+
+ 2. Build the tools by hand
+
+ Software products are how we test ideas.
+ We design, write, and operate apps that try to sit quietly inside daily life,
+ and we keep this work in-house rather than outsourcing it.
+ Our first product is GratefulMoments ,
+ a SwiftUI gratitude journal for iOS.
+
+ 3. Express only as much as needed
+
+ Music, publishing, events, and Web3 cultural work are the expressive layer
+ on top of the asset and the tools.
+ Our charter lists twenty-three business purposes,
+ but we only foreground on this site the parts that are actually moving.
+
+
+
+ Infrastructure with breathing room
+
+ Web3, software, and culture are all crowded fields.
+ Plenty of larger, faster, louder players already exist in each.
+ Our edge is not scale — it is breathing room.
+
+
+ We don't rush, and we don't sprawl.
+ We try to operate at a pace one person and one company can sustain
+ for a long time, while building things on top of Ethereum
+ that genuinely deserve to exist.
+
+
+
+ About the operator
+
+ Representative member: Soh Masuda (増田 創).
+ A software engineer who has been writing code as an individual developer for years
+ and incorporated Ether LLC in Tokyo in December 2025.
+ See Company for legal facts,
+ and Contact to reach us.
+
+
+
+
+
+
+
+
+
+
+ Next
+
+
Three pillars, one at a time.
+
Each pillar — Holdings, Software, and Culture — has its own page with current details and policy.
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/holdings.html b/ether/templates/pages/holdings.html
new file mode 100644
index 0000000..93b0f12
--- /dev/null
+++ b/ether/templates/pages/holdings.html
@@ -0,0 +1,139 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Holdings | Ether合同会社
+{% endblock title %}
+{% block bodyclass %}
+ page-holdings
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Holdings
+
+
Ethereumを基軸に、長く持つ。
+
+ Ether合同会社は、Ethereum (ETH) を会社の基軸資産として位置づけ、
+ 投機ではなく長期コミットメントとして保有しています。
+ 本ページでは、その方針と前提を公開しています。
+
+
+
+
+
+
+
+
+ Policy
+
+
保有方針
+
短期売買を目的とせず、プロトコルへの長期コミットメントとして保有します。
+
+
+
+ 01
+ 長期保有が前提
+
+ ETHは複数年〜数十年の単位で保有することを前提とした基軸資産です。
+ 日次・週次の価格変動を目的とした売買は行いません。
+
+
+
+ 02
+ 自己資金のみ
+
+ Ether合同会社のETH保有は、自己資金により取得・運用しています。
+ 第三者からの預託、投資信託の組成、暗号資産交換業の提供は行いません。
+
+
+
+ 03
+ セルフカストディ
+
+ 原則として保有資産は自社管理のウォレットで保管します。
+ 運用上の必要に応じて、信頼できるカストディを限定的に併用する場合があります。
+
+
+
+ 04
+ ステーキング・参加
+
+ Ethereumのステーキングや関連プロトコルへの参加は、
+ 実装内容と分散性を確認したうえで段階的に検討します。
+ 投機的な利回りそのものを目的とはしません。
+
+
+
+ 05
+ 透明性
+
+ 必要に応じて保有方針・運用上の重要な変更を本ページで公開します。
+ 個別ウォレットアドレスの公表は、現時点では行っていません。
+
+
+
+ 06
+ 会社事業との関係
+
+ ETHは事業基盤を支える会社資産であり、
+ ソフトウェア開発・文化事業の継続性を確保するためのバッファとして機能します。
+
+
+
+
+
+
+
+
+ なぜEthereumなのか
+
+ Ethereumは、検閲耐性のある計算プラットフォームとして、
+ 長期にわたり中立性と信頼性を高めてきました。
+ 単一の通貨としてではなく、グローバルなアプリケーション基盤として、
+ 私たちは価値を見出しています。
+
+
+ ETHを単に投資対象として持つのではなく、
+ 自社の事業(ソフトウェア・文化)を、
+ このオープンな基盤の上に静かに積み上げていく。
+ そのための「基軸資産」としてETHを保有しています。
+
+
+
+ 規制とコンプライアンス
+
+ 当社のETH保有は、自己資金による会社資産の保有であり、
+ 顧客資産の預託・運用を伴う暗号資産交換業ではありません。
+ 国内法令に従って必要な届出・税務処理を行います。
+
+
+ 本ページの内容は情報提供を目的としたものであり、
+ 特定の投資勧誘や金融商品の販売・募集を行うものではありません。
+
+
+
+
+
+
+
+
+
+
+ Inquiries
+
+
エコシステム協業のご相談
+
Ethereumエコシステムでの共同企画、技術検証、コンサルティングのご相談はお気軽にどうぞ。
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/home.html b/ether/templates/pages/home.html
index 5bb4a00..171a010 100644
--- a/ether/templates/pages/home.html
+++ b/ether/templates/pages/home.html
@@ -3,7 +3,7 @@
{% load static %}
{% block title %}
- Ether合同会社 | Music, Software and Web3
+ Ether合同会社 | Ethereum, Software, Culture
{% endblock title %}
{% block bodyclass %}
page-home
@@ -14,20 +14,23 @@
- Tokyo · Music · Software · Web3
+ Tokyo · Ethereum · Software · Culture
- 文化と技術を、
+ Ethereumを基軸に、
- 静かに前へ。
+ ソフトウェアと文化を編む。
-
Ether合同会社は、音楽カルチャー、イベント、ソフトウェア、Web3を横断し、企画から実装までを丁寧に進める組織です。
+
+ Ether合同会社は、Ethereumを長期保有資産とし、ソフトウェアプロダクトの開発と
+ 文化事業を通じて、技術と表現の交点を静かに形にしていく東京の会社です。
+
-
- 事業内容を見る
+
+ 会社のビジョンを読む
arrow_forward
-
+
mail
お問い合わせ
@@ -36,19 +39,19 @@
Founded
- Tokyo, Japan
+ 2025-12-22
-
Domains
+ Reserve Asset
- Music · Events · Software · Web3
+ Ethereum (ETH)
-
Approach
+ Domains
- Calm, considered, hands-on
+ Software · Web3 · Culture
@@ -56,29 +59,29 @@
- graphic_eq
+ database
-
Music & Media
-
レコード、映像、出版を横断する企画と制作。
+
Ethereum Treasury
+
ETHを長期保有し、事業の基盤資産とする。
- stadium
+ code
-
Live Culture
-
イベントと場所づくり。
+
Software
+
個と日常を支えるアプリの設計・開発。
- hub
+ graphic_eq
-
Web3
-
分散システムの設計と運用。
+
Culture
+
音楽、出版、イベント、Web3の表現。
@@ -91,56 +94,71 @@ Web3
Focus
- 次のレイヤーのカルチャーとインフラ。
+ 資産・道具・表現、を地続きに。
- Ether合同会社は、音楽・映像メディア、ライブイベント、ソフトウェア、ブロックチェーン技術を扱う会社です。
- 表現と技術の接点にあるプロジェクトを、企画、開発、運用、コンサルティングの面から支えます。
+ Ethereumを基軸資産として持ち、ソフトウェアプロダクトを道具として作り、
+ 音楽や出版や場づくりとして表現する。三つを別ものとして扱わず、
+ ひとつの会社の中で接続することが、Ether合同会社のやり方です。
-
+
- Business Areas
+ Three Pillars
-
主な事業領域
-
定款に定める幅広い事業のうち、サイト上では4つの領域を中心に紹介しています。
+
三つの柱
+
事業の輪郭は、保有・開発・文化の三層で構成されています。
- graphic_eq
-
- 01 — Music & Media
- 音楽・映像・出版
- 音楽レコード、コンパクトディスク、映像物、出版物などの企画・制作・販売。
-
-
-
- stadium
+ database
- 02 — Live Culture
- イベント・場づくり
- イベントの企画・運営、飲食店およびライブハウスの経営に関わる事業。
+ 01 — Holdings
+ Ethereumの長期保有
+
+ ETHを基軸とした自己資金運用。短期売買ではなく、
+ プロトコルそのものへの長期コミットメントとして保有しています。
+
+
+ 方針を見る
+ arrow_forward
+
code
- 03 — Software & Data
- ソフトウェア・情報
- ソフトウェア、情報処理、広告、教育コンテンツ、企業管理領域の企画・提供。
+ 02 — Software
+ プロダクトの開発
+
+ 日々の小さな前向きさを支えるiOSアプリ
+ GratefulMoments を中心に、
+ 個と日常のためのソフトウェアを開発しています。
+
+
+ プロダクトを見る
+ arrow_forward
+
- hub
+ graphic_eq
- 04 — Web3
- Web3・ブロックチェーン
- Web3サービス、ブロックチェーン技術、Ethereum関連領域の企画・開発・助言。
+ 03 — Culture
+ 文化事業の運営
+
+ 音楽・映像・出版・イベント・場づくり。
+ 定款で広く定めた事業領域のうち、表現と人が集まる軸の活動です。
+
+
+ 事業内容を見る
+ arrow_forward
+
@@ -153,17 +171,20 @@ Web3・ブロックチェーン
Contact
- 協業、企画相談、事業に関するお問い合わせ
- 表現と技術の交点にあるプロジェクトについて、お気軽にご連絡ください。
+ 協業・企画・取材のご相談
+
+ Ethereumエコシステムでの協業、ソフトウェアの開発、文化事業の企画など。
+ お気軽にご連絡ください。
+
diff --git a/ether/templates/pages/legal/cookies.html b/ether/templates/pages/legal/cookies.html
new file mode 100644
index 0000000..72bf0a4
--- /dev/null
+++ b/ether/templates/pages/legal/cookies.html
@@ -0,0 +1,81 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Cookieポリシー | Ether合同会社
+{% endblock title %}
+{% block bodyclass %}
+ page-legal
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Legal
+
+
Cookieポリシー
+
制定日: 2025年12月22日 / 最終改定日: 2026年5月4日
+
+
+
+
+
+ 1. 当サイトでのCookie利用について
+
+ Ether合同会社が運営する https://ether-llc.com (以下「本サイト」といいます)は、
+ 現時点で独自のCookieやローカルストレージを利用したトラッキングを行っていません。
+
+
+ ただし、本サイトのホスティングを担うVercel Inc.が、
+ サービス提供および基本的なセキュリティ確保のために
+ 標準的なログ・Cookieを設定する場合があります。
+
+
+
+ 2. 第三者によるCookie
+
+ 本サイトでは、外部から読み込まれるフォントおよびCSSライブラリ
+ (Google Fonts、Bootstrap CDN 等) のリクエストが行われます。
+ これらの提供事業者は、それぞれのプライバシーポリシーに従い
+ 独自にデータを取得する可能性があります。
+
+
+
+
+ 3. アクセス解析
+
+ 本サイトでは、現時点でGoogle Analytics等のサードパーティ製のアクセス解析ツールを
+ 利用していません。今後導入する場合は、本ポリシーおよびプライバシーポリシーを更新し、
+ 適切な方法でお知らせします。
+
+
+
+ 4. Cookieの管理
+
+ 多くのブラウザは、Cookieの保存可否やサイト別の設定を変更する機能を提供しています。
+ ブラウザの設定により、本サイトに関連するCookieを拒否・削除することが可能です。
+ Cookieを無効にした場合でも、本サイトの公開ページの主要機能はご利用いただけます。
+
+
+
+ 5. お問い合わせ
+
+ 本ポリシーに関するご質問は、
+ so.masuda.2003@ether-llc.com
+ までご連絡ください。
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/legal/privacy.html b/ether/templates/pages/legal/privacy.html
new file mode 100644
index 0000000..eaef3a5
--- /dev/null
+++ b/ether/templates/pages/legal/privacy.html
@@ -0,0 +1,102 @@
+{% extends "base.html" %}
+
+{% block title %}
+ プライバシーポリシー | Ether合同会社
+{% endblock title %}
+{% block bodyclass %}
+ page-legal
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Legal
+
+
プライバシーポリシー
+
制定日: 2025年12月22日 / 最終改定日: 2026年5月4日
+
+
+
+
+
+ 1. 事業者情報
+
+ Ether合同会社(以下「当社」といいます)は、個人情報の保護に関する法律(個人情報保護法)
+ および関連法令を遵守し、当社が運営するウェブサイト
+ https://ether-llc.com
+ (以下「本サイト」といいます)における個人情報の取扱いについて、
+ 本プライバシーポリシーを定めます。
+
+ 事業者: Ether合同会社(東京都新宿区新宿四丁目3番15号、法人番号 4011103016903)
+
+
+ 2. 収集する情報
+ 本サイトでは、以下の情報を取得することがあります。
+
+ お問い合わせメールに記載された氏名・メールアドレス・所属組織等
+ 本サイト閲覧時のアクセスログ(IPアドレス、ユーザーエージェント、参照元、訪問時刻等)
+ 静的ホスティング事業者(Vercel)が自動的に取得する標準的なリクエストログ
+
+
+ 当社は本サイト上で、独自のクッキーやサードパーティ製のアクセス解析ツールを
+ 現時点では使用していません。導入する場合は、別途クッキーポリシー を更新します。
+
+
+
+ 3. 利用目的
+ 取得した情報は、次の目的の範囲内で利用します。
+
+ お問い合わせへの回答および必要な連絡
+ 本サイトの提供・運営・改善およびセキュリティ確保
+ 利用状況の統計的把握(個人を識別できない形で)
+ 法令に基づく対応
+
+
+
+ 4. 第三者提供
+
+ 当社は、法令に基づく場合を除き、本人の同意なく個人情報を第三者に提供しません。
+ ただし、本サイトのホスティング・配信のために、
+ 国内外のクラウドサービス事業者を業務委託先として利用することがあります。
+
+
+
+ 5. 業務委託先
+ 本サイトの運営に関連して、以下の事業者にデータの保管・配信を委託しています。
+
+ Vercel Inc. (米国) — 静的ホスティングおよびCDN
+
+
+
+ 6. 安全管理措置
+
+ 当社は、取得した個人情報の漏えい、滅失、毀損を防止するため、
+ 合理的な技術的・組織的安全管理措置を講じます。
+
+
+
+ 7. 開示等の請求
+
+ ご本人からの個人情報の開示、訂正、利用停止、削除等のご請求は、
+ 下記の連絡先までお問い合わせください。本人確認のうえ、合理的期間内に対応します。
+
+
+
+ 8. お問い合わせ窓口
+
+ Ether合同会社 個人情報お問い合わせ窓口
+
+ メール: so.masuda.2003@ether-llc.com
+
+
+
+ 9. 改定
+
+ 本ポリシーは、法令の変更や本サイトの運用状況に応じて、予告なく改定することがあります。
+ 重要な変更がある場合は、本サイト上で適切な方法によりお知らせします。
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/services.html b/ether/templates/pages/services.html
deleted file mode 100644
index c02a0a0..0000000
--- a/ether/templates/pages/services.html
+++ /dev/null
@@ -1,75 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}
- 事業内容 | Ether合同会社
-{% endblock title %}
-{% block bodyclass %}
- page-services
-{% endblock bodyclass %}
-{% block content %}
-
-
-
-
- Services
-
-
事業内容
-
定款に定める幅広い事業目的のうち、公開サイトでは文化・ソフトウェア・Web3領域を中心に紹介します。
-
-
-
-
-
-
- 01
-
-
音楽・映像・出版メディア
-
レコード音盤、コンパクトディスク、映像物、印刷物、出版物など、文化的なコンテンツや関連商品の企画・制作・販売を行います。
-
-
-
- 02
-
-
イベント・ライブカルチャー
-
イベントの企画・運営・実施、飲食店やライブハウスの経営など、人が集まる場づくりに関わる事業を扱います。
-
-
-
- 03
-
-
ソフトウェア・情報サービス
-
コンピュータソフトウェア、情報処理、広告、教育コンテンツ、企業管理支援など、事業運営を支えるデジタル領域を推進します。
-
-
-
- 04
-
-
Web3・ブロックチェーン
-
Web3サービス、ブロックチェーン技術、暗号通貨、NFTその他のデジタル資産に関する企画・開発・管理・コンサルティングを行います。
-
-
-
-
-
-
-
-
-
-
-
- Next step
-
-
具体的なご相談は、お気軽に。
-
領域横断のプロジェクトでも、まずはメッセージをお送りください。
-
-
-
-
-
-{% endblock content %}
diff --git a/ether/templates/pages/software.html b/ether/templates/pages/software.html
new file mode 100644
index 0000000..c7181b0
--- /dev/null
+++ b/ether/templates/pages/software.html
@@ -0,0 +1,210 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Software | Ether合同会社
+{% endblock title %}
+{% block bodyclass %}
+ page-software
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Software
+
+
日々を支える、静かなアプリ。
+
+ Ether合同会社は、ソフトウェアプロダクトを思想を試す道具と捉えています。
+ 個と日常に寄り添うアプリを、自分たちの手で設計し、書き、運用しています。
+
+
+
+
+
+
+
+
+ Flagship
+
+
GratefulMoments
+
日々の「ありがとう」を残すiOS用の感謝日記アプリ。
+
+
+
+
SwiftUI · SwiftData · Apple Intelligence
+
毎日の小さな前向きさを、静かに積み上げる
+
+ タイトル、メモ、写真でうれしかったできごとを書き留め、
+ 連続記録日数とバッジで習慣化をやさしく支援します。
+ iOS 26以降のApple Intelligence対応端末では、
+ 書いた内容をもとにふりかえりチャットも利用できます。
+
+
+
+ edit_note
+ ありがとう日記: タイトル・メモ・写真でできごとを残す
+
+
+ photo_library
+ 写真サポート: PhotosPickerで画像を添付
+
+
+ hexagon
+ 六角形UI: HexagonLayoutでできごとを視覚化
+
+
+ local_fire_department
+ 連続記録日数: 毎日の習慣をストリークで可視化
+
+
+ workspace_premium
+ バッジシステム: 達成条件に応じてアンロック
+
+
+ psychology
+ ふりかえり: Apple Intelligenceで過去の記録を対話的に振り返る
+
+
+ storage
+ SwiftData永続化: できごととバッジをローカル保存
+
+
+
+
+
プラットフォーム
+
+ iOS 18.6以上 (ふりかえり機能はiOS 26以降)
+
+
+
+
技術スタック
+
+ Swift / SwiftUI / SwiftData / PhotosUI
+
+
+
+
ライセンス
+
+ Apache License 2.0 (オープンソース)
+
+
+
+
ステータス
+
+ 開発継続中 / App Store公開準備中
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Approach
+
+
プロダクトに対する態度
+
+
+
+ 01
+ 個に寄り添う
+
+ 大規模プラットフォームではなく、
+ 一人の人間の日常に静かに寄り添うアプリを作ります。
+
+
+
+ 02
+ 長く続ける
+
+ 一度公開したアプリは、流行ではなく定着を目指して、
+ 少しずつ手を入れ続けます。
+
+
+
+ 03
+ オープンソースを基本に
+
+ 可能なものはApache License 2.0などで公開し、
+ 技術コミュニティへ還元することを基本姿勢としています。
+
+
+
+ 04
+ 新しいAPIを早めに試す
+
+ Apple Intelligenceなど新しいプラットフォームAPIは、
+ 現実的な用途に絞って早期に組み込んで検証します。
+
+
+
+
+
+
+
+
+
+
+ Roadmap
+
+
これから
+
短期で予定している取り組み。詳細は確定次第、本ページでお知らせします。
+
+
+
+ In progress
+ GratefulMomentsのApp Store審査向け仕上げ
+
+
+ Planned
+ 感謝日記体験を補完する小さなiOSウィジェットの追加
+
+
+ Exploring
+ Web3要素(オンチェーン保存・属性付きNFTなど)との接続検証
+
+
+
+
+
+
+
+
+
+
+ Collaborate
+
+
ソフトウェア開発の協業
+
SwiftUI/Web3領域での共同企画、ライセンス、技術相談などお気軽にどうぞ。
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/pages/vision.html b/ether/templates/pages/vision.html
new file mode 100644
index 0000000..e80d506
--- /dev/null
+++ b/ether/templates/pages/vision.html
@@ -0,0 +1,110 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Vision | Ether合同会社
+{% endblock title %}
+{% block bodyclass %}
+ page-vision
+{% endblock bodyclass %}
+{% block content %}
+
+
+
+
+ Vision
+
+
Ether — 余白のあるインフラ
+
会社名と定款の言葉を起点に、なぜEtherを始めたのかをまとめています。
+
+
+
+
+
+ 会社名について
+
+ Etherという名前は、二つの意味を重ねています。
+ ひとつはイーサリアム(Ethereum)の基盤資産であるETH。
+ もうひとつは、近代物理学が一度想定していた「光や情報を運ぶ媒質」としてのEther(エーテル)です。
+
+
+ 実体としては存在しないと否定されたエーテルが、
+ いまブロックチェーンという形で再び情報を運ぶ媒体として戻ってきている。
+ 会社の名前は、その重なりに対する敬意です。
+
+
+
+ 三つの態度
+ 1. 資産は、長く持つ
+
+ Ethereumを売買するためではなく、
+ 長く持つために保有しています。
+ 価格の上下ではなく、プロトコルが解決しようとしている問題に対して
+ 時間をかけて関与する立場をとります。
+
+ 2. 道具は、自分の手で作る
+
+ ソフトウェアプロダクトは、思想を試すための道具です。
+ 人の暮らしに静かに寄り添うアプリを、
+ 外注ではなく自分の手で設計し、書き、運用します。
+ 最初のプロダクトはiOS向けの感謝日記
+ GratefulMoments です。
+
+ 3. 表現は、必要なだけ
+
+ 音楽、出版、イベント、Web3の文化活動は、
+ 資産と道具によって支えられた表現です。
+ 定款には23項目の事業目的を広く定めていますが、
+ 公開サイトでは、いま動いている軸だけを丁寧に紹介していきます。
+
+
+
+ 余白のあるインフラ
+
+ Web3、ソフトウェア、文化。それぞれの領域には、
+ すでに勢いのあるプレイヤーが多数います。
+ 私たちは、規模ではなく、余白で勝負します。
+
+
+ 急がない。広げすぎない。
+ 一人の人間と一つの会社が、長く健やかに続けられる速度を保ちながら、
+ Ethereumという基盤の上で、
+ 作るに値するものを作り続けていきます。
+
+
+
+ 運営者について
+
+ 代表社員: 増田 創(Soh Masuda)。
+ ソフトウェアエンジニアとして個人開発を続ける傍ら、
+ 2025年12月にEther合同会社を東京で設立しました。
+ 会社情報は会社概要 を、
+ 連絡はお問い合わせ からお願いします。
+
+
+
+
+
+
+
+
+
+
+ Next
+
+
三つの柱を、ひとつずつ。
+
Holdings・Software・Cultureの各ページで、より具体的な現状と方針を公開しています。
+
+
+
+
+
+{% endblock content %}
diff --git a/ether/templates/robots.txt b/ether/templates/robots.txt
new file mode 100644
index 0000000..2285e6c
--- /dev/null
+++ b/ether/templates/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+Disallow: /admin/
+Disallow: /accounts/
+Disallow: /users/
+
+Sitemap: https://ether-llc.com/sitemap.xml
diff --git a/ether/templates/sitemap.xml b/ether/templates/sitemap.xml
new file mode 100644
index 0000000..1a0335d
--- /dev/null
+++ b/ether/templates/sitemap.xml
@@ -0,0 +1,67 @@
+
+
+
+ https://ether-llc.com/
+ monthly
+ 1.0
+
+
+
+
+ https://ether-llc.com/vision/
+ yearly
+ 0.8
+
+
+
+
+ https://ether-llc.com/holdings/
+ monthly
+ 0.8
+
+
+
+
+ https://ether-llc.com/software/
+ monthly
+ 0.9
+
+
+
+
+ https://ether-llc.com/business/
+ yearly
+ 0.7
+
+
+
+
+ https://ether-llc.com/company/
+ yearly
+ 0.7
+
+
+
+
+ https://ether-llc.com/contact/
+ monthly
+ 0.8
+
+
+
+
+ https://ether-llc.com/legal/privacy/
+ yearly
+ 0.4
+
+
+
+
+ https://ether-llc.com/legal/cookies/
+ yearly
+ 0.3
+
+
+
+
diff --git a/ether/users/__init__.py b/ether/users/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/ether/users/adapters.py b/ether/users/adapters.py
deleted file mode 100644
index 611a458..0000000
--- a/ether/users/adapters.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from __future__ import annotations
-
-import typing
-
-from allauth.account.adapter import DefaultAccountAdapter
-from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
-from django.conf import settings
-
-if typing.TYPE_CHECKING:
- from allauth.socialaccount.models import SocialLogin
- from django.http import HttpRequest
-
- from ether.users.models import User
-
-
-class AccountAdapter(DefaultAccountAdapter):
- def is_open_for_signup(self, request: HttpRequest) -> bool:
- return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
-
-
-class SocialAccountAdapter(DefaultSocialAccountAdapter):
- def is_open_for_signup(
- self,
- request: HttpRequest,
- sociallogin: SocialLogin,
- ) -> bool:
- return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
-
- def populate_user(
- self,
- request: HttpRequest,
- sociallogin: SocialLogin,
- data: dict[str, typing.Any],
- ) -> User:
- """
- Populates user information from social provider info.
-
- See: https://docs.allauth.org/en/latest/socialaccount/advanced.html#creating-and-populating-user-instances
- """
- user = super().populate_user(request, sociallogin, data)
- if not user.name:
- if name := data.get("name"):
- user.name = name
- elif first_name := data.get("first_name"):
- user.name = first_name
- if last_name := data.get("last_name"):
- user.name += f" {last_name}"
- return user
diff --git a/ether/users/admin.py b/ether/users/admin.py
deleted file mode 100644
index daa04ff..0000000
--- a/ether/users/admin.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from allauth.account.decorators import secure_admin_login
-from django.conf import settings
-from django.contrib import admin
-from django.contrib.auth import admin as auth_admin
-from django.utils.translation import gettext_lazy as _
-
-from .forms import UserAdminChangeForm
-from .forms import UserAdminCreationForm
-from .models import User
-
-if settings.DJANGO_ADMIN_FORCE_ALLAUTH:
- # Force the `admin` sign in process to go through the `django-allauth` workflow:
- # https://docs.allauth.org/en/latest/common/admin.html#admin
- admin.autodiscover()
- admin.site.login = secure_admin_login(admin.site.login) # type: ignore[method-assign]
-
-
-@admin.register(User)
-class UserAdmin(auth_admin.UserAdmin):
- form = UserAdminChangeForm
- add_form = UserAdminCreationForm
- fieldsets = (
- (None, {"fields": ("email", "password")}),
- (_("Personal info"), {"fields": ("name",)}),
- (
- _("Permissions"),
- {
- "fields": (
- "is_active",
- "is_staff",
- "is_superuser",
- "groups",
- "user_permissions",
- ),
- },
- ),
- (_("Important dates"), {"fields": ("last_login", "date_joined")}),
- )
- list_display = ["email", "name", "is_superuser"]
- search_fields = ["name"]
- ordering = ["id"]
- add_fieldsets = (
- (
- None,
- {
- "classes": ("wide",),
- "fields": ("email", "password1", "password2"),
- },
- ),
- )
diff --git a/ether/users/apps.py b/ether/users/apps.py
deleted file mode 100644
index 3be0948..0000000
--- a/ether/users/apps.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.apps import AppConfig
-from django.utils.translation import gettext_lazy as _
-
-
-class UsersConfig(AppConfig):
- name = "ether.users"
- verbose_name = _("Users")
-
- def ready(self):
- """
- Override this method in subclasses to run code when Django starts.
- """
diff --git a/ether/users/context_processors.py b/ether/users/context_processors.py
deleted file mode 100644
index e2633ae..0000000
--- a/ether/users/context_processors.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.conf import settings
-
-
-def allauth_settings(request):
- """Expose some settings from django-allauth in templates."""
- return {
- "ACCOUNT_ALLOW_REGISTRATION": settings.ACCOUNT_ALLOW_REGISTRATION,
- }
diff --git a/ether/users/forms.py b/ether/users/forms.py
deleted file mode 100644
index b69e12b..0000000
--- a/ether/users/forms.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from allauth.account.forms import SignupForm
-from allauth.socialaccount.forms import SignupForm as SocialSignupForm
-from django.contrib.auth import forms as admin_forms
-from django.forms import EmailField
-from django.utils.translation import gettext_lazy as _
-
-from .models import User
-
-
-class UserAdminChangeForm(admin_forms.UserChangeForm):
- class Meta(admin_forms.UserChangeForm.Meta):
- model = User
- field_classes = {"email": EmailField}
-
-
-class UserAdminCreationForm(admin_forms.AdminUserCreationForm):
- """
- Form for User Creation in the Admin Area.
- To change user signup, see UserSignupForm and UserSocialSignupForm.
- """
-
- class Meta(admin_forms.UserCreationForm.Meta):
- model = User
- fields = ("email",)
- field_classes = {"email": EmailField}
- error_messages = {
- "email": {"unique": _("This email has already been taken.")},
- }
-
-
-class UserSignupForm(SignupForm):
- """
- Form that will be rendered on a user sign up section/screen.
- Default fields will be added automatically.
- Check UserSocialSignupForm for accounts created from social.
- """
-
-
-class UserSocialSignupForm(SocialSignupForm):
- """
- Renders the form when user has signed up using social accounts.
- Default fields will be added automatically.
- See UserSignupForm otherwise.
- """
diff --git a/ether/users/managers.py b/ether/users/managers.py
deleted file mode 100644
index d8beaa4..0000000
--- a/ether/users/managers.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import TYPE_CHECKING
-
-from django.contrib.auth.hashers import make_password
-from django.contrib.auth.models import UserManager as DjangoUserManager
-
-if TYPE_CHECKING:
- from .models import User # noqa: F401
-
-
-class UserManager(DjangoUserManager["User"]):
- """Custom manager for the User model."""
-
- def _create_user(self, email: str, password: str | None, **extra_fields):
- """
- Create and save a user with the given email and password.
- """
- if not email:
- msg = "The given email must be set"
- raise ValueError(msg)
- email = self.normalize_email(email)
- user = self.model(email=email, **extra_fields)
- user.password = make_password(password)
- user.save(using=self._db)
- return user
-
- def create_user(self, email: str, password: str | None = None, **extra_fields): # type: ignore[override]
- extra_fields.setdefault("is_staff", False)
- extra_fields.setdefault("is_superuser", False)
- return self._create_user(email, password, **extra_fields)
-
- def create_superuser(self, email: str, password: str | None = None, **extra_fields): # type: ignore[override]
- extra_fields.setdefault("is_staff", True)
- extra_fields.setdefault("is_superuser", True)
-
- if extra_fields.get("is_staff") is not True:
- msg = "Superuser must have is_staff=True."
- raise ValueError(msg)
- if extra_fields.get("is_superuser") is not True:
- msg = "Superuser must have is_superuser=True."
- raise ValueError(msg)
-
- return self._create_user(email, password, **extra_fields)
diff --git a/ether/users/migrations/0001_initial.py b/ether/users/migrations/0001_initial.py
deleted file mode 100644
index 397d150..0000000
--- a/ether/users/migrations/0001_initial.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import django.contrib.auth.models
-import django.contrib.auth.validators
-import django.utils.timezone
-from django.db import migrations
-from django.db import models
-
-import ether.users.models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ("auth", "0012_alter_user_first_name_max_length"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="User",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("password", models.CharField(max_length=128, verbose_name="password")),
- (
- "last_login",
- models.DateTimeField(
- blank=True, null=True, verbose_name="last login",
- ),
- ),
- (
- "is_superuser",
- models.BooleanField(
- default=False,
- help_text="Designates that this user has all permissions without explicitly assigning them.",
- verbose_name="superuser status",
- ),
- ),
- (
- "email",
- models.EmailField(
- unique=True, max_length=254, verbose_name="email address",
- ),
- ),
- (
- "is_staff",
- models.BooleanField(
- default=False,
- help_text="Designates whether the user can log into this admin site.",
- verbose_name="staff status",
- ),
- ),
- (
- "is_active",
- models.BooleanField(
- default=True,
- help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
- verbose_name="active",
- ),
- ),
- (
- "date_joined",
- models.DateTimeField(
- default=django.utils.timezone.now, verbose_name="date joined",
- ),
- ),
- (
- "name",
- models.CharField(
- blank=True, max_length=255, verbose_name="Name of User",
- ),
- ),
- (
- "groups",
- models.ManyToManyField(
- blank=True,
- help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
- related_name="user_set",
- related_query_name="user",
- to="auth.Group",
- verbose_name="groups",
- ),
- ),
- (
- "user_permissions",
- models.ManyToManyField(
- blank=True,
- help_text="Specific permissions for this user.",
- related_name="user_set",
- related_query_name="user",
- to="auth.Permission",
- verbose_name="user permissions",
- ),
- ),
- ],
- options={
- "verbose_name": "user",
- "verbose_name_plural": "users",
- "abstract": False,
- },
- managers=[
- ("objects", ether.users.models.UserManager()),
- ],
- ),
- ]
diff --git a/ether/users/migrations/__init__.py b/ether/users/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/ether/users/models.py b/ether/users/models.py
deleted file mode 100644
index 09ba426..0000000
--- a/ether/users/models.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from typing import ClassVar
-
-from django.contrib.auth.models import AbstractUser
-from django.db.models import CharField
-from django.db.models import EmailField
-from django.urls import reverse
-from django.utils.translation import gettext_lazy as _
-
-from .managers import UserManager
-
-
-class User(AbstractUser):
- """
- Default custom user model for Ether Organization Website.
- If adding fields that need to be filled at user signup,
- check forms.SignupForm and forms.SocialSignupForms accordingly.
- """
-
- # First and last name do not cover name patterns around the globe
- name = CharField(_("Name of User"), blank=True, max_length=255)
- first_name = None # type: ignore[assignment]
- last_name = None # type: ignore[assignment]
- email = EmailField(_("email address"), unique=True)
- username = None # type: ignore[assignment]
-
- USERNAME_FIELD = "email"
- REQUIRED_FIELDS = []
-
- objects: ClassVar[UserManager] = UserManager()
-
- def get_absolute_url(self) -> str:
- """Get URL for user's detail view.
-
- Returns:
- str: URL for user detail.
-
- """
- return reverse("users:detail", kwargs={"pk": self.id})
diff --git a/ether/users/tests/__init__.py b/ether/users/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/ether/users/tests/factories.py b/ether/users/tests/factories.py
deleted file mode 100644
index 789ceb5..0000000
--- a/ether/users/tests/factories.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import annotations
-
-from factory import Faker
-from factory import post_generation
-from factory.django import DjangoModelFactory
-
-from ether.users.models import User
-
-
-class UserFactory(DjangoModelFactory[User]):
- email = Faker("email")
- name = Faker("name")
-
- @post_generation
- def password(self: User, create: bool, extracted: str | None, **kwargs): # noqa: FBT001
- password = (
- extracted
- if extracted
- else Faker(
- "password",
- length=42,
- special_chars=True,
- digits=True,
- upper_case=True,
- lower_case=True,
- ).evaluate(None, None, extra={"locale": None})
- )
- self.set_password(password)
- if create:
- self.save()
-
- class Meta:
- model = User
- django_get_or_create = ["email"]
- skip_postgeneration_save = True
diff --git a/ether/users/tests/test_admin.py b/ether/users/tests/test_admin.py
deleted file mode 100644
index 4d6ee0c..0000000
--- a/ether/users/tests/test_admin.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import contextlib
-from http import HTTPStatus
-from importlib import reload
-
-import pytest
-from django.contrib import admin
-from django.contrib.auth.models import AnonymousUser
-from django.urls import reverse
-from pytest_django.asserts import assertRedirects
-
-from ether.users.models import User
-
-
-class TestUserAdmin:
- def test_changelist(self, admin_client):
- url = reverse("admin:users_user_changelist")
- response = admin_client.get(url)
- assert response.status_code == HTTPStatus.OK
-
- def test_search(self, admin_client):
- url = reverse("admin:users_user_changelist")
- response = admin_client.get(url, data={"q": "test"})
- assert response.status_code == HTTPStatus.OK
-
- def test_add(self, admin_client):
- url = reverse("admin:users_user_add")
- response = admin_client.get(url)
- assert response.status_code == HTTPStatus.OK
-
- response = admin_client.post(
- url,
- data={
- "email": "new-admin@example.com",
- "password1": "My_R@ndom-P@ssw0rd",
- "password2": "My_R@ndom-P@ssw0rd",
- },
- )
- assert response.status_code == HTTPStatus.FOUND
- assert User.objects.filter(email="new-admin@example.com").exists()
-
- def test_view_user(self, admin_client):
- user = User.objects.get(email="admin@example.com")
- url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
- response = admin_client.get(url)
- assert response.status_code == HTTPStatus.OK
-
- @pytest.fixture
- def _force_allauth(self, settings):
- settings.DJANGO_ADMIN_FORCE_ALLAUTH = True
- # Reload the admin module to apply the setting change
- import ether.users.admin as users_admin # noqa: PLC0415
-
- with contextlib.suppress(admin.sites.AlreadyRegistered): # type: ignore[attr-defined]
- reload(users_admin)
-
- @pytest.mark.django_db
- @pytest.mark.usefixtures("_force_allauth")
- def test_allauth_login(self, rf, settings):
- request = rf.get("/fake-url")
- request.user = AnonymousUser()
- response = admin.site.login(request)
-
- # The `admin` login view should redirect to the `allauth` login view
- target_url = reverse(settings.LOGIN_URL) + "?next=" + request.path
- assertRedirects(response, target_url, fetch_redirect_response=False)
diff --git a/ether/users/tests/test_forms.py b/ether/users/tests/test_forms.py
deleted file mode 100644
index f96a25b..0000000
--- a/ether/users/tests/test_forms.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""Module for all Form Tests."""
-
-from django.utils.translation import gettext_lazy as _
-
-from ether.users.forms import UserAdminCreationForm
-from ether.users.models import User
-
-
-class TestUserAdminCreationForm:
- """
- Test class for all tests related to the UserAdminCreationForm
- """
-
- def test_username_validation_error_msg(self, user: User):
- """
- Tests UserAdminCreation Form's unique validator functions correctly by testing:
- 1) A new user with an existing username cannot be added.
- 2) Only 1 error is raised by the UserCreation Form
- 3) The desired error message is raised
- """
-
- # The user already exists,
- # hence cannot be created.
- form = UserAdminCreationForm(
- {
- "email": user.email,
- "password1": user.password,
- "password2": user.password,
- },
- )
-
- assert not form.is_valid()
- assert len(form.errors) == 1
- assert "email" in form.errors
- assert form.errors["email"][0] == _("This email has already been taken.")
diff --git a/ether/users/tests/test_managers.py b/ether/users/tests/test_managers.py
deleted file mode 100644
index e734a1c..0000000
--- a/ether/users/tests/test_managers.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from io import StringIO
-
-import pytest
-from django.core.management import call_command
-
-from ether.users.models import User
-
-
-@pytest.mark.django_db
-class TestUserManager:
- def test_create_user(self):
- user = User.objects.create_user(
- email="john@example.com",
- password="something-r@nd0m!", # noqa: S106
- )
- assert user.email == "john@example.com"
- assert not user.is_staff
- assert not user.is_superuser
- assert user.check_password("something-r@nd0m!")
- assert user.username is None
-
- def test_create_superuser(self):
- user = User.objects.create_superuser(
- email="admin@example.com",
- password="something-r@nd0m!", # noqa: S106
- )
- assert user.email == "admin@example.com"
- assert user.is_staff
- assert user.is_superuser
- assert user.username is None
-
- def test_create_superuser_username_ignored(self):
- user = User.objects.create_superuser(
- email="test@example.com",
- password="something-r@nd0m!", # noqa: S106
- )
- assert user.username is None
-
-
-@pytest.mark.django_db
-def test_createsuperuser_command():
- """Ensure createsuperuser command works with our custom manager."""
- out = StringIO()
- command_result = call_command(
- "createsuperuser",
- "--email",
- "henry@example.com",
- interactive=False,
- stdout=out,
- )
-
- assert command_result is None
- assert out.getvalue() == "Superuser created successfully.\n"
- user = User.objects.get(email="henry@example.com")
- assert not user.has_usable_password()
diff --git a/ether/users/tests/test_models.py b/ether/users/tests/test_models.py
deleted file mode 100644
index 15638af..0000000
--- a/ether/users/tests/test_models.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from ether.users.models import User
-
-
-def test_user_get_absolute_url(user: User):
- assert user.get_absolute_url() == f"/users/{user.pk}/"
diff --git a/ether/users/tests/test_urls.py b/ether/users/tests/test_urls.py
deleted file mode 100644
index 86b91d5..0000000
--- a/ether/users/tests/test_urls.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.urls import resolve
-from django.urls import reverse
-
-from ether.users.models import User
-
-
-def test_detail(user: User):
- assert reverse("users:detail", kwargs={"pk": user.pk}) == f"/users/{user.pk}/"
- assert resolve(f"/users/{user.pk}/").view_name == "users:detail"
-
-
-def test_update():
- assert reverse("users:update") == "/users/~update/"
- assert resolve("/users/~update/").view_name == "users:update"
-
-
-def test_redirect():
- assert reverse("users:redirect") == "/users/~redirect/"
- assert resolve("/users/~redirect/").view_name == "users:redirect"
diff --git a/ether/users/tests/test_views.py b/ether/users/tests/test_views.py
deleted file mode 100644
index cbe8316..0000000
--- a/ether/users/tests/test_views.py
+++ /dev/null
@@ -1,101 +0,0 @@
-from http import HTTPStatus
-
-import pytest
-from django.conf import settings
-from django.contrib import messages
-from django.contrib.auth.models import AnonymousUser
-from django.contrib.messages.middleware import MessageMiddleware
-from django.contrib.sessions.middleware import SessionMiddleware
-from django.http import HttpRequest
-from django.http import HttpResponseRedirect
-from django.test import RequestFactory
-from django.urls import reverse
-from django.utils.translation import gettext_lazy as _
-
-from ether.users.forms import UserAdminChangeForm
-from ether.users.models import User
-from ether.users.tests.factories import UserFactory
-from ether.users.views import UserRedirectView
-from ether.users.views import UserUpdateView
-from ether.users.views import user_detail_view
-
-pytestmark = pytest.mark.django_db
-
-
-class TestUserUpdateView:
- """
- TODO:
- extracting view initialization code as class-scoped fixture
- would be great if only pytest-django supported non-function-scoped
- fixture db access -- this is a work-in-progress for now:
- https://github.com/pytest-dev/pytest-django/pull/258
- """
-
- def dummy_get_response(self, request: HttpRequest):
- return None
-
- def test_get_success_url(self, user: User, rf: RequestFactory):
- view = UserUpdateView()
- request = rf.get("/fake-url/")
- request.user = user
-
- view.request = request
- assert view.get_success_url() == f"/users/{user.pk}/"
-
- def test_get_object(self, user: User, rf: RequestFactory):
- view = UserUpdateView()
- request = rf.get("/fake-url/")
- request.user = user
-
- view.request = request
-
- assert view.get_object() == user
-
- def test_form_valid(self, user: User, rf: RequestFactory):
- view = UserUpdateView()
- request = rf.get("/fake-url/")
-
- # Add the session/message middleware to the request
- SessionMiddleware(self.dummy_get_response).process_request(request)
- MessageMiddleware(self.dummy_get_response).process_request(request)
- request.user = user
-
- view.request = request
-
- # Initialize the form
- form = UserAdminChangeForm()
- form.cleaned_data = {}
- form.instance = user
- view.form_valid(form)
-
- messages_sent = [m.message for m in messages.get_messages(request)]
- assert messages_sent == [_("Information successfully updated")]
-
-
-class TestUserRedirectView:
- def test_get_redirect_url(self, user: User, rf: RequestFactory):
- view = UserRedirectView()
- request = rf.get("/fake-url")
- request.user = user
-
- view.request = request
- assert view.get_redirect_url() == f"/users/{user.pk}/"
-
-
-class TestUserDetailView:
- def test_authenticated(self, user: User, rf: RequestFactory):
- request = rf.get("/fake-url/")
- request.user = UserFactory.create()
- response = user_detail_view(request, pk=user.pk)
-
- assert response.status_code == HTTPStatus.OK
-
- def test_not_authenticated(self, user: User, rf: RequestFactory):
- request = rf.get("/fake-url/")
- request.user = AnonymousUser()
- response = user_detail_view(request, pk=user.pk)
- login_url = reverse(settings.LOGIN_URL)
-
- assert isinstance(response, HttpResponseRedirect)
- assert response.status_code == HTTPStatus.FOUND
- assert response.url == f"{login_url}?next=/fake-url/"
diff --git a/ether/users/urls.py b/ether/users/urls.py
deleted file mode 100644
index 56c246c..0000000
--- a/ether/users/urls.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.urls import path
-
-from .views import user_detail_view
-from .views import user_redirect_view
-from .views import user_update_view
-
-app_name = "users"
-urlpatterns = [
- path("~redirect/", view=user_redirect_view, name="redirect"),
- path("~update/", view=user_update_view, name="update"),
- path("/", view=user_detail_view, name="detail"),
-]
diff --git a/ether/users/views.py b/ether/users/views.py
deleted file mode 100644
index 370508b..0000000
--- a/ether/users/views.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.contrib.messages.views import SuccessMessageMixin
-from django.db.models import QuerySet
-from django.urls import reverse
-from django.utils.translation import gettext_lazy as _
-from django.views.generic import DetailView
-from django.views.generic import RedirectView
-from django.views.generic import UpdateView
-
-from ether.users.models import User
-
-
-class UserDetailView(LoginRequiredMixin, DetailView):
- model = User
- slug_field = "id"
- slug_url_kwarg = "id"
-
-
-user_detail_view = UserDetailView.as_view()
-
-
-class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
- model = User
- fields = ["name"]
- success_message = _("Information successfully updated")
-
- def get_success_url(self) -> str:
- assert self.request.user.is_authenticated # type guard
- return self.request.user.get_absolute_url()
-
- def get_object(self, queryset: QuerySet | None = None) -> User:
- assert self.request.user.is_authenticated # type guard
- return self.request.user
-
-
-user_update_view = UserUpdateView.as_view()
-
-
-class UserRedirectView(LoginRequiredMixin, RedirectView):
- permanent = False
-
- def get_redirect_url(self) -> str:
- return reverse("users:detail", kwargs={"pk": self.request.user.pk})
-
-
-user_redirect_view = UserRedirectView.as_view()
diff --git a/justfile b/justfile
deleted file mode 100644
index 63b2235..0000000
--- a/justfile
+++ /dev/null
@@ -1,38 +0,0 @@
-export COMPOSE_FILE := "docker-compose.local.yml"
-
-## Just does not yet manage signals for subprocesses reliably, which can lead to unexpected behavior.
-## Exercise caution before expanding its usage in production environments.
-## For more information, see https://github.com/casey/just/issues/2473 .
-
-
-# Default command to list all available commands.
-default:
- @just --list
-
-# build: Build python image.
-build *args:
- @echo "Building python image..."
- @docker compose build {{args}}
-
-# up: Start up containers.
-up:
- @echo "Starting up containers..."
- @docker compose up -d --remove-orphans
-
-# down: Stop containers.
-down:
- @echo "Stopping containers..."
- @docker compose down
-
-# prune: Remove containers and their volumes.
-prune *args:
- @echo "Killing containers and removing volumes..."
- @docker compose down -v {{args}}
-
-# logs: View container logs
-logs *args:
- @docker compose logs -f {{args}}
-
-# manage: Executes `manage.py` command.
-manage +args:
- @docker compose run --rm django python ./manage.py {{args}}
diff --git a/locale/README.md b/locale/README.md
deleted file mode 100644
index 8a220a9..0000000
--- a/locale/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Translations
-
-Start by configuring the `LANGUAGES` settings in `base.py`, by uncommenting languages you are willing to support. Then, translation strings will be placed in this folder when running:
-
-```bash
-docker compose -f docker-compose.local.yml run --rm django python manage.py makemessages --all --no-location
-```
-
-This should generate `django.po` (stands for Portable Object) files under each locale `/LC_MESSAGES/django.po`. Each translatable string in the codebase is collected with its `msgid` and need to be translated as `msgstr`, for example:
-
-```po
-msgid "users"
-msgstr "utilisateurs"
-```
-
-Once all translations are done, they need to be compiled into `.mo` files (stands for Machine Object), which are the actual binary files used by the application:
-
-```bash
-docker compose -f docker-compose.local.yml run --rm django python manage.py compilemessages
-```
-
-Note that the `.po` files are NOT used by the application directly, so if the `.mo` files are out of date, the content won't appear as translated even if the `.po` files are up-to-date.
-
-## Production
-
-The production image runs `compilemessages` automatically at build time, so as long as your translated source files (PO) are up-to-date, you're good to go.
-
-## Add a new language
-
-1. Update the [`LANGUAGES` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-LANGUAGES) to your project's base settings.
-2. Create the locale folder for the language next to this file, e.g. `fr_FR` for French. Make sure the case is correct.
-3. Run `makemessages` (as instructed above) to generate the PO files for the new language.
diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po
deleted file mode 100644
index 3be9277..0000000
--- a/locale/en_US/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,12 +0,0 @@
-# Translations for the Ether Organization Website project
-# Copyright (C) 2026 Ether LLC
-# Ether LLC , 2026.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: 0.1.0\n"
-"Language: en-US\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
diff --git a/locale/fr_FR/LC_MESSAGES/django.po b/locale/fr_FR/LC_MESSAGES/django.po
deleted file mode 100644
index ee441bc..0000000
--- a/locale/fr_FR/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,335 +0,0 @@
-# Translations for the Ether Organization Website project
-# Copyright (C) 2026 Ether LLC
-# Ether LLC , 2026.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: 0.1.0\n"
-"Language: fr-FR\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: ether/templates/account/account_inactive.html:5
-#: ether/templates/account/account_inactive.html:8
-msgid "Account Inactive"
-msgstr "Compte inactif"
-
-#: ether/templates/account/account_inactive.html:10
-msgid "This account is inactive."
-msgstr "Ce compte est inactif."
-
-#: ether/templates/account/email.html:7
-msgid "Account"
-msgstr "Compte"
-
-#: ether/templates/account/email.html:10
-msgid "E-mail Addresses"
-msgstr "Adresses e-mail"
-
-#: ether/templates/account/email.html:13
-msgid "The following e-mail addresses are associated with your account:"
-msgstr "Les adresses e-mail suivantes sont associées à votre compte :"
-
-#: ether/templates/account/email.html:27
-msgid "Verified"
-msgstr "Vérifié"
-
-#: ether/templates/account/email.html:29
-msgid "Unverified"
-msgstr "Non vérifié"
-
-#: ether/templates/account/email.html:31
-msgid "Primary"
-msgstr "Primaire"
-
-#: ether/templates/account/email.html:37
-msgid "Make Primary"
-msgstr "Changer Primaire"
-
-#: ether/templates/account/email.html:38
-msgid "Re-send Verification"
-msgstr "Renvoyer vérification"
-
-#: ether/templates/account/email.html:39
-msgid "Remove"
-msgstr "Supprimer"
-
-#: ether/templates/account/email.html:46
-msgid "Warning:"
-msgstr "Avertissement:"
-
-#: ether/templates/account/email.html:46
-msgid ""
-"You currently do not have any e-mail address set up. You should really add "
-"an e-mail address so you can receive notifications, reset your password, etc."
-msgstr ""
-"Vous n'avez actuellement aucune adresse e-mail configurée. Vous devriez ajouter "
-"une adresse e-mail pour reçevoir des notifications, réinitialiser votre mot "
-"de passe, etc."
-
-#: ether/templates/account/email.html:51
-msgid "Add E-mail Address"
-msgstr "Ajouter une adresse e-mail"
-
-#: ether/templates/account/email.html:56
-msgid "Add E-mail"
-msgstr "Ajouter e-mail"
-
-#: ether/templates/account/email.html:66
-msgid "Do you really want to remove the selected e-mail address?"
-msgstr "Voulez-vous vraiment supprimer l'adresse e-mail sélectionnée ?"
-
-#: ether/templates/account/email_confirm.html:6
-#: ether/templates/account/email_confirm.html:10
-msgid "Confirm E-mail Address"
-msgstr "Confirmez votre adresse email"
-
-#: ether/templates/account/email_confirm.html:16
-#, python-format
-msgid ""
-"Please confirm that %(email)s is an e-mail "
-"address for user %(user_display)s."
-msgstr ""
-"Veuillez confirmer que %(email)s est un e-mail "
-"adresse de l'utilisateur %(user_display)s."
-
-#: ether/templates/account/email_confirm.html:20
-msgid "Confirm"
-msgstr "Confirm"
-
-#: ether/templates/account/email_confirm.html:27
-#, python-format
-msgid ""
-"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request ."
-msgstr ""
-"Ce lien de confirmation par e-mail a expiré ou n'est pas valide. Veuillez"
- "émettre une nouvelle demande de confirmation "
-"par e-mail ."
-
-#: ether/templates/account/login.html:7
-#: ether/templates/account/login.html:11
-#: ether/templates/account/login.html:56
-#: ether/templates/base.html:72
-msgid "Sign In"
-msgstr "S'identifier"
-
-#: ether/templates/account/login.html:17
-msgid "Please sign in with one of your existing third party accounts:"
-msgstr "Veuillez vous connecter avec l'un de vos comptes tiers existants :"
-
-#: ether/templates/account/login.html:19
-#, python-format
-msgid ""
-"Or, sign up for a %(site_name)s account and "
-"sign in below:"
-msgstr ""
-"Ou, créez un compte %(site_name)s et "
-"connectez-vous ci-dessous :"
-
-#: ether/templates/account/login.html:32
-msgid "or"
-msgstr "ou"
-
-#: ether/templates/account/login.html:41
-#, python-format
-msgid ""
-"If you have not created an account yet, then please sign up first."
-msgstr ""
-"Si vous n'avez pas encore créé de compte, veuillez d'abord vous inscrire ."
-
-#: ether/templates/account/login.html:55
-msgid "Forgot Password?"
-msgstr "Mot de passe oublié?"
-
-#: ether/templates/account/logout.html:5
-#: ether/templates/account/logout.html:8
-#: ether/templates/account/logout.html:17
-#: ether/templates/base.html:61
-msgid "Sign Out"
-msgstr "Se déconnecter"
-
-#: ether/templates/account/logout.html:10
-msgid "Are you sure you want to sign out?"
-msgstr "Êtes-vous certain de vouloir vous déconnecter?"
-
-#: ether/templates/account/password_change.html:6
-#: ether/templates/account/password_change.html:9
-#: ether/templates/account/password_change.html:14
-#: ether/templates/account/password_reset_from_key.html:5
-#: ether/templates/account/password_reset_from_key.html:8
-#: ether/templates/account/password_reset_from_key_done.html:4
-#: ether/templates/account/password_reset_from_key_done.html:7
-msgid "Change Password"
-msgstr "Changer le mot de passe"
-
-#: ether/templates/account/password_reset.html:7
-#: ether/templates/account/password_reset.html:11
-#: ether/templates/account/password_reset_done.html:6
-#: ether/templates/account/password_reset_done.html:9
-msgid "Password Reset"
-msgstr "Réinitialisation du mot de passe"
-
-#: ether/templates/account/password_reset.html:16
-msgid ""
-"Forgotten your password? Enter your e-mail address below, and we'll send you "
-"an e-mail allowing you to reset it."
-msgstr ""
-"Mot de passe oublié? Entrez votre adresse e-mail ci-dessous, et nous vous "
-"enverrons un e-mail vous permettant de le réinitialiser."
-
-#: ether/templates/account/password_reset.html:21
-msgid "Reset My Password"
-msgstr "Réinitialiser mon mot de passe"
-
-#: ether/templates/account/password_reset.html:24
-msgid "Please contact us if you have any trouble resetting your password."
-msgstr ""
-"Veuillez nous contacter si vous rencontrez des difficultés pour réinitialiser"
-"votre mot de passe."
-
-#: ether/templates/account/password_reset_done.html:15
-msgid ""
-"We have sent you an e-mail. Please contact us if you do not receive it "
-"within a few minutes."
-msgstr ""
-"Nous vous avons envoyé un e-mail. Veuillez nous contacter si vous ne le "
-"recevez pas d'ici quelques minutes."
-
-#: ether/templates/account/password_reset_from_key.html:8
-msgid "Bad Token"
-msgstr "Token Invalide"
-
-#: ether/templates/account/password_reset_from_key.html:12
-#, python-format
-msgid ""
-"The password reset link was invalid, possibly because it has already been "
-"used. Please request a new password reset"
-"a>."
-msgstr ""
-"Le lien de réinitialisation du mot de passe n'était pas valide, peut-être parce "
-"qu'il a déjà été utilisé. Veuillez faire une "
-"nouvelle demande de réinitialisation de mot de passe ."
-
-#: ether/templates/account/password_reset_from_key.html:18
-msgid "change password"
-msgstr "changer le mot de passe"
-
-#: ether/templates/account/password_reset_from_key.html:21
-#: ether/templates/account/password_reset_from_key_done.html:8
-msgid "Your password is now changed."
-msgstr "Votre mot de passe est maintenant modifié."
-
-#: ether/templates/account/password_set.html:6
-#: ether/templates/account/password_set.html:9
-#: ether/templates/account/password_set.html:14
-msgid "Set Password"
-msgstr "Définir le mot de passe"
-
-#: ether/templates/account/signup.html:6
-msgid "Signup"
-msgstr "S'inscrire"
-
-#: ether/templates/account/signup.html:9
-#: ether/templates/account/signup.html:19
-#: ether/templates/base.html:67
-msgid "Sign Up"
-msgstr "S'inscrire"
-
-#: ether/templates/account/signup.html:11
-#, python-format
-msgid ""
-"Already have an account? Then please sign in ."
-msgstr ""
-"Vous avez déjà un compte? Alors veuillez vous connecter ."
-
-#: ether/templates/account/signup_closed.html:5
-#: ether/templates/account/signup_closed.html:8
-msgid "Sign Up Closed"
-msgstr "Inscriptions closes"
-
-#: ether/templates/account/signup_closed.html:10
-msgid "We are sorry, but the sign up is currently closed."
-msgstr "Désolé, mais l'inscription est actuellement fermée."
-
-#: ether/templates/account/verification_sent.html:5
-#: ether/templates/account/verification_sent.html:8
-#: ether/templates/account/verified_email_required.html:5
-#: ether/templates/account/verified_email_required.html:8
-msgid "Verify Your E-mail Address"
-msgstr "Vérifiez votre adresse e-mail"
-
-#: ether/templates/account/verification_sent.html:10
-msgid ""
-"We have sent an e-mail to you for verification. Follow the link provided to "
-"finalize the signup process. Please contact us if you do not receive it "
-"within a few minutes."
-msgstr "Nous vous avons envoyé un e-mail pour vérification. Suivez le lien fourni "
-"pour finalisez le processus d'inscription. Veuillez nous contacter si vous ne le "
-"recevez pas d'ici quelques minutes."
-
-#: ether/templates/account/verified_email_required.html:12
-msgid ""
-"This part of the site requires us to verify that\n"
-"you are who you claim to be. For this purpose, we require that you\n"
-"verify ownership of your e-mail address. "
-msgstr ""
-"Cette partie du site nous oblige à vérifier que\n"
-"vous êtes qui vous prétendez être. Nous vous demandons donc de\n"
-"vérifier la propriété de votre adresse e-mail."
-
-#: ether/templates/account/verified_email_required.html:16
-msgid ""
-"We have sent an e-mail to you for\n"
-"verification. Please click on the link inside this e-mail. Please\n"
-"contact us if you do not receive it within a few minutes."
-msgstr ""
-"Nous vous avons envoyé un e-mail pour\n"
-"vérification. Veuillez cliquer sur le lien contenu dans cet e-mail. Veuillez nous\n"
-"contacter si vous ne le recevez pas d'ici quelques minutes."
-
-#: ether/templates/account/verified_email_required.html:20
-#, python-format
-msgid ""
-"Note: you can still change your e-"
-"mail address ."
-msgstr ""
-"Remarque : vous pouvez toujours changer votre e-"
-"adresse e-mail ."
-
-#: ether/templates/base.html:57
-msgid "My Profile"
-msgstr "Mon Profil"
-
-#: ether/users/admin.py:17
-msgid "Personal info"
-msgstr "Personal info"
-
-#: ether/users/admin.py:19
-msgid "Permissions"
-msgstr "Permissions"
-
-#: ether/users/admin.py:30
-msgid "Important dates"
-msgstr "Dates importantes"
-
-#: ether/users/apps.py:7
-msgid "Users"
-msgstr "Utilisateurs"
-
-#: ether/users/forms.py:24
-#: ether/users/tests/test_forms.py:36
-msgid "This username has already been taken."
-msgstr "Ce nom d'utilisateur est déjà pris."
-
-#: ether/users/models.py:15
-msgid "Name of User"
-msgstr "Nom de l'utilisateur"
-
-#: ether/users/views.py:23
-msgid "Information successfully updated"
-msgstr "Informations mises à jour avec succès"
diff --git a/locale/pt_BR/LC_MESSAGES/django.po b/locale/pt_BR/LC_MESSAGES/django.po
deleted file mode 100644
index 5fbe63f..0000000
--- a/locale/pt_BR/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,315 +0,0 @@
-# Translations for the Ether Organization Website project
-# Copyright (C) 2026 Ether LLC
-# Ether LLC , 2026.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: 0.1.0\n"
-"Language: pt-BR\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: ether/templates/account/account_inactive.html:5
-#: ether/templates/account/account_inactive.html:8
-msgid "Account Inactive"
-msgstr "Conta Inativa"
-
-#: ether/templates/account/account_inactive.html:10
-msgid "This account is inactive."
-msgstr "Esta conta está inativa."
-
-#: ether/templates/account/email.html:7
-msgid "Account"
-msgstr "Conta"
-
-#: ether/templates/account/email.html:10
-msgid "E-mail Addresses"
-msgstr "Endereços de E-mail"
-
-#: ether/templates/account/email.html:13
-msgid "The following e-mail addresses are associated with your account:"
-msgstr "Os seguintes endereços de e-mail estão associados à sua conta:"
-
-#: ether/templates/account/email.html:27
-msgid "Verified"
-msgstr "Verificado"
-
-#: ether/templates/account/email.html:29
-msgid "Unverified"
-msgstr "Não verificado"
-
-#: ether/templates/account/email.html:31
-msgid "Primary"
-msgstr "Primário"
-
-#: ether/templates/account/email.html:37
-msgid "Make Primary"
-msgstr "Tornar Primário"
-
-#: ether/templates/account/email.html:38
-msgid "Re-send Verification"
-msgstr "Reenviar verificação"
-
-#: ether/templates/account/email.html:39
-msgid "Remove"
-msgstr "Remover"
-
-#: ether/templates/account/email.html:46
-msgid "Warning:"
-msgstr "Aviso:"
-
-#: ether/templates/account/email.html:46
-msgid ""
-"You currently do not have any e-mail address set up. You should really add "
-"an e-mail address so you can receive notifications, reset your password, etc."
-msgstr ""
-"No momento, você não tem nenhum endereço de e-mail configurado. Você "
-"realmente deve adicionar um endereço de e-mail para receber notificações, "
-"redefinir sua senha etc."
-
-#: ether/templates/account/email.html:51
-msgid "Add E-mail Address"
-msgstr "Adicionar Endereço de E-mail"
-
-#: ether/templates/account/email.html:56
-msgid "Add E-mail"
-msgstr "Adicionar E-mail"
-
-#: ether/templates/account/email.html:66
-msgid "Do you really want to remove the selected e-mail address?"
-msgstr "Você realmente deseja remover o endereço de e-mail selecionado?"
-
-#: ether/templates/account/email_confirm.html:6
-#: ether/templates/account/email_confirm.html:10
-msgid "Confirm E-mail Address"
-msgstr "Confirme o endereço de e-mail"
-
-#: ether/templates/account/email_confirm.html:16
-#, python-format
-msgid ""
-"Please confirm that %(email)s is an e-mail "
-"address for user %(user_display)s."
-msgstr ""
-"Confirme se %(email)s é um endereço de "
-"e-mail do usuário %(user_display)s."
-
-#: ether/templates/account/email_confirm.html:20
-msgid "Confirm"
-msgstr "Confirmar"
-
-#: ether/templates/account/email_confirm.html:27
-#, python-format
-msgid ""
-"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request ."
-msgstr "Este link de confirmação de e-mail expirou ou é inválido. "
-"Por favor, emita um novo pedido de confirmação por e-mail ."
-
-#: ether/templates/account/login.html:7
-#: ether/templates/account/login.html:11
-#: ether/templates/account/login.html:56
-#: ether/templates/base.html:72
-msgid "Sign In"
-msgstr "Entrar"
-
-#: ether/templates/account/login.html:17
-msgid "Please sign in with one of your existing third party accounts:"
-msgstr "Faça login com uma de suas contas de terceiros existentes:"
-
-#: ether/templates/account/login.html:19
-#, python-format
-msgid ""
-"Or, sign up for a %(site_name)s account and "
-"sign in below:"
-msgstr "Ou, cadastre-se para uma conta em %(site_name)s e entre abaixo:"
-
-#: ether/templates/account/login.html:32
-msgid "or"
-msgstr "ou"
-
-#: ether/templates/account/login.html:41
-#, python-format
-msgid ""
-"If you have not created an account yet, then please sign up first."
-msgstr "Se você ainda não criou uma conta, registre-se primeiro ."
-
-#: ether/templates/account/login.html:55
-msgid "Forgot Password?"
-msgstr "Esqueceu sua senha?"
-
-#: ether/templates/account/logout.html:5
-#: ether/templates/account/logout.html:8
-#: ether/templates/account/logout.html:17
-#: ether/templates/base.html:61
-msgid "Sign Out"
-msgstr "Sair"
-
-#: ether/templates/account/logout.html:10
-msgid "Are you sure you want to sign out?"
-msgstr "Você tem certeza que deseja sair?"
-
-#: ether/templates/account/password_change.html:6
-#: ether/templates/account/password_change.html:9
-#: ether/templates/account/password_change.html:14
-#: ether/templates/account/password_reset_from_key.html:5
-#: ether/templates/account/password_reset_from_key.html:8
-#: ether/templates/account/password_reset_from_key_done.html:4
-#: ether/templates/account/password_reset_from_key_done.html:7
-msgid "Change Password"
-msgstr "Alterar Senha"
-
-#: ether/templates/account/password_reset.html:7
-#: ether/templates/account/password_reset.html:11
-#: ether/templates/account/password_reset_done.html:6
-#: ether/templates/account/password_reset_done.html:9
-msgid "Password Reset"
-msgstr "Redefinição de senha"
-
-#: ether/templates/account/password_reset.html:16
-msgid ""
-"Forgotten your password? Enter your e-mail address below, and we'll send you "
-"an e-mail allowing you to reset it."
-msgstr "Esqueceu sua senha? Digite seu endereço de e-mail abaixo e enviaremos um e-mail permitindo que você o redefina."
-
-#: ether/templates/account/password_reset.html:21
-msgid "Reset My Password"
-msgstr "Redefinir minha senha"
-
-#: ether/templates/account/password_reset.html:24
-msgid "Please contact us if you have any trouble resetting your password."
-msgstr "Entre em contato conosco se tiver algum problema para redefinir sua senha."
-
-#: ether/templates/account/password_reset_done.html:15
-msgid ""
-"We have sent you an e-mail. Please contact us if you do not receive it "
-"within a few minutes."
-msgstr "Enviamos um e-mail para você. Entre em contato conosco se você não recebê-lo dentro de alguns minutos."
-
-#: ether/templates/account/password_reset_from_key.html:8
-msgid "Bad Token"
-msgstr "Token Inválido"
-
-#: ether/templates/account/password_reset_from_key.html:12
-#, python-format
-msgid ""
-"The password reset link was invalid, possibly because it has already been "
-"used. Please request a new password reset"
-"a>."
-msgstr "O link de redefinição de senha era inválido, possivelmente porque já foi usado. "
-" Solicite uma nova redefinição de senha ."
-
-#: ether/templates/account/password_reset_from_key.html:18
-msgid "change password"
-msgstr "alterar senha"
-
-#: ether/templates/account/password_reset_from_key.html:21
-#: ether/templates/account/password_reset_from_key_done.html:8
-msgid "Your password is now changed."
-msgstr "Sua senha agora foi alterada."
-
-#: ether/templates/account/password_set.html:6
-#: ether/templates/account/password_set.html:9
-#: ether/templates/account/password_set.html:14
-msgid "Set Password"
-msgstr "Definir Senha"
-
-#: ether/templates/account/signup.html:6
-msgid "Signup"
-msgstr "Cadastro"
-
-#: ether/templates/account/signup.html:9
-#: ether/templates/account/signup.html:19
-#: ether/templates/base.html:67
-msgid "Sign Up"
-msgstr "Cadastro"
-
-#: ether/templates/account/signup.html:11
-#, python-format
-msgid ""
-"Already have an account? Then please sign in ."
-msgstr "já tem uma conta? Então, por favor, faça login ."
-
-#: ether/templates/account/signup_closed.html:5
-#: ether/templates/account/signup_closed.html:8
-msgid "Sign Up Closed"
-msgstr "Inscrições encerradas"
-
-#: ether/templates/account/signup_closed.html:10
-msgid "We are sorry, but the sign up is currently closed."
-msgstr "Lamentamos, mas as inscrições estão encerradas no momento."
-
-#: ether/templates/account/verification_sent.html:5
-#: ether/templates/account/verification_sent.html:8
-#: ether/templates/account/verified_email_required.html:5
-#: ether/templates/account/verified_email_required.html:8
-msgid "Verify Your E-mail Address"
-msgstr "Verifique seu endereço de e-mail"
-
-#: ether/templates/account/verification_sent.html:10
-msgid ""
-"We have sent an e-mail to you for verification. Follow the link provided to "
-"finalize the signup process. Please contact us if you do not receive it "
-"within a few minutes."
-msgstr "Enviamos um e-mail para você para verificação. Siga o link fornecido para finalizar o processo de inscrição. Entre em contato conosco se você não recebê-lo dentro de alguns minutos."
-
-#: ether/templates/account/verified_email_required.html:12
-msgid ""
-"This part of the site requires us to verify that\n"
-"you are who you claim to be. For this purpose, we require that you\n"
-"verify ownership of your e-mail address. "
-msgstr "Esta parte do site exige que verifiquemos se você é quem afirma ser.\n"
-"Para esse fim, exigimos que você verifique a propriedade\n"
-"do seu endereço de e-mail."
-
-#: ether/templates/account/verified_email_required.html:16
-msgid ""
-"We have sent an e-mail to you for\n"
-"verification. Please click on the link inside this e-mail. Please\n"
-"contact us if you do not receive it within a few minutes."
-msgstr "Enviamos um e-mail para você para verificação.\n"
-"Por favor, clique no link dentro deste e-mail.\n"
-"Entre em contato conosco se você não recebê-lo dentro de alguns minutos."
-
-#: ether/templates/account/verified_email_required.html:20
-#, python-format
-msgid ""
-"Note: you can still change your e-"
-"mail address ."
-msgstr "Nota : você ainda pode alterar seu endereço de e-mail ."
-
-#: ether/templates/base.html:57
-msgid "My Profile"
-msgstr "Meu perfil"
-
-#: ether/users/admin.py:17
-msgid "Personal info"
-msgstr "Informação pessoal"
-
-#: ether/users/admin.py:19
-msgid "Permissions"
-msgstr "Permissões"
-
-#: ether/users/admin.py:30
-msgid "Important dates"
-msgstr "Datas importantes"
-
-#: ether/users/apps.py:7
-msgid "Users"
-msgstr "Usuários"
-
-#: ether/users/forms.py:24
-#: ether/users/tests/test_forms.py:36
-msgid "This username has already been taken."
-msgstr "Este nome de usuário já foi usado."
-
-#: ether/users/models.py:15
-msgid "Name of User"
-msgstr "Nome do Usuário"
-
-#: ether/users/views.py:23
-msgid "Information successfully updated"
-msgstr "Informação atualizada com sucesso"
diff --git a/manage.py b/manage.py
index 275dad5..da300d7 100755
--- a/manage.py
+++ b/manage.py
@@ -3,27 +3,20 @@
import os
import sys
-from pathlib import Path
-def main():
+def main() -> None:
"""Run administrative tasks."""
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
-
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
try:
from django.core.management import execute_from_command_line # noqa: PLC0415
except ImportError as exc:
raise ImportError( # noqa: TRY003
- "Couldn't import Django. Are you sure it's installed and " # noqa: EM101
+ "Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?",
) from exc
- # This allows easy placement of apps within the interior
- # ether directory.
- current_path = Path(__file__).parent.resolve()
- sys.path.append(str(current_path / "ether"))
-
execute_from_command_line(sys.argv)
diff --git a/merge_production_dotenvs_in_dotenv.py b/merge_production_dotenvs_in_dotenv.py
deleted file mode 100644
index b050374..0000000
--- a/merge_production_dotenvs_in_dotenv.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from collections.abc import Sequence
-from pathlib import Path
-
-BASE_DIR = Path(__file__).parent.resolve()
-PRODUCTION_DOTENVS_DIR = BASE_DIR / ".envs" / ".production"
-PRODUCTION_DOTENV_FILES = [
- PRODUCTION_DOTENVS_DIR / ".django",
- PRODUCTION_DOTENVS_DIR / ".postgres",
-]
-DOTENV_FILE = BASE_DIR / ".env"
-
-
-def merge(
- output_file: Path,
- files_to_merge: Sequence[Path],
-) -> None:
- merged_content = ""
- for merge_file in files_to_merge:
- merged_content += merge_file.read_text()
- merged_content += "\n"
- output_file.write_text(merged_content)
-
-
-if __name__ == "__main__":
- merge(DOTENV_FILE, PRODUCTION_DOTENV_FILES)
diff --git a/pyproject.toml b/pyproject.toml
index efa26b3..a3fed24 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,164 +5,62 @@ description = "Organization website for Ether LLC"
readme = "README.md"
license = { text = "Not open source" }
authors = [
- { name = "Ether LLC", email = "info@ether.local" },
+ { name = "Ether LLC", email = "so.masuda.2003@ether-llc.com" },
]
-requires-python = "==3.13.*"
+requires-python = ">=3.12"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.13",
]
dependencies = [
- "argon2-cffi==25.1.0",
- "crispy-bootstrap5==2026.3",
- "django==6.0.4",
- "django-allauth[mfa]==65.16.1",
- "django-anymail==15.0",
- "django-crispy-forms==2.6",
- "django-environ==0.13.0",
- "django-model-utils==5.0.0",
- "django-redis==6.0.0",
- "gunicorn==25.3.0",
- "hiredis==3.3.1",
- "pillow==12.2.0",
- "psycopg[c]==3.3.3",
- "python-slugify==8.0.4",
- "redis==7.4.0",
- "whitenoise==6.12.0",
+ "django>=6.0",
]
[dependency-groups]
dev = [
- "coverage==7.13.5",
- "django-coverage-plugin==3.2.2",
- "django-debug-toolbar==6.3.0",
- "django-extensions==4.1",
- "django-stubs[compatible-mypy]==6.0.3",
"djlint==1.36.4",
- "factory-boy==3.3.3",
- "ipdb==0.13.13",
- "mypy==1.20.2",
- "pre-commit==4.6.0",
- "psycopg[c]==3.3.3",
- "pytest==9.0.3",
- "pytest-django==4.12.0",
- "pytest-sugar==1.1.1",
"ruff==0.15.12",
- "sphinx==9.1.0",
- "sphinx-autobuild==2025.8.25",
- "werkzeug[watchdog]==3.1.8",
]
[tool.ruff]
-# Exclude a variety of commonly ignored directories.
extend-exclude = [
"*/migrations/*.py",
"staticfiles/*",
]
lint.select = [
"A",
- "ASYNC",
"B",
- "BLE",
"C4",
- "C90",
- "COM",
"DJ",
- "DTZ",
"E",
- "EM",
- "ERA",
- "EXE",
"F",
- "FA",
- "FBT",
- "FLY",
- "G",
"I",
- "ICN",
- "INP",
- "INT",
- "ISC",
"N",
- "PD",
"PERF",
- "PGH",
"PIE",
"PL",
- "PT",
"PTH",
- "PYI",
"Q",
"RET",
- "RSE",
"RUF",
"S",
"SIM",
- "SLF",
- "SLOT",
- "T10",
- "T20",
- "TC",
- "TID",
"TRY",
"UP",
"W",
- "YTT",
]
lint.ignore = [
- "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
- "S101", # Use of assert detected https://docs.astral.sh/ruff/rules/assert/
- "SIM102", # sometimes it's better to nest
- # of types for comparison.
- # Deactivated because it can make the code slow:
- # https://github.com/astral-sh/ruff/issues/7871
+ "RUF012",
+ "S101",
+ "SIM102",
]
lint.isort.force-single-line = true
-[tool.pyproject-fmt]
-keep_full_version = true
-
-# ==== mypy ====
-[tool.mypy]
-python_version = "3.13"
-check_untyped_defs = true
-ignore_missing_imports = true
-warn_unused_ignores = true
-warn_redundant_casts = true
-warn_unused_configs = true
-plugins = [
- "mypy_django_plugin.main",
-]
-overrides = [
- # Django migrations should not produce any errors:
- { module = "*.migrations.*", ignore_errors = true },
-]
-
-[tool.django-stubs]
-django_settings_module = "config.settings.test"
-
-[tool.pytest]
-# ==== pytest ====
-ini_options.minversion = "6.0"
-ini_options.addopts = "--ds=config.settings.test --reuse-db --import-mode=importlib"
-ini_options.python_files = [
- "tests.py",
- "test_*.py",
-]
-
-[tool.coverage]
-# ==== Coverage ====
-run.include = [ "ether/**" ]
-run.omit = [ "*/migrations/*", "*/tests/*" ]
-run.plugins = [ "django_coverage_plugin" ]
-
-# ==== djLint ====
[tool.djlint]
blank_line_after_tag = "load,extends"
close_void_tags = true
format_css = true
format_js = true
-# TODO: remove T002 when fixed https://github.com/djlint/djLint/issues/687
ignore = "H006,H030,H031,T002"
include = "H017,H035"
indent = 2
diff --git a/scripts/__init__.py b/scripts/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/scripts/export_static_site.py b/scripts/export_static_site.py
deleted file mode 100644
index 3b5db2b..0000000
--- a/scripts/export_static_site.py
+++ /dev/null
@@ -1,173 +0,0 @@
-from __future__ import annotations
-
-import shutil
-import sys
-from pathlib import Path
-
-import django
-from django.conf import settings
-from django.test import Client
-from django.urls import resolve
-from django.urls import reverse
-
-BASE_DIR = Path(__file__).resolve().parent.parent
-DIST_DIR = BASE_DIR / "dist"
-HTTP_OK = 200
-
-if str(BASE_DIR) not in sys.path:
- sys.path.insert(0, str(BASE_DIR))
-
-PAGES = [
- ("home", "/", "Ether合同会社", Path("index.html")),
- ("services", "/services/", "事業内容", Path("services/index.html")),
- ("company", "/company/", "会社概要", Path("company/index.html")),
- ("about", "/about/", "会社概要", Path("about/index.html")),
- ("contact", "/contact/", "お問い合わせ", Path("contact/index.html")),
-]
-
-SENSITIVE_TERMS = [
- "千葉県浦安市",
- "入船三丁目",
- "金1円",
- "有限責任社員増田創",
-]
-
-
-def configure_django() -> None:
- if settings.configured:
- return
-
- settings.configure(
- SECRET_KEY="static-export-only", # noqa: S106 - Not used outside static rendering.
- DEBUG=False,
- ALLOWED_HOSTS=["testserver"],
- ROOT_URLCONF="scripts.static_export_urls",
- INSTALLED_APPS=[
- "django.contrib.admin",
- "django.contrib.auth",
- "django.contrib.contenttypes",
- "django.contrib.sessions",
- "django.contrib.messages",
- "django.contrib.staticfiles",
- ],
- MIDDLEWARE=[],
- DATABASES={
- "default": {
- "ENGINE": "django.db.backends.sqlite3",
- "NAME": ":memory:",
- },
- },
- TEMPLATES=[
- {
- "BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": [str(BASE_DIR / "ether" / "templates")],
- "APP_DIRS": True,
- "OPTIONS": {
- "context_processors": [
- "django.template.context_processors.request",
- "django.template.context_processors.i18n",
- "django.template.context_processors.media",
- "django.template.context_processors.static",
- "django.template.context_processors.tz",
- "django.contrib.messages.context_processors.messages",
- ],
- },
- },
- ],
- STATIC_URL="/static/",
- STATICFILES_DIRS=[str(BASE_DIR / "ether" / "static")],
- MEDIA_URL="/media/",
- USE_I18N=True,
- USE_TZ=True,
- LANGUAGE_CODE="ja",
- TIME_ZONE="Asia/Tokyo",
- DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
- )
-
-
-def render_pages() -> list[str]:
- client = Client()
- rendered_html: list[str] = []
-
- for name, path, expected_text, output_path in PAGES:
- reversed_path = reverse(name)
- if reversed_path != path:
- msg = f"{name} reversed to {reversed_path}, expected {path}"
- raise RuntimeError(msg)
-
- resolved_name = resolve(path).view_name
- if resolved_name != name:
- msg = f"{path} resolved to {resolved_name}, expected {name}"
- raise RuntimeError(msg)
-
- response = client.get(path)
- if response.status_code != HTTP_OK:
- msg = f"{path} returned HTTP {response.status_code}"
- raise RuntimeError(msg)
-
- html = response.content.decode()
- if expected_text not in html:
- msg = f"{path} is missing expected text: {expected_text}"
- raise RuntimeError(msg)
-
- target = DIST_DIR / output_path
- target.parent.mkdir(parents=True, exist_ok=True)
- target.write_text(html, encoding="utf-8")
- rendered_html.append(html)
-
- return rendered_html
-
-
-def validate_output(rendered_html: list[str]) -> None:
- combined_html = "".join(rendered_html)
- for term in SENSITIVE_TERMS:
- if term in combined_html:
- msg = f"Rendered static output contains sensitive term: {term}"
- raise RuntimeError(msg)
-
- required_files = [
- Path("index.html"),
- Path("services/index.html"),
- Path("company/index.html"),
- Path("about/index.html"),
- Path("contact/index.html"),
- Path("static/css/project.css"),
- Path("static/js/project.js"),
- Path("static/images/ether-hero.png"),
- Path("static/images/favicons/favicon.ico"),
- ]
- for relative_path in required_files:
- if not (DIST_DIR / relative_path).is_file():
- msg = f"Missing static export file: {relative_path}"
- raise RuntimeError(msg)
-
- home_html = (DIST_DIR / "index.html").read_text(encoding="utf-8")
- for link in [
- "/services/",
- "/company/",
- "/contact/",
- "/static/css/project.css",
- "/static/js/project.js",
- ]:
- if link not in home_html:
- msg = f"Home page is missing link: {link}"
- raise RuntimeError(msg)
-
-
-def export_static_site() -> None:
- configure_django()
- django.setup()
-
- if DIST_DIR.exists():
- shutil.rmtree(DIST_DIR)
- DIST_DIR.mkdir()
-
- rendered_html = render_pages()
- shutil.copytree(BASE_DIR / "ether" / "static", DIST_DIR / "static")
- validate_output(rendered_html)
-
- sys.stdout.write(f"Exported {len(PAGES)} pages to {DIST_DIR}\n")
-
-
-if __name__ == "__main__":
- export_static_site()
diff --git a/scripts/static_export_urls.py b/scripts/static_export_urls.py
deleted file mode 100644
index 911ce32..0000000
--- a/scripts/static_export_urls.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.contrib import admin
-from django.urls import path
-from django.views.generic import TemplateView
-
-urlpatterns = [
- path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
- path(
- "services/",
- TemplateView.as_view(template_name="pages/services.html"),
- name="services",
- ),
- path(
- "company/",
- TemplateView.as_view(template_name="pages/company.html"),
- name="company",
- ),
- path(
- "about/",
- TemplateView.as_view(template_name="pages/company.html"),
- name="about",
- ),
- path(
- "contact/",
- TemplateView.as_view(template_name="pages/contact.html"),
- name="contact",
- ),
- path("admin/", admin.site.urls),
-]
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/test_merge_production_dotenvs_in_dotenv.py b/tests/test_merge_production_dotenvs_in_dotenv.py
deleted file mode 100644
index c0e68f6..0000000
--- a/tests/test_merge_production_dotenvs_in_dotenv.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from pathlib import Path
-
-import pytest
-
-from merge_production_dotenvs_in_dotenv import merge
-
-
-@pytest.mark.parametrize(
- ("input_contents", "expected_output"),
- [
- ([], ""),
- ([""], "\n"),
- (["JANE=doe"], "JANE=doe\n"),
- (["SEP=true", "AR=ator"], "SEP=true\nAR=ator\n"),
- (["A=0", "B=1", "C=2"], "A=0\nB=1\nC=2\n"),
- (["X=x\n", "Y=y", "Z=z\n"], "X=x\n\nY=y\nZ=z\n\n"),
- ],
-)
-def test_merge(
- tmp_path: Path,
- input_contents: list[str],
- expected_output: str,
-):
- output_file = tmp_path / ".env"
-
- files_to_merge = []
- for num, input_content in enumerate(input_contents, start=1):
- merge_file = tmp_path / f".service{num}"
- merge_file.write_text(input_content)
- files_to_merge.append(merge_file)
-
- merge(output_file, files_to_merge)
-
- assert output_file.read_text() == expected_output
diff --git a/tests/test_public_pages.py b/tests/test_public_pages.py
deleted file mode 100644
index 450434b..0000000
--- a/tests/test_public_pages.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import pytest
-from django.urls import resolve
-from django.urls import reverse
-
-HTTP_OK = 200
-pytestmark = pytest.mark.django_db
-
-
-@pytest.mark.parametrize(
- ("name", "path", "content"),
- [
- ("home", "/", "Ether合同会社"),
- ("services", "/services/", "事業内容"),
- ("company", "/company/", "会社概要"),
- ("contact", "/contact/", "お問い合わせ"),
- ],
-)
-def test_public_page_urls(client, name, path, content):
- assert reverse(name) == path
- assert resolve(path).view_name == name
-
- response = client.get(path)
-
- assert response.status_code == HTTP_OK
- assert content in response.content.decode()
-
-
-def test_about_aliases_company_page(client):
- assert reverse("about") == "/about/"
- assert resolve("/about/").view_name == "about"
-
- response = client.get("/about/")
-
- assert response.status_code == HTTP_OK
- assert "会社概要" in response.content.decode()
-
-
-def test_public_pages_do_not_include_sensitive_charter_details(client):
- sensitive_terms = [
- "千葉県浦安市",
- "入船三丁目",
- "金1円",
- "有限責任社員増田創",
- ]
-
- combined_pages = "".join(
- client.get(path).content.decode()
- for path in ["/", "/services/", "/company/", "/contact/"]
- )
-
- for term in sensitive_terms:
- assert term not in combined_pages
diff --git a/uv.lock b/uv.lock
index a3c119d..4f33f26 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,60 +1,6 @@
version = 1
revision = 3
-requires-python = "==3.13.*"
-
-[[package]]
-name = "alabaster"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" },
-]
-
-[[package]]
-name = "anyio"
-version = "4.13.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "idna" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
-]
-
-[[package]]
-name = "argon2-cffi"
-version = "25.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "argon2-cffi-bindings" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" },
-]
-
-[[package]]
-name = "argon2-cffi-bindings"
-version = "25.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cffi" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" },
- { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" },
- { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" },
- { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" },
- { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" },
- { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" },
- { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" },
- { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" },
- { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" },
- { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" },
-]
+requires-python = ">=3.12"
[[package]]
name = "asgiref"
@@ -65,90 +11,6 @@ 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 = "babel"
-version = "2.18.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" },
-]
-
-[[package]]
-name = "certifi"
-version = "2026.4.22"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
-]
-
-[[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/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
- { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
- { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
- { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
- { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
- { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
- { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
- { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
- { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
- { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
- { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
- { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
-]
-
-[[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 = "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/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
- { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
- { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
- { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
- { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
- { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
- { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
- { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
- { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
- { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
- { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
- { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
- { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
- { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
- { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
- { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
- { 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.3.3"
@@ -165,1019 +27,128 @@ wheels = [
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 = "coverage"
-version = "7.13.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" },
- { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" },
- { url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" },
- { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" },
- { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" },
- { url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" },
- { url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" },
- { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" },
- { url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" },
- { url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" },
- { url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" },
- { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" },
- { url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" },
- { url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" },
- { url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" },
- { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" },
- { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" },
- { url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" },
- { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" },
- { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" },
- { url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" },
- { url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" },
- { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" },
- { url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" },
- { url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" },
- { url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" },
- { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" },
- { url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" },
- { url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" },
- { url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" },
- { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" },
-]
-
-[[package]]
-name = "crispy-bootstrap5"
-version = "2026.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "django-crispy-forms" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ab/e8/05e1170f6b8fbfe4098392bdca813c2094659853a438919234d4663009b1/crispy_bootstrap5-2026.3.tar.gz", hash = "sha256:e7f5adb36acfbb456444c46e82c436931c796c539e9c620be4fa9dc9c9d6679c", size = 23452, upload-time = "2026-03-01T10:08:00.459Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9d/2d/93f78072f203aa28d961add6a130b929b47f7aa3fef6905898b4bb9a637d/crispy_bootstrap5-2026.3-py3-none-any.whl", hash = "sha256:e0fff85c0503e9aed610a0ee31368e2191d340657f813669491c288c1c2e2dfa", size = 24770, upload-time = "2026-03-01T10:07:59.058Z" },
-]
-
-[[package]]
-name = "cryptography"
-version = "47.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" },
- { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" },
- { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" },
- { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" },
- { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" },
- { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" },
- { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" },
- { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" },
- { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" },
- { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" },
- { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" },
- { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" },
- { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" },
- { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" },
- { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" },
- { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" },
- { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" },
- { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" },
- { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" },
- { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" },
- { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" },
- { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" },
- { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" },
- { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" },
- { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" },
- { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" },
- { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" },
- { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" },
-]
-
-[[package]]
-name = "cssbeautifier"
-version = "1.15.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "editorconfig" },
- { name = "jsbeautifier" },
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f7/01/fdf41c1e5f93d359681976ba10410a04b299d248e28ecce1d4e88588dde4/cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5", size = 25376, upload-time = "2025-02-27T17:53:51.341Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/63/51/ef6c5628e46092f0a54c7cee69acc827adc6b6aab57b55d344fefbdf28f1/cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98", size = 123667, upload-time = "2025-02-27T17:53:43.594Z" },
-]
-
-[[package]]
-name = "decorator"
-version = "5.2.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
-]
-
-[[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 = "6.0.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asgiref" },
- { name = "sqlparse" },
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/60/b9/4155091ad1788b38563bd77a7258c0834e8c12a7f56f6975deaf54f8b61d/django-6.0.4.tar.gz", hash = "sha256:8cfa2572b3f2768b2e84983cf3c4811877a01edb64e817986ec5d60751c113ac", size = 10907407, upload-time = "2026-04-07T13:55:44.961Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e9/47/3d61d611609764aa71a37f7037b870e7bfb22937366974c4fd46cada7bab/django-6.0.4-py3-none-any.whl", hash = "sha256:14359c809fc16e8f81fd2b59d7d348e4d2d799da6840b10522b6edf7b8afc1da", size = 8368342, upload-time = "2026-04-07T13:55:37.999Z" },
-]
-
-[[package]]
-name = "django-allauth"
-version = "65.16.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asgiref" },
- { name = "django" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/df/357187dfff18c7783e4911827a6c69437e290d7259a32a99c23fcd85997f/django_allauth-65.16.1.tar.gz", hash = "sha256:4425ac3088541c4c54983e16e08f6e3eb9f438dc1b1009534fa51c8bb739ed31", size = 2232835, upload-time = "2026-04-17T18:53:59.475Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ad/58/d95b6c3088d83697bfd93782ee57bc6a6462e41eb19121a947b8a015396a/django_allauth-65.16.1-py3-none-any.whl", hash = "sha256:e49df24056bf37c44e56aaad1e51f78994b7d175bc3476d65e8f8f58390a8ce8", size = 2051868, upload-time = "2026-04-17T18:54:12.032Z" },
-]
-
-[package.optional-dependencies]
-mfa = [
- { name = "fido2" },
- { name = "qrcode" },
-]
-
-[[package]]
-name = "django-anymail"
-version = "15.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "idna" },
- { name = "requests" },
- { name = "urllib3" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/00/43/f0aadb31f2c58afcd9f001f4291998cbd6d289898167e79d908506fc6faf/django_anymail-15.0.tar.gz", hash = "sha256:23d8ab6589afe8cc1ae7665c26879814ad192f4c3ed837a2a1868b0a056869e0", size = 106985, upload-time = "2026-04-18T20:44:19.237Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/75/d1/daae99ec3b30886010a499975880ec20c32c622bee6b92c226b715e42f0c/django_anymail-15.0-py3-none-any.whl", hash = "sha256:64d33dd1084bfc8e4e12245f56629be40aa0b0498fc7fc7544d87b9b2048be1e", size = 147229, upload-time = "2026-04-18T20:44:17.323Z" },
-]
-
-[[package]]
-name = "django-coverage-plugin"
-version = "3.2.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "coverage" },
- { name = "django" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/76/07/c3814563d63b8680f4d5bb8880bb151039e258e7d91a7867b0eaf45165bd/django_coverage_plugin-3.2.2.tar.gz", hash = "sha256:fcc507ee02f3a8f7c1d79b6eba1bafebb4a95b5055801987b715f4f88270c441", size = 30814, upload-time = "2026-04-04T20:43:22.409Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/ae/981522be2a8c46eebdd70debcbb2b0e55a8dbd44a3034c727e298b2ba129/django_coverage_plugin-3.2.2-py3-none-any.whl", hash = "sha256:66c9bdb2756762d6bef3510548bb228e1e8465ff1cd2b372775b82624e85e0b4", size = 14463, upload-time = "2026-04-04T20:43:20.849Z" },
-]
-
-[[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-debug-toolbar"
-version = "6.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "sqlparse" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d8/ea/b62673424dd72d2dbf5adf4145281a421d5792f47380d9bc8e3b11e1a769/django_debug_toolbar-6.3.0.tar.gz", hash = "sha256:f830a86fe02e17f625a22cfbed24a5bd1500762e201ec959c50efb0f9327282b", size = 334079, upload-time = "2026-04-02T16:07:01.385Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7d/9e/d8c3c845f4b5ccac7377c19f4049e7e00c6f121846a81f69a497b45734df/django_debug_toolbar-6.3.0-py3-none-any.whl", hash = "sha256:a199ce3d0f884739a9096835ad417479fede05f3b3c4824bc8b354721ba8f629", size = 298304, upload-time = "2026-04-02T16:06:59.617Z" },
-]
-
-[[package]]
-name = "django-environ"
-version = "0.13.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/18/3c/60983e6ec9b24a8d8588eecebfd21123cba980bce0a905807a27692f0860/django_environ-0.13.0.tar.gz", hash = "sha256:6c401e4c219442c2c4588c2116d5292b5484a6f69163ed09cd41f3943bfb645f", size = 63529, upload-time = "2026-02-18T01:08:08.791Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c4/00/3767393ece946084e1c6830a33ffb8e39d68642e27ad5ac7d4c8bd5de866/django_environ-0.13.0-py3-none-any.whl", hash = "sha256:37799d14cd78222c6fd8298e48bfe17965ff8e586091ad66a463e52e0e7b799e", size = 20682, upload-time = "2026-02-18T01:08:07.359Z" },
-]
-
-[[package]]
-name = "django-extensions"
-version = "4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/6d/b3/ed0f54ed706ec0b54fd251cc0364a249c6cd6c6ec97f04dc34be5e929eac/django_extensions-4.1.tar.gz", hash = "sha256:7b70a4d28e9b840f44694e3f7feb54f55d495f8b3fa6c5c0e5e12bcb2aa3cdeb", size = 283078, upload-time = "2025-04-11T01:15:39.617Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
-]
-
-[[package]]
-name = "django-model-utils"
-version = "5.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/81/60/5e232c32a2c977cc1af8c70a38ef436598bc649ad89c2c4568454edde2c9/django_model_utils-5.0.0.tar.gz", hash = "sha256:041cdd6230d2fbf6cd943e1969318bce762272077f4ecd333ab2263924b4e5eb", size = 80559, upload-time = "2024-09-04T11:35:22.858Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/13/87a42048700c54bfce35900a34e2031245132775fb24363fc0e33664aa9c/django_model_utils-5.0.0-py3-none-any.whl", hash = "sha256:fec78e6c323d565a221f7c4edc703f4567d7bb1caeafe1acd16a80c5ff82056b", size = 42630, upload-time = "2024-09-04T11:36:23.166Z" },
-]
-
-[[package]]
-name = "django-redis"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "redis" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/08/53/dbcfa1e528e0d6c39947092625b2c89274b5d88f14d357cee53c4d6dbbd4/django_redis-6.0.0.tar.gz", hash = "sha256:2d9cb12a20424a4c4dde082c6122f486628bae2d9c2bee4c0126a4de7fda00dd", size = 56904, upload-time = "2025-06-17T18:15:46.376Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/79/055dfcc508cfe9f439d9f453741188d633efa9eab90fc78a67b0ab50b137/django_redis-6.0.0-py3-none-any.whl", hash = "sha256:20bf0063a8abee567eb5f77f375143c32810c8700c0674ced34737f8de4e36c0", size = 33687, upload-time = "2025-06-17T18:15:34.165Z" },
-]
-
-[[package]]
-name = "django-stubs"
-version = "6.0.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "django-stubs-ext" },
- { name = "types-pyyaml" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/86/0c/8d0d875af79bf774c1c3997c84aa118dba3a77be12086b9c14e130e8ec72/django_stubs-6.0.3.tar.gz", hash = "sha256:ee895f403c373608eeb50822f0733f9d9ec5ab12731d4ab58956053bb95fdd9e", size = 278214, upload-time = "2026-04-18T15:11:22.327Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/80/a3/6751b7684d20fc4f228bdd3dd8341d382ab3faaf65d3d050c0d59ab0a1b0/django_stubs-6.0.3-py3-none-any.whl", hash = "sha256:5fee22bcbbad59a78c727a820b6f4e68ff442ca76a922b7002e57c25dd7cb390", size = 541570, upload-time = "2026-04-18T15:11:20.711Z" },
-]
-
-[package.optional-dependencies]
-compatible-mypy = [
- { name = "mypy" },
-]
-
-[[package]]
-name = "django-stubs-ext"
-version = "6.0.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/fb/e6/5dcdaa785ec3eed5fc196c7e68fb7ad9d9fe6d5acccea4690e65f2546417/django_stubs_ext-6.0.3.tar.gz", hash = "sha256:3307d42132bc295d5744de6276bc5fdf6896efc70f891e21c0ae8bdf529d2762", size = 6663, upload-time = "2026-04-18T15:10:53.667Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/fa/0a3a05c29d6295dbd52fa3cb4047a95de11ba4f2696072d6f3f2c1e6f370/django_stubs_ext-6.0.3-py3-none-any.whl", hash = "sha256:9e4105955419ae310d7da9cfd808e039d4dae3092c628f021057bb4f2c237f8f", size = 10354, upload-time = "2026-04-18T15:10:52.395Z" },
-]
-
-[[package]]
-name = "djlint"
-version = "1.36.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "colorama" },
- { name = "cssbeautifier" },
- { name = "jsbeautifier" },
- { name = "json5" },
- { name = "pathspec" },
- { name = "pyyaml" },
- { name = "regex" },
- { name = "tqdm" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/74/89/ecf5be9f5c59a0c53bcaa29671742c5e269cc7d0e2622e3f65f41df251bf/djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1", size = 47849, upload-time = "2024-12-24T13:06:36.36Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/da/83/88b4c885812921739f5529a29085c3762705154d41caf7eb9a8886a3380c/djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2", size = 354384, upload-time = "2024-12-24T13:06:20.809Z" },
- { url = "https://files.pythonhosted.org/packages/32/38/67695f7a150b3d9d62fadb65242213d96024151570c3cf5d966effa68b0e/djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835", size = 322971, upload-time = "2024-12-24T13:06:22.185Z" },
- { url = "https://files.pythonhosted.org/packages/ac/7a/cd851393291b12e7fe17cf5d4d8874b8ea133aebbe9235f5314aabc96a52/djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f", size = 410972, upload-time = "2024-12-24T13:06:24.077Z" },
- { url = "https://files.pythonhosted.org/packages/6c/31/56469120394b970d4f079a552fde21ed27702ca729595ab0ed459eb6d240/djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4", size = 362053, upload-time = "2024-12-24T13:06:25.432Z" },
- { url = "https://files.pythonhosted.org/packages/4b/67/f7aeea9be6fb3bd984487af8d0d80225a0b1e5f6f7126e3332d349fb13fe/djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd", size = 52290, upload-time = "2024-12-24T13:06:33.76Z" },
-]
-
-[[package]]
-name = "docutils"
-version = "0.22.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" },
-]
-
-[[package]]
-name = "editorconfig"
-version = "0.17.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/88/3a/a61d9a1f319a186b05d14df17daea42fcddea63c213bcd61a929fb3a6796/editorconfig-0.17.1.tar.gz", hash = "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745", size = 14695, upload-time = "2025-06-09T08:21:37.097Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl", hash = "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", size = 16360, upload-time = "2025-06-09T08:21:35.654Z" },
-]
-
-[[package]]
-name = "ether"
-version = "0.1.0"
-source = { virtual = "." }
-dependencies = [
- { name = "argon2-cffi" },
- { name = "crispy-bootstrap5" },
- { name = "django" },
- { name = "django-allauth", extra = ["mfa"] },
- { name = "django-anymail" },
- { name = "django-crispy-forms" },
- { name = "django-environ" },
- { name = "django-model-utils" },
- { name = "django-redis" },
- { name = "gunicorn" },
- { name = "hiredis" },
- { name = "pillow" },
- { name = "psycopg", extra = ["c"] },
- { name = "python-slugify" },
- { name = "redis" },
- { name = "whitenoise" },
-]
-
-[package.dev-dependencies]
-dev = [
- { name = "coverage" },
- { name = "django-coverage-plugin" },
- { name = "django-debug-toolbar" },
- { name = "django-extensions" },
- { name = "django-stubs", extra = ["compatible-mypy"] },
- { name = "djlint" },
- { name = "factory-boy" },
- { name = "ipdb" },
- { name = "mypy" },
- { name = "pre-commit" },
- { name = "psycopg", extra = ["c"] },
- { name = "pytest" },
- { name = "pytest-django" },
- { name = "pytest-sugar" },
- { name = "ruff" },
- { name = "sphinx" },
- { name = "sphinx-autobuild" },
- { name = "werkzeug", extra = ["watchdog"] },
-]
-
-[package.metadata]
-requires-dist = [
- { name = "argon2-cffi", specifier = "==25.1.0" },
- { name = "crispy-bootstrap5", specifier = "==2026.3" },
- { name = "django", specifier = "==6.0.4" },
- { name = "django-allauth", extras = ["mfa"], specifier = "==65.16.1" },
- { name = "django-anymail", specifier = "==15.0" },
- { name = "django-crispy-forms", specifier = "==2.6" },
- { name = "django-environ", specifier = "==0.13.0" },
- { name = "django-model-utils", specifier = "==5.0.0" },
- { name = "django-redis", specifier = "==6.0.0" },
- { name = "gunicorn", specifier = "==25.3.0" },
- { name = "hiredis", specifier = "==3.3.1" },
- { name = "pillow", specifier = "==12.2.0" },
- { name = "psycopg", extras = ["c"], specifier = "==3.3.3" },
- { name = "python-slugify", specifier = "==8.0.4" },
- { name = "redis", specifier = "==7.4.0" },
- { name = "whitenoise", specifier = "==6.12.0" },
-]
-
-[package.metadata.requires-dev]
-dev = [
- { name = "coverage", specifier = "==7.13.5" },
- { name = "django-coverage-plugin", specifier = "==3.2.2" },
- { name = "django-debug-toolbar", specifier = "==6.3.0" },
- { name = "django-extensions", specifier = "==4.1" },
- { name = "django-stubs", extras = ["compatible-mypy"], specifier = "==6.0.3" },
- { name = "djlint", specifier = "==1.36.4" },
- { name = "factory-boy", specifier = "==3.3.3" },
- { name = "ipdb", specifier = "==0.13.13" },
- { name = "mypy", specifier = "==1.20.2" },
- { name = "pre-commit", specifier = "==4.6.0" },
- { name = "psycopg", extras = ["c"], specifier = "==3.3.3" },
- { name = "pytest", specifier = "==9.0.3" },
- { name = "pytest-django", specifier = "==4.12.0" },
- { name = "pytest-sugar", specifier = "==1.1.1" },
- { name = "ruff", specifier = "==0.15.12" },
- { name = "sphinx", specifier = "==9.1.0" },
- { name = "sphinx-autobuild", specifier = "==2025.8.25" },
- { name = "werkzeug", extras = ["watchdog"], specifier = "==3.1.8" },
-]
-
-[[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 = "factory-boy"
-version = "3.3.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "faker" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146, upload-time = "2025-02-03T09:49:04.433Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036, upload-time = "2025-02-03T09:49:01.659Z" },
-]
-
-[[package]]
-name = "faker"
-version = "40.15.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/7f/13/6741787bd91c4109c7bed047d68273965cd52ce8a5f773c471b949334b6d/faker-40.15.0.tar.gz", hash = "sha256:20f3a6ec8c266b74d4c554e34118b21c3c2056c0b4a519d15c8decb3a4e6e795", size = 1967447, upload-time = "2026-04-17T20:05:27.555Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" },
-]
-
-[[package]]
-name = "fido2"
-version = "2.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/09/34/4837e2f5640baf61d8abd6125ccb6cc60b4b2933088528356ad6e781496f/fido2-2.2.0.tar.gz", hash = "sha256:0d8122e690096ad82afde42ac9d6433a4eeffda64084f36341ea02546b181dd1", size = 294167, upload-time = "2026-04-15T06:42:50.264Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/01/82/f3c5dd87b0977f5547cc132b7969e6f5075a8c2f5881cf4b6df6378505f9/fido2-2.2.0-py3-none-any.whl", hash = "sha256:3587ccf0af7b71b5dd73f17e1dbec9f0fd157292f9163f02e7778f46d0d25fe5", size = 234025, upload-time = "2026-04-15T06:42:51.813Z" },
-]
-
-[[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 = "gunicorn"
-version = "25.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/c8/8aaf447698c4d59aa853fd318eed300b5c9e44459f242ab8ead6c9c09792/gunicorn-25.3.0-py3-none-any.whl", hash = "sha256:cacea387dab08cd6776501621c295a904fe8e3b7aae9a1a3cbb26f4e7ed54660", size = 208403, upload-time = "2026-03-27T00:00:27.386Z" },
-]
-
-[[package]]
-name = "h11"
-version = "0.16.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
-]
-
-[[package]]
-name = "hiredis"
-version = "3.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/97/d6/9bef6dc3052c168c93fbf7e6c0f2b12c45f0f741a2d30fd919096774343a/hiredis-3.3.1.tar.gz", hash = "sha256:da6f0302360e99d32bc2869772692797ebadd536e1b826d0103c72ba49d38698", size = 89101, upload-time = "2026-03-16T15:21:08.092Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d8/4b/c7f4d6d6643622f296395269e24b02c69d4ac72822f052b8cae16fa3af03/hiredis-3.3.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:afe3c3863f16704fb5d7c2c6ff56aaf9e054f6d269f7b4c9074c5476178d1aba", size = 82027, upload-time = "2026-03-16T15:19:48.002Z" },
- { url = "https://files.pythonhosted.org/packages/9b/45/198be960a7443d6eb5045751e929480929c0defbca316ce1a47d15187330/hiredis-3.3.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:f19ee7dc1ef8a6497570d91fa4057ba910ad98297a50b8c44ff37589f7c89d17", size = 46220, upload-time = "2026-03-16T15:19:48.953Z" },
- { url = "https://files.pythonhosted.org/packages/6a/a4/6ab925177f289830008dbe1488a9858675e2e234f48c9c1653bd4d0eaddc/hiredis-3.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:09f5e510f637f2c72d2a79fb3ad05f7b6211e057e367ca5c4f97bb3d8c9d71f4", size = 41858, upload-time = "2026-03-16T15:19:49.939Z" },
- { url = "https://files.pythonhosted.org/packages/fe/c8/a0ddbb9e9c27fcb0022f7b7e93abc75727cb634c6a5273ca5171033dac78/hiredis-3.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b46e96b50dad03495447860510daebd2c96fd44ed25ba8ccb03e9f89eaa9d34", size = 170095, upload-time = "2026-03-16T15:19:51.216Z" },
- { url = "https://files.pythonhosted.org/packages/94/06/618d509cc454912028f71995f3dd6eb54606f0aa8163ff79c5b7ec1f2bda/hiredis-3.3.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b4fe7f38aa8956fcc1cea270e62601e0e11066aff78e384be70fd283d30293b6", size = 181745, upload-time = "2026-03-16T15:19:52.72Z" },
- { url = "https://files.pythonhosted.org/packages/06/14/75b2deb62a61fc75a41ce1a6a781fe239133bbc88fef404d32a148ad152a/hiredis-3.3.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b96da7e365d6488d2a75266a662cbe3cc14b28c23dd9b0c9aa04b5bc5c20192", size = 180465, upload-time = "2026-03-16T15:19:53.847Z" },
- { url = "https://files.pythonhosted.org/packages/7e/8c/8e03dcbfde8e2ca3f880fce06ad0877b3f098ed5fdfb17cf3b821a32323a/hiredis-3.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52d5641027d6731bc7b5e7d126a5158a99784a9f8c6de3d97ca89aca4969e9f8", size = 172419, upload-time = "2026-03-16T15:19:54.959Z" },
- { url = "https://files.pythonhosted.org/packages/03/05/843005d68403a3805309075efc6638360a3ababa6cb4545163bf80c8e7f7/hiredis-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eddeb9a153795cf6e615f9f3cef66a1d573ff3b6ee16df2b10d1d1c2f2baeaa8", size = 166398, upload-time = "2026-03-16T15:19:56.36Z" },
- { url = "https://files.pythonhosted.org/packages/f5/23/abe2476244fd792f5108009ec0ae666eaa5b2165ca19f2e86638d8324ac9/hiredis-3.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:011a9071c3df4885cac7f58a2623feac6c8e2ad30e6ba93c55195af05ce61ff5", size = 176844, upload-time = "2026-03-16T15:19:57.462Z" },
- { url = "https://files.pythonhosted.org/packages/c6/47/e1cdccc559b98e548bcff0868c3938d375663418c0adca465895ee1f72e7/hiredis-3.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:264ee7e9cb6c30dc78da4ecf71d74cf14ca122817c665d838eda8b4384bce1b0", size = 170366, upload-time = "2026-03-16T15:19:58.548Z" },
- { url = "https://files.pythonhosted.org/packages/a2/e1/fda8325f51d06877e8e92500b15d4aff3855b4c3c91dbd9636a82e4591f2/hiredis-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d1434d0bcc1b3ef048bae53f26456405c08aeed9827e65b24094f5f3a6793f1", size = 168023, upload-time = "2026-03-16T15:19:59.727Z" },
- { url = "https://files.pythonhosted.org/packages/cd/21/2839d1625095989c116470e2b6841bbe1a2a5509585e82a4f3f5cd47f511/hiredis-3.3.1-cp313-cp313-win32.whl", hash = "sha256:f915a34fb742e23d0d61573349aa45d6f74037fde9d58a9f340435eff8d62736", size = 20535, upload-time = "2026-03-16T15:20:00.938Z" },
- { url = "https://files.pythonhosted.org/packages/84/f9/534c2a89b24445a9a9623beb4697fd72b8c8f16286f6f3bda012c7af004a/hiredis-3.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:d8e56e0d1fe607bfff422633f313aec9191c3859ab99d11ff097e3e6e068000c", size = 22383, upload-time = "2026-03-16T15:20:01.865Z" },
-]
-
-[[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.13"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" },
-]
-
-[[package]]
-name = "imagesize"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" },
-]
-
-[[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 = "ipdb"
-version = "0.13.13"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "decorator" },
- { name = "ipython" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/1b/7e07e7b752017f7693a0f4d41c13e5ca29ce8cbcfdcc1fd6c4ad8c0a27a0/ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726", size = 17042, upload-time = "2023-03-09T15:40:57.487Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/4c/b075da0092003d9a55cf2ecc1cae9384a1ca4f650d51b00fc59875fe76f6/ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4", size = 12130, upload-time = "2023-03-09T15:40:55.021Z" },
-]
-
-[[package]]
-name = "ipython"
-version = "9.13.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "decorator" },
- { name = "ipython-pygments-lexers" },
- { name = "jedi" },
- { name = "matplotlib-inline" },
- { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
- { name = "prompt-toolkit" },
- { name = "psutil" },
- { name = "pygments" },
- { name = "stack-data" },
- { name = "traitlets" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" },
-]
-
-[[package]]
-name = "ipython-pygments-lexers"
-version = "1.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pygments" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
-]
-
-[[package]]
-name = "jedi"
-version = "0.19.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "parso" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" },
-]
-
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
-]
-
-[[package]]
-name = "jsbeautifier"
-version = "1.15.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "editorconfig" },
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ea/98/d6cadf4d5a1c03b2136837a435682418c29fdeb66be137128544cecc5b7a/jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", size = 75257, upload-time = "2025-02-27T17:53:53.252Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528", size = 94707, upload-time = "2025-02-27T17:53:46.152Z" },
-]
-
-[[package]]
-name = "json5"
-version = "0.14.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656, upload-time = "2026-03-27T22:50:48.108Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271, upload-time = "2026-03-27T22:50:47.073Z" },
-]
-
-[[package]]
-name = "librt"
-version = "0.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/eb/6b/3d5c13fb3e3c4f43206c8f9dfed13778c2ed4f000bacaa0b7ce3c402a265/librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d", size = 184368, upload-time = "2026-04-09T16:06:26.173Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/d7/1b3e26fffde1452d82f5666164858a81c26ebe808e7ae8c9c88628981540/librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e", size = 68367, upload-time = "2026-04-09T16:05:17.243Z" },
- { url = "https://files.pythonhosted.org/packages/a5/5b/c61b043ad2e091fbe1f2d35d14795e545d0b56b03edaa390fa1dcee3d160/librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22", size = 70595, upload-time = "2026-04-09T16:05:18.471Z" },
- { url = "https://files.pythonhosted.org/packages/a3/22/2448471196d8a73370aa2f23445455dc42712c21404081fcd7a03b9e0749/librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a", size = 204354, upload-time = "2026-04-09T16:05:19.593Z" },
- { url = "https://files.pythonhosted.org/packages/ac/5e/39fc4b153c78cfd2c8a2dcb32700f2d41d2312aa1050513183be4540930d/librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5", size = 216238, upload-time = "2026-04-09T16:05:20.868Z" },
- { url = "https://files.pythonhosted.org/packages/d7/42/bc2d02d0fa7badfa63aa8d6dcd8793a9f7ef5a94396801684a51ed8d8287/librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11", size = 230589, upload-time = "2026-04-09T16:05:22.305Z" },
- { url = "https://files.pythonhosted.org/packages/c8/7b/e2d95cc513866373692aa5edf98080d5602dd07cabfb9e5d2f70df2f25f7/librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858", size = 224610, upload-time = "2026-04-09T16:05:23.647Z" },
- { url = "https://files.pythonhosted.org/packages/31/d5/6cec4607e998eaba57564d06a1295c21b0a0c8de76e4e74d699e627bd98c/librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e", size = 232558, upload-time = "2026-04-09T16:05:25.025Z" },
- { url = "https://files.pythonhosted.org/packages/95/8c/27f1d8d3aaf079d3eb26439bf0b32f1482340c3552e324f7db9dca858671/librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0", size = 225521, upload-time = "2026-04-09T16:05:26.311Z" },
- { url = "https://files.pythonhosted.org/packages/6b/d8/1e0d43b1c329b416017619469b3c3801a25a6a4ef4a1c68332aeaa6f72ca/librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2", size = 227789, upload-time = "2026-04-09T16:05:27.624Z" },
- { url = "https://files.pythonhosted.org/packages/2c/b4/d3d842e88610fcd4c8eec7067b0c23ef2d7d3bff31496eded6a83b0f99be/librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d", size = 248616, upload-time = "2026-04-09T16:05:29.181Z" },
- { url = "https://files.pythonhosted.org/packages/ec/28/527df8ad0d1eb6c8bdfa82fc190f1f7c4cca5a1b6d7b36aeabf95b52d74d/librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd", size = 56039, upload-time = "2026-04-09T16:05:30.709Z" },
- { url = "https://files.pythonhosted.org/packages/f3/a7/413652ad0d92273ee5e30c000fc494b361171177c83e57c060ecd3c21538/librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519", size = 63264, upload-time = "2026-04-09T16:05:31.881Z" },
- { url = "https://files.pythonhosted.org/packages/a4/0a/92c244309b774e290ddb15e93363846ae7aa753d9586b8aad511c5e6145b/librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5", size = 53728, upload-time = "2026-04-09T16:05:33.31Z" },
-]
-
-[[package]]
-name = "markupsafe"
-version = "3.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
- { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
- { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
- { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
- { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
- { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
- { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
- { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
- { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
- { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
- { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
- { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
- { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
- { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
- { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
- { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
- { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
- { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
- { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
- { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
- { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
- { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
-]
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.2.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "traitlets" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" },
-]
-
-[[package]]
-name = "mypy"
-version = "1.20.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "librt", marker = "platform_python_implementation != 'PyPy'" },
- { name = "mypy-extensions" },
- { name = "pathspec" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" },
- { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" },
- { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" },
- { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" },
- { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" },
- { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" },
- { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" },
- { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" },
-]
-
-[[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 = "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 = "parso"
-version = "0.8.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" },
-]
-
-[[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 = "pexpect"
-version = "4.9.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "ptyprocess" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
-]
-
-[[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/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" },
- { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" },
- { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" },
- { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" },
- { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" },
- { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" },
- { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" },
- { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" },
- { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" },
- { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" },
- { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" },
- { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" },
- { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" },
- { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" },
- { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" },
- { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" },
- { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" },
- { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" },
- { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" },
- { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" },
- { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" },
- { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" },
- { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" },
- { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" },
- { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" },
-]
-
-[[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 = "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"
-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 = "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 = "psutil"
-version = "7.2.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
- { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
- { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
- { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
- { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
- { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
- { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
- { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
- { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
- { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
- { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
- { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
- { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
- { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
-]
-
-[[package]]
-name = "psycopg"
-version = "3.3.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tzdata", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d3/b6/379d0a960f8f435ec78720462fd94c4863e7a31237cf81bf76d0af5883bf/psycopg-3.3.3.tar.gz", hash = "sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9", size = 165624, upload-time = "2026-02-18T16:52:16.546Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/5b/181e2e3becb7672b502f0ed7f16ed7352aca7c109cfb94cf3878a9186db9/psycopg-3.3.3-py3-none-any.whl", hash = "sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698", size = 212768, upload-time = "2026-02-18T16:46:27.365Z" },
-]
-
-[package.optional-dependencies]
-c = [
- { name = "psycopg-c", marker = "implementation_name != 'pypy'" },
-]
-
-[[package]]
-name = "psycopg-c"
-version = "3.3.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cb/a0/8feb0ca8c7c20a8b9ac4d46b335ddd57e48e593b714262f006880f34fee5/psycopg_c-3.3.3.tar.gz", hash = "sha256:86ef6f4424348247828e83fb0882c9f8acb33e64d0a5ce66c1b4a5107ee73edd", size = 631965, upload-time = "2026-02-18T16:52:18.084Z" }
-
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
+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/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
+ { 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 = "pure-eval"
-version = "0.2.3"
+name = "cssbeautifier"
+version = "1.15.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
+dependencies = [
+ { name = "editorconfig" },
+ { name = "jsbeautifier" },
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f7/01/fdf41c1e5f93d359681976ba10410a04b299d248e28ecce1d4e88588dde4/cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5", size = 25376, upload-time = "2025-02-27T17:53:51.341Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
+ { url = "https://files.pythonhosted.org/packages/63/51/ef6c5628e46092f0a54c7cee69acc827adc6b6aab57b55d344fefbdf28f1/cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98", size = 123667, upload-time = "2025-02-27T17:53:43.594Z" },
]
[[package]]
-name = "pycparser"
-version = "3.0"
+name = "django"
+version = "6.0.4"
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" }
+dependencies = [
+ { name = "asgiref" },
+ { name = "sqlparse" },
+ { name = "tzdata", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/60/b9/4155091ad1788b38563bd77a7258c0834e8c12a7f56f6975deaf54f8b61d/django-6.0.4.tar.gz", hash = "sha256:8cfa2572b3f2768b2e84983cf3c4811877a01edb64e817986ec5d60751c113ac", size = 10907407, upload-time = "2026-04-07T13:55:44.961Z" }
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" },
+ { url = "https://files.pythonhosted.org/packages/e9/47/3d61d611609764aa71a37f7037b870e7bfb22937366974c4fd46cada7bab/django-6.0.4-py3-none-any.whl", hash = "sha256:14359c809fc16e8f81fd2b59d7d348e4d2d799da6840b10522b6edf7b8afc1da", size = 8368342, upload-time = "2026-04-07T13:55:37.999Z" },
]
[[package]]
-name = "pygments"
-version = "2.20.0"
+name = "djlint"
+version = "1.36.4"
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" }
+dependencies = [
+ { name = "click" },
+ { name = "colorama" },
+ { name = "cssbeautifier" },
+ { name = "jsbeautifier" },
+ { name = "json5" },
+ { name = "pathspec" },
+ { name = "pyyaml" },
+ { name = "regex" },
+ { name = "tqdm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/89/ecf5be9f5c59a0c53bcaa29671742c5e269cc7d0e2622e3f65f41df251bf/djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1", size = 47849, upload-time = "2024-12-24T13:06:36.36Z" }
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" },
+ { url = "https://files.pythonhosted.org/packages/53/f5/9ae02b875604755d4d00cebf96b218b0faa3198edc630f56a139581aed87/djlint-1.36.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b", size = 354886, upload-time = "2024-12-24T13:06:11.571Z" },
+ { url = "https://files.pythonhosted.org/packages/97/51/284443ff2f2a278f61d4ae6ae55eaf820ad9f0fd386d781cdfe91f4de495/djlint-1.36.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e", size = 323237, upload-time = "2024-12-24T13:06:13.057Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/5e/791f4c5571f3f168ad26fa3757af8f7a05c623fde1134a9c4de814ee33b7/djlint-1.36.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675", size = 411719, upload-time = "2024-12-24T13:06:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/11/894425add6f84deffcc6e373f2ce250f2f7b01aa58c7f230016ebe7a0085/djlint-1.36.4-cp312-cp312-win_amd64.whl", hash = "sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08", size = 362076, upload-time = "2024-12-24T13:06:17.517Z" },
+ { url = "https://files.pythonhosted.org/packages/da/83/88b4c885812921739f5529a29085c3762705154d41caf7eb9a8886a3380c/djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2", size = 354384, upload-time = "2024-12-24T13:06:20.809Z" },
+ { url = "https://files.pythonhosted.org/packages/32/38/67695f7a150b3d9d62fadb65242213d96024151570c3cf5d966effa68b0e/djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835", size = 322971, upload-time = "2024-12-24T13:06:22.185Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/7a/cd851393291b12e7fe17cf5d4d8874b8ea133aebbe9235f5314aabc96a52/djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f", size = 410972, upload-time = "2024-12-24T13:06:24.077Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/31/56469120394b970d4f079a552fde21ed27702ca729595ab0ed459eb6d240/djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4", size = 362053, upload-time = "2024-12-24T13:06:25.432Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/67/f7aeea9be6fb3bd984487af8d0d80225a0b1e5f6f7126e3332d349fb13fe/djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd", size = 52290, upload-time = "2024-12-24T13:06:33.76Z" },
]
[[package]]
-name = "pytest"
-version = "9.0.3"
+name = "editorconfig"
+version = "0.17.1"
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" }
+sdist = { url = "https://files.pythonhosted.org/packages/88/3a/a61d9a1f319a186b05d14df17daea42fcddea63c213bcd61a929fb3a6796/editorconfig-0.17.1.tar.gz", hash = "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745", size = 14695, upload-time = "2025-06-09T08:21:37.097Z" }
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" },
+ { url = "https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl", hash = "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", size = 16360, upload-time = "2025-06-09T08:21:35.654Z" },
]
[[package]]
-name = "pytest-django"
-version = "4.12.0"
-source = { registry = "https://pypi.org/simple" }
+name = "ether"
+version = "0.1.0"
+source = { virtual = "." }
dependencies = [
- { name = "pytest" },
+ { name = "django" },
]
-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.dev-dependencies]
+dev = [
+ { name = "djlint" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+requires-dist = [{ name = "django", specifier = ">=6.0" }]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "djlint", specifier = "==1.36.4" },
+ { name = "ruff", specifier = "==0.15.12" },
]
[[package]]
-name = "pytest-sugar"
-version = "1.1.1"
+name = "jsbeautifier"
+version = "1.15.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "pytest" },
- { name = "termcolor" },
+ { name = "editorconfig" },
+ { name = "six" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/0b/4e/60fed105549297ba1a700e1ea7b828044842ea27d72c898990510b79b0e2/pytest-sugar-1.1.1.tar.gz", hash = "sha256:73b8b65163ebf10f9f671efab9eed3d56f20d2ca68bda83fa64740a92c08f65d", size = 16533, upload-time = "2025-08-23T12:19:35.737Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ea/98/d6cadf4d5a1c03b2136837a435682418c29fdeb66be137128544cecc5b7a/jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", size = 75257, upload-time = "2025-02-27T17:53:53.252Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/87/d5/81d38a91c1fdafb6711f053f5a9b92ff788013b19821257c2c38c1e132df/pytest_sugar-1.1.1-py3-none-any.whl", hash = "sha256:2f8319b907548d5b9d03a171515c1d43d2e38e32bd8182a1781eb20b43344cc8", size = 11440, upload-time = "2025-08-23T12:19:34.894Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528", size = 94707, upload-time = "2025-02-27T17:53:46.152Z" },
]
[[package]]
-name = "python-discovery"
-version = "1.2.2"
+name = "json5"
+version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filelock" },
- { name = "platformdirs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656, upload-time = "2026-03-27T22:50:48.108Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271, upload-time = "2026-03-27T22:50:47.073Z" },
]
[[package]]
-name = "python-slugify"
-version = "8.0.4"
+name = "pathspec"
+version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "text-unidecode" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921, upload-time = "2024-02-08T18:32:45.488Z" }
+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/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" },
+ { 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]]
@@ -1186,6 +157,16 @@ 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/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
@@ -1196,27 +177,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
-]
-
-[[package]]
-name = "qrcode"
-version = "8.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" },
-]
-
-[[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" },
+ { 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]]
@@ -1225,6 +203,22 @@ version = "2026.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000, upload-time = "2026-04-03T20:56:28.155Z" }
wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/28/b972a4d3df61e1d7bcf1b59fdb3cddef22f88b6be43f161bb41ebc0e4081/regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52", size = 490434, upload-time = "2026-04-03T20:53:40.219Z" },
+ { url = "https://files.pythonhosted.org/packages/84/20/30041446cf6dc3e0eab344fc62770e84c23b6b68a3b657821f9f80cb69b4/regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb", size = 292061, upload-time = "2026-04-03T20:53:41.862Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c8/3baa06d75c98c46d4cc4262b71fd2edb9062b5665e868bca57859dadf93a/regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76", size = 289628, upload-time = "2026-04-03T20:53:43.701Z" },
+ { url = "https://files.pythonhosted.org/packages/31/87/3accf55634caad8c0acab23f5135ef7d4a21c39f28c55c816ae012931408/regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be", size = 796651, upload-time = "2026-04-03T20:53:45.379Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/0c/aaa2c83f34efedbf06f61cb1942c25f6cf1ee3b200f832c4d05f28306c2e/regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1", size = 865916, upload-time = "2026-04-03T20:53:47.064Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f6/8c6924c865124643e8f37823eca845dc27ac509b2ee58123685e71cd0279/regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13", size = 912287, upload-time = "2026-04-03T20:53:49.422Z" },
+ { url = "https://files.pythonhosted.org/packages/11/0e/a9f6f81013e0deaf559b25711623864970fe6a098314e374ccb1540a4152/regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9", size = 801126, upload-time = "2026-04-03T20:53:51.096Z" },
+ { url = "https://files.pythonhosted.org/packages/71/61/3a0cc8af2dc0c8deb48e644dd2521f173f7e6513c6e195aad9aa8dd77ac5/regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d", size = 776788, upload-time = "2026-04-03T20:53:52.889Z" },
+ { url = "https://files.pythonhosted.org/packages/64/0b/8bb9cbf21ef7dee58e49b0fdb066a7aded146c823202e16494a36777594f/regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3", size = 785184, upload-time = "2026-04-03T20:53:55.627Z" },
+ { url = "https://files.pythonhosted.org/packages/99/c2/d3e80e8137b25ee06c92627de4e4d98b94830e02b3e6f81f3d2e3f504cf5/regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0", size = 859913, upload-time = "2026-04-03T20:53:57.249Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/9d5d876157d969c804622456ef250017ac7a8f83e0e14f903b9e6df5ce95/regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043", size = 765732, upload-time = "2026-04-03T20:53:59.428Z" },
+ { url = "https://files.pythonhosted.org/packages/82/80/b568935b4421388561c8ed42aff77247285d3ae3bb2a6ca22af63bae805e/regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244", size = 852152, upload-time = "2026-04-03T20:54:01.505Z" },
+ { url = "https://files.pythonhosted.org/packages/39/29/f0f81217e21cd998245da047405366385d5c6072048038a3d33b37a79dc0/regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73", size = 789076, upload-time = "2026-04-03T20:54:03.323Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1d/1d957a61976ab9d4e767dd4f9d04b66cc0c41c5e36cf40e2d43688b5ae6f/regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f", size = 266700, upload-time = "2026-04-03T20:54:05.639Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/5c/bf575d396aeb58ea13b06ef2adf624f65b70fafef6950a80fc3da9cae3bc/regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b", size = 277768, upload-time = "2026-04-03T20:54:07.312Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/27/049df16ec6a6828ccd72add3c7f54b4df029669bea8e9817df6fff58be90/regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983", size = 270568, upload-time = "2026-04-03T20:54:09.484Z" },
{ url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273, upload-time = "2026-04-03T20:54:11.202Z" },
{ url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954, upload-time = "2026-04-03T20:54:13.412Z" },
{ url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487, upload-time = "2026-04-03T20:54:15.824Z" },
@@ -1257,30 +251,38 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921, upload-time = "2026-04-03T20:55:09.62Z" },
{ url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240, upload-time = "2026-04-03T20:55:11.521Z" },
{ url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440, upload-time = "2026-04-03T20:55:13.365Z" },
-]
-
-[[package]]
-name = "requests"
-version = "2.33.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "certifi" },
- { name = "charset-normalizer" },
- { name = "idna" },
- { name = "urllib3" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" },
-]
-
-[[package]]
-name = "roman-numerals"
-version = "4.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343, upload-time = "2026-04-03T20:55:15.241Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909, upload-time = "2026-04-03T20:55:17.558Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692, upload-time = "2026-04-03T20:55:20.561Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979, upload-time = "2026-04-03T20:55:22.56Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744, upload-time = "2026-04-03T20:55:24.646Z" },
+ { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613, upload-time = "2026-04-03T20:55:27.033Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551, upload-time = "2026-04-03T20:55:29.532Z" },
+ { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911, upload-time = "2026-04-03T20:55:31.526Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751, upload-time = "2026-04-03T20:55:33.797Z" },
+ { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484, upload-time = "2026-04-03T20:55:35.745Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939, upload-time = "2026-04-03T20:55:37.972Z" },
+ { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417, upload-time = "2026-04-03T20:55:39.92Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056, upload-time = "2026-04-03T20:55:42.303Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130, upload-time = "2026-04-03T20:55:44.995Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992, upload-time = "2026-04-03T20:55:47.316Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563, upload-time = "2026-04-03T20:55:49.273Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191, upload-time = "2026-04-03T20:55:51.258Z" },
+ { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877, upload-time = "2026-04-03T20:55:53.214Z" },
+ { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410, upload-time = "2026-04-03T20:55:55.736Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831, upload-time = "2026-04-03T20:55:57.802Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199, upload-time = "2026-04-03T20:56:00.333Z" },
+ { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649, upload-time = "2026-04-03T20:56:02.445Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388, upload-time = "2026-04-03T20:56:04.595Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746, upload-time = "2026-04-03T20:56:07.13Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483, upload-time = "2026-04-03T20:56:09.365Z" },
+ { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331, upload-time = "2026-04-03T20:56:12.039Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673, upload-time = "2026-04-03T20:56:14.558Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146, upload-time = "2026-04-03T20:56:16.663Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463, upload-time = "2026-04-03T20:56:18.923Z" },
+ { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709, upload-time = "2026-04-03T20:56:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622, upload-time = "2026-04-03T20:56:23.641Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773, upload-time = "2026-04-03T20:56:26.07Z" },
]
[[package]]
@@ -1317,114 +319,6 @@ 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 = "snowballstemmer"
-version = "3.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" },
-]
-
-[[package]]
-name = "sphinx"
-version = "9.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "alabaster" },
- { name = "babel" },
- { name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "docutils" },
- { name = "imagesize" },
- { name = "jinja2" },
- { name = "packaging" },
- { name = "pygments" },
- { name = "requests" },
- { name = "roman-numerals" },
- { name = "snowballstemmer" },
- { name = "sphinxcontrib-applehelp" },
- { name = "sphinxcontrib-devhelp" },
- { name = "sphinxcontrib-htmlhelp" },
- { name = "sphinxcontrib-jsmath" },
- { name = "sphinxcontrib-qthelp" },
- { name = "sphinxcontrib-serializinghtml" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" },
-]
-
-[[package]]
-name = "sphinx-autobuild"
-version = "2025.8.25"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama" },
- { name = "sphinx" },
- { name = "starlette" },
- { name = "uvicorn" },
- { name = "watchfiles" },
- { name = "websockets" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d7/20/56411b52f917696995f5ad27d2ea7e9492c84a043c5b49a3a3173573cd93/sphinx_autobuild-2025.8.25-py3-none-any.whl", hash = "sha256:b750ac7d5a18603e4665294323fd20f6dcc0a984117026d1986704fa68f0379a", size = 12535, upload-time = "2025-08-25T18:44:54.164Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-applehelp"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-devhelp"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-htmlhelp"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-jsmath"
-version = "1.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-qthelp"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" },
-]
-
-[[package]]
-name = "sphinxcontrib-serializinghtml"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" },
-]
-
[[package]]
name = "sqlparse"
version = "0.5.5"
@@ -1434,50 +328,6 @@ 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 = "stack-data"
-version = "0.6.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asttokens" },
- { name = "executing" },
- { name = "pure-eval" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
-]
-
-[[package]]
-name = "starlette"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "anyio" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" },
-]
-
-[[package]]
-name = "termcolor"
-version = "3.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" },
-]
-
-[[package]]
-name = "text-unidecode"
-version = "1.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" },
-]
-
[[package]]
name = "tqdm"
version = "4.67.3"
@@ -1490,33 +340,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
]
-[[package]]
-name = "traitlets"
-version = "5.14.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
-]
-
-[[package]]
-name = "types-pyyaml"
-version = "6.0.12.20260408"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/74/73/b759b1e413c31034cc01ecdfb96b38115d0ab4db55a752a3929f0cd449fd/types_pyyaml-6.0.12.20260408.tar.gz", hash = "sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307", size = 17735, upload-time = "2026-04-08T04:30:50.974Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1c/f0/c391068b86abb708882c6d75a08cd7d25b2c7227dab527b3a3685a3c635b/types_pyyaml-6.0.12.20260408-py3-none-any.whl", hash = "sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384", size = 20339, upload-time = "2026-04-08T04:30:50.113Z" },
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.15.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
-]
-
[[package]]
name = "tzdata"
version = "2026.2"
@@ -1525,148 +348,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35c
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 = "urllib3"
-version = "2.6.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
-]
-
-[[package]]
-name = "uvicorn"
-version = "0.46.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "h11" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" },
-]
-
-[[package]]
-name = "virtualenv"
-version = "21.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "distlib" },
- { name = "filelock" },
- { name = "platformdirs" },
- { name = "python-discovery" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3f/8b/6331f7a7fe70131c301106ec1e7cf23e2501bf7d4ca3636805801ca191bb/virtualenv-21.3.0.tar.gz", hash = "sha256:733750db978ec95c2d8eb4feadaa57091002bce404cb39ba69899cf7bd28944e", size = 7614069, upload-time = "2026-04-27T17:05:58.927Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl", hash = "sha256:4d28ee41f6d9ec8f1f00cd472b9ffbcedda1b3d3b9a575b5c94a2d004fd51bd7", size = 7594690, upload-time = "2026-04-27T17:05:55.468Z" },
-]
-
-[[package]]
-name = "watchdog"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
- { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
- { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
- { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
- { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
- { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
- { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
- { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
- { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
- { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
- { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
- { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
- { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
-]
-
-[[package]]
-name = "watchfiles"
-version = "1.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "anyio" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" },
- { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" },
- { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" },
- { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" },
- { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" },
- { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" },
- { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" },
- { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" },
- { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" },
- { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" },
- { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" },
- { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" },
- { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" },
- { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" },
- { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" },
- { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" },
- { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" },
- { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" },
- { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" },
- { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" },
- { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" },
- { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" },
- { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" },
-]
-
-[[package]]
-name = "wcwidth"
-version = "0.6.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
-]
-
-[[package]]
-name = "websockets"
-version = "16.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" },
- { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" },
- { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" },
- { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" },
- { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" },
- { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" },
- { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" },
- { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" },
- { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" },
- { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
-]
-
-[[package]]
-name = "werkzeug"
-version = "3.1.8"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" },
-]
-
-[package.optional-dependencies]
-watchdog = [
- { name = "watchdog" },
-]
-
-[[package]]
-name = "whitenoise"
-version = "6.12.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cb/2a/55b3f3a4ec326cd077c1c3defeee656b9298372a69229134d930151acd01/whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad", size = 26841, upload-time = "2026-02-27T00:05:42.028Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/db/eb/d5583a11486211f3ebd4b385545ae787f32363d453c19fffd81106c9c138/whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2", size = 20302, upload-time = "2026-02-27T00:05:40.086Z" },
-]
diff --git a/vercel.json b/vercel.json
deleted file mode 100644
index c5ab2fa..0000000
--- a/vercel.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "$schema": "https://openapi.vercel.sh/vercel.json",
- "framework": null,
- "installCommand": "python3 -m venv .venv && .venv/bin/python -m pip install --disable-pip-version-check django==6.0.4",
- "buildCommand": ".venv/bin/python scripts/export_static_site.py",
- "outputDirectory": "dist"
-}
From bcf5fb7b1137c42aeaac23575db56d9a5b1956e5 Mon Sep 17 00:00:00 2001
From: masudaso
Date: Tue, 5 May 2026 00:31:19 +0900
Subject: [PATCH 2/2] fix(ci): drop Docker test job, apply pre-commit
auto-fixers, ignore djlint D018
- Replace docker-compose-based pytest job with a uv-based Django sanity
job (check + migrate + collectstatic). The previous job referenced
docker-compose.local.yml which was removed in this PR.
- django-upgrade dropped DEFAULT_AUTO_FIELD (BigAutoField is the default
in Django 6).
- end-of-file-fixer added trailing newlines on config/urls.py and
ether/static/css/project.css.
- pyproject-fmt added Python 3.12/3.14 classifiers.
- Ignore djlint D018: hardcoded internal links are intentional because
the language switching produces conditional /en/ vs / paths that don't
fit the {% url %} pattern cleanly.
---
.github/workflows/ci.yml | 65 +++++++++++++-----------------------
config/settings.py | 2 --
config/urls.py | 1 -
ether/static/css/project.css | 1 -
pyproject.toml | 6 ++--
5 files changed, 28 insertions(+), 47 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 52855ed..adf6aca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,18 +1,11 @@
name: CI
-# Enable Buildkit and let compose use it to speed up image building
-env:
- DOCKER_BUILDKIT: 1
- COMPOSE_DOCKER_CLI_BUILD: 1
-
on:
pull_request:
branches: ['main']
- paths-ignore: ['docs/**']
push:
branches: ['main']
- paths-ignore: ['docs/**']
concurrency:
group: ${{ github.head_ref || github.run_id }}
@@ -22,58 +15,48 @@ jobs:
linter:
runs-on: ubuntu-latest
steps:
- - name: Checkout Code Repository
+ - name: Checkout
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version-file: '.python-version'
+
- name: Run pre-commit
uses: pre-commit/action@v3.0.1
- # With no caching at all the entire ci process takes 3m to complete!
- pytest:
+ django:
runs-on: ubuntu-latest
steps:
- - name: Checkout Code Repository
+ - name: Checkout
uses: actions/checkout@v6
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v4
-
- - name: Build and cache local backend
- uses: docker/bake-action@v7
+ - name: Install uv
+ uses: astral-sh/setup-uv@v6
with:
- push: false
- load: true
- files: docker-compose.local.yml
- targets: django
- set: |
- django.cache-from=type=gha,scope=django-cached-tests
- django.cache-to=type=gha,scope=django-cached-tests,mode=max
- postgres.cache-from=type=gha,scope=postgres-cached-tests
- postgres.cache-to=type=gha,scope=postgres-cached-tests,mode=max
+ enable-cache: true
- - name: Build and cache docs
- uses: docker/bake-action@v7
+ - name: Set up Python
+ uses: actions/setup-python@v6
with:
- push: false
- load: true
- files: docker-compose.docs.yml
- set: |
- docs.cache-from=type=gha,scope=cached-docs
- docs.cache-to=type=gha,scope=cached-docs,mode=max
+ python-version-file: '.python-version'
- - name: Check DB Migrations
- run: docker compose -f docker-compose.local.yml run --rm django python manage.py makemigrations --check
+ - name: Install dependencies
+ run: uv sync --frozen
- - name: Run DB Migrations
- run: docker compose -f docker-compose.local.yml run --rm django python manage.py migrate
+ - name: Django system check
+ env:
+ DJANGO_SECRET_KEY: ci-secret-key
+ run: uv run python manage.py check
- - name: Run Django Tests
- run: docker compose -f docker-compose.local.yml run django pytest
+ - name: Run migrations
+ env:
+ DJANGO_SECRET_KEY: ci-secret-key
+ run: uv run python manage.py migrate --run-syncdb
- - name: Tear down the Stack
- run: docker compose -f docker-compose.local.yml down
+ - name: Collectstatic
+ env:
+ DJANGO_SECRET_KEY: ci-secret-key
+ run: uv run python manage.py collectstatic --noinput
diff --git a/config/settings.py b/config/settings.py
index 25b6a25..28f1d9d 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -87,5 +87,3 @@
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_DIRS = [BASE_DIR / "ether" / "static"]
-
-DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
diff --git a/config/urls.py b/config/urls.py
index ba42593..e53ad32 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -115,4 +115,3 @@ def _page(template: str, lang: str, page_name: str, alt_url: str) -> TemplateVie
),
path("admin/", admin.site.urls),
]
-
diff --git a/ether/static/css/project.css b/ether/static/css/project.css
index ad63895..fd54212 100644
--- a/ether/static/css/project.css
+++ b/ether/static/css/project.css
@@ -1780,4 +1780,3 @@ h4 {
flex-wrap: wrap;
}
}
-
diff --git a/pyproject.toml b/pyproject.toml
index a3fed24..fcf54e7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,10 +10,12 @@ authors = [
requires-python = ">=3.12"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
]
dependencies = [
- "django>=6.0",
+ "django>=6",
]
[dependency-groups]
@@ -61,7 +63,7 @@ blank_line_after_tag = "load,extends"
close_void_tags = true
format_css = true
format_js = true
-ignore = "H006,H030,H031,T002"
+ignore = "H006,H030,H031,T002,D018"
include = "H017,H035"
indent = 2
max_line_length = 119