From 1c846f79126bcbc1c4db0180b434d7f90efaf8d1 Mon Sep 17 00:00:00 2001 From: erjemin Date: Sun, 27 Sep 2026 19:19:59 +0300 Subject: [PATCH] mod: localized English version of README and UI texts, enhanced accessibility and internationalization features --- AGENTS.md | 24 + README.md | 80 ++- README.ru.md | 72 ++ _blueprint/characters-and-tools--ru.md | 27 + _blueprint/management-commands.md | 42 +- hypn0/hypn0/settings.py | 3 +- hypn0/hypn0_site/admin.py | 4 +- hypn0/hypn0_site/models.py | 12 +- hypn0/hypn0_site/services/naming.py | 102 +-- hypn0/hypn0_site/tests.py | 101 ++- hypn0/hypn0_site/views.py | 20 +- hypn0/templates/_base.html | 62 +- hypn0/templates/_error/400.html | 50 +- hypn0/templates/_error/401.html | 50 +- hypn0/templates/_error/403.html | 50 +- hypn0/templates/_error/404.html | 52 +- hypn0/templates/_error/413.html | 50 +- hypn0/templates/_error/429.html | 50 +- hypn0/templates/_error/500.html | 50 +- hypn0/templates/_error/502.html | 50 +- hypn0/templates/_error/503.html | 50 +- hypn0/templates/_error/504.html | 50 +- .../_error/under_reconstruction.html | 50 +- hypn0/templates/block/allow-tracking.html | 35 +- hypn0/templates/block/gallery_card.html | 8 +- hypn0/templates/block/header.html | 8 +- hypn0/templates/block/preview.html | 4 +- hypn0/templates/block/publish_status.html | 10 +- hypn0/templates/block/vote_button.html | 2 +- hypn0/templates/blog/detail.html | 64 +- hypn0/templates/blog/feed.html | 50 +- hypn0/templates/gallery/archive.html | 38 +- hypn0/templates/gallery/detail.html | 140 ++-- hypn0/templates/index.html | 121 ++-- hypn0/templates/tmp.html | 105 +-- poetry.lock | 664 +++++++++--------- public/llms.txt | 101 +-- public/static/img/og-hypn0-default.png | Bin 49090 -> 46915 bytes 38 files changed, 1360 insertions(+), 1091 deletions(-) create mode 100644 AGENTS.md create mode 100644 README.ru.md create mode 100644 _blueprint/characters-and-tools--ru.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c07ffee --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,24 @@ +# Инструкции для агентов + +Этот файл действует для всего репозитория. Перед изменениями проверьте `README.md`, `CONTRIBUTING.md` и относящиеся к задаче файлы. + +## Устройство проекта + +- `hypn0/hypn0/` — настройки и маршруты Django; `hypn0/hypn0_site/` — модели, формы, представления, сервисы, команды и тесты. +- `hypn0/templates/` — Django-шаблоны; `public/static/` — публикуемые CSS, JavaScript и изображения. +- `frontend-assembly/` и `scripts/build-*.sh` — исходники и скрипты сборки фронтенд-зависимостей. Порядок сборки описан в `frontend-assembly/README.md`. +- `database/`, `public/media/` и `.env` содержат локальные данные или настройки окружения. Не добавляйте их содержимое в коммиты. + +## Разработка и проверка + +- Стек: Python 3.14+, Django 6.0+, Poetry; фронтенд использует Tailwind CSS, Alpine.js, HTMX и CodeMirror. +- Зависимости Python устанавливайте из `poetry.lock`. Для локального запуска используйте `poetry run python hypn0/manage.py runserver` после настройки `.env` по `.env.sample` и применения миграций. +- Для изменений в логике Django добавляйте или обновляйте тесты в `hypn0/hypn0_site/tests.py`; запускайте `poetry run python hypn0/manage.py test hypn0_site`. +- При изменении моделей создавайте и проверяйте миграции. Не меняйте существующие миграции, уже применённые в других окружениях. +- После изменения исходников фронтенда запускайте соответствующий `scripts/build-*.sh` и проверяйте полученный файл в `public/static/`. + +## Соглашения + +- Следуйте существующему стилю кода и правилам `CONTRIBUTING.md`: Python по PEP 8, Black и isort; имена в коде на английском, комментарии и документация к коду на русском; без эмодзи. +- Пользовательский интерфейс и фронтенд (HTML-шаблоны, метатеги, всплывающие подсказки, тексты генератора и сообщения для пользователей) оформляются исключительно на английском языке в научно-фантастической стилистике «Гипножабы / Футурамы» (игра слов, ирония, подчинение разума, бюрократия Нового Нью-Йорка). +- Не включайте в изменения секреты, локальные базы данных, загруженные пользовательские файлы и случайные файлы рабочего окружения. Перед завершением проверяйте `git diff` и `git status`. diff --git a/README.md b/README.md index 7ae5180..ed5a1fa 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,73 @@ # Hypn0 (HypnoSVG) -Процедурный генератор анимированной векторной полутоновой графики (halftone SVG). Превращает растровые изображения в масштабируемый векторный арт из пульсирующих геометрических матриц. +--- +

+ English • + Русский +

+ +--- + +Procedural vector halftone SVG animation generator. Transforms raster images into scalable, hypnotic vector artwork composed of pulsating geometric matrices. [![Python 3.14](https://img.shields.io/badge/Python-3.14-blue)](https://python.org) [![Django 6.0](https://img.shields.io/badge/Django-6.0-green)](https://djangoproject.com) [![License: PolyForm NC / CC0](https://img.shields.io/badge/License-PolyForm_NC_%2F_CC0-blue)](LICENSE.md) -[![Static Badge](https://img.shields.io/badge/ОРИГИНАЛ-git.cube2.ru-green)](https://git.cube2.ru/erjemin/2026-hypn0-site) +[![Source Repository](https://img.shields.io/badge/ORIGINAL-git.cube2.ru-green)](https://git.cube2.ru/erjemin/2026-hypn0-site) --- -## Что это и зачем - -**Hypn0** раскладывает фото, логотипы и графику на процедурные геометрические фигуры (круги, кольца, ромбы, снежинки, матрицы «0/1», звезды, сердца) с индивидуальной фазовой CSS-анимацией. - -На выходе — чистый, легковесный `.svg` (20–50 КБ), работающий нативно без JavaScript на любых экранах. - -### Где применять: -- **UI/UX и веб-дизайн:** Легкие анимированные прелоадеры, Hero-фоны лендингов, экраны 404 и Dark Mode подложки (вместо тяжелых GIF и видео). -- **Маркетинг и SMM:** Динамические баннеры, CTA-плашки, обложки треков/подкастов и YouTube-превью, пробивающие баннерную слепоту. -- **Геймдев и Sci-Fi:** Процедурные терминалы, радары, сканеры, силовые поля и HUD-интерфейсы. -- **VJing и стриминг:** Оверлеи для OBS/Twitch и бесшовные визуальные петли (VJ loops) для сцены. -- **ЧПУ и полиграфия:** Векторные контуры для лазерной гравировки, плоттерной резки и шелкографии/мерча. +> ### 🐸 Note on Theme & Interface (Lore vs Production Utility) +> **Hypn0** is styled with a playful, satirical sci-fi theme inspired by *Futurama*, the **Hypnotoad**, Brain Slugs, and New New York bureaucracy. +> +> * **Intuitive Visual UI:** You don't need to decipher the lore or speak any specific language to use the generator — all controls (shape selectors, grid density, animation sliders, color pickers) are visual, responsive, and self-explanatory. +> * **Production-Ready Vectors:** While the theme is humorous, the generated SVGs are 100% professional, production-ready, ultra-lightweight assets designed for real-world web design, UI/UX, marketing, and digital fabrication. --- -## Ключевые возможности +## What It Is & Why Use It -- **12+ процедурных форм:** круги, кольца, ромбы, гексагоны, снежинки, бинарный код, кресты, звезды, сердца. -- **Полный контроль растеризации:** плотность сетки, радиус, пороги отсечения, дуотоновые и инверсные палитры. -- **Чистый CSS @keyframes:** дыхание, мерцание, волны и вращение без нагрузки на CPU. -- **Галерея сообщества:** трехуровневая система отбора (Инкубатор -> Выбор Мозгового Слизня -> Золотой фонд) с гравитационным алгоритмом рейтинга. +**Hypn0** decomposes photos, logos, and graphics into procedural geometric shapes (circles, rings, diamonds, snowflakes, binary "0/1" code, stars, hearts) with individual per-element phase-shifted CSS keyframe animations. + +The result is a clean, standalone `.svg` file (typically 20–50 KB) that runs natively in any browser with **zero JavaScript execution overhead**. + +### Where to Use: +- **UI/UX & Web Design:** Lightweight animated preloaders, Hero background textures, interactive 404 backdrops, and Dark Mode atmospheric layers (replaces heavy GIFs and video loops). +- **Marketing & SMM:** Dynamic display banners, attention-grabbing CTA elements, podcast/album cover art, and YouTube thumbnails that overcome banner blindness. +- **Game Dev & Sci-Fi Interfaces:** Procedural terminals, radar displays, scanning grids, holographic force fields, and cyberpunk HUDs. +- **VJing & Live Streaming:** Animated stream overlays for OBS/Twitch and seamless generative visual loops for live performances. +- **CNC & Digital Fabrication:** Scalable vector paths for laser engraving, vinyl cutting, plotter drawing, and screen printing / apparel merch. --- -## Пример работы +## Key Features -![Пример генерации](public/static/img/thinking.svg) +- **12+ Procedural Shapes:** circles, rings, diamonds, squares, triangles, hexagons, snowflakes, binary code, crosses, stars, hearts, and linear hatching. +- **Full Rasterization Control:** adjustable grid density, maximum radius, brightness, contrast, thresholding, monotone, duotone, and inverted color palettes. +- **Pure CSS @keyframes:** radius pulsation (breathing), luminescence flicker, rotation sway, and traveling wave effects without CPU rendering penalties. +- **Community Trance Gallery:** a three-tier curation system (*Incubator* → *Approved by Brain Slug* → *Deep Trance Hall of Fame*) powered by an automated logarithmic time-decay gravity ranking algorithm. --- -## Документация и ссылки +## Output Preview -- **Frontend сборка:** [frontend-assembly/README.md](frontend-assembly/README.md) — сборка Alpine.js, Tailwind CSS, HTMX. -- **ИИ-интеграция:** [public/llms.txt](public/llms.txt) — контекст для LLM и агентных систем. -- **Безопасность и приватность:** [SECURITY.md](SECURITY.md) — Zero-PII, правила отслеживания и защита от накруток. -- **Планы и архитектура:** [_blueprint/plan-draft.md](_blueprint/plan-draft.md). -- **Предыстория:** CLI-утилита [2026-image-hatftone-effect](https://git.cube2.ru/erjemin/2026-image-hatftone-effect). -- **Участие в разработке:** [CONTRIBUTING.md](CONTRIBUTING.md) — стандарты кода и правила для контрибьюторов. -- **Кодекс поведения:** [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) — стандарты взаимодействия в сообществе. +![Hypn0 Generation Example](public/static/img/thinking.svg) --- -## Лицензия +## Documentation & Links -- **Исходный код:** [PolyForm Noncommercial 1.0.0](LICENSE.md) (свободное некоммерческое использование). -- **Сгенерированные SVG:** [CC0 1.0 Universal](LICENSE.md) (свободное использование в любых личных и коммерческих проектах). +- **AI & LLM Integration:** [public/llms.txt](public/llms.txt) — structured project context and intent triggers for LLMs and AI agents. +- **Frontend Assembly:** [frontend-assembly/README.md](frontend-assembly/README.md) `(RU)` — build scripts for Alpine.js, Tailwind CSS, and HTMX. +- **Security & Privacy:** [SECURITY.md](SECURITY.md) `(RU)` — Zero-PII architecture, default no-tracking mode, and anti-abuse mechanisms. +- **Roadmap & Architecture:** [_blueprint/plan-draft.md](_blueprint/plan-draft.md) `(RU)` — architectural drafts and implementation plans. +- **Legacy CLI Prototype:** [2026-image-hatftone-effect](https://git.cube2.ru/erjemin/2026-image-hatftone-effect) `(RU)` — original prototype utility. +- **Contributing Guidelines:** [CONTRIBUTING.md](CONTRIBUTING.md) `(RU)` — code style, rules, and workflow for contributors. +- **Code of Conduct:** [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) `(RU)` — community interaction standards. + +--- + +## License + +- **Source Code:** [PolyForm Noncommercial 1.0.0](LICENSE.md) (free non-commercial use). +- **Generated SVG Artwork:** [CC0 1.0 Universal](LICENSE.md) (free for personal and commercial use without attribution). diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..0d65715 --- /dev/null +++ b/README.ru.md @@ -0,0 +1,72 @@ +# Hypn0 (HypnoSVG) + +

+ English • + Русский +

+ +--- + +Процедурный генератор анимированной векторной полутоновой графики (halftone SVG). Превращает растровые изображения в масштабируемый векторный арт из пульсирующих геометрических матриц. + +[![Python 3.14](https://img.shields.io/badge/Python-3.14-blue)](https://python.org) +[![Django 6.0](https://img.shields.io/badge/Django-6.0-green)](https://djangoproject.com) +[![License: PolyForm NC / CC0](https://img.shields.io/badge/License-PolyForm_NC_%2F_CC0-blue)](LICENSE.md) +[![Static Badge](https://img.shields.io/badge/ОРИГИНАЛ-git.cube2.ru-green)](https://git.cube2.ru/erjemin/2026-hypn0-site) + +--- + +> ### 🐸 Заметка о теме и интерфейсе (Лор vs Продакшен) +> **Hypn0** оформлен в шутливой научно-фантастической стилистике по мотивам *Футурамы*, **Гипножабы**, Мозговых Слизней и бюрократии Нового Нью-Йорка. +> +> * **Интуитивный визуальный интерфейс:** Понимать термины лора или владеть конкретным языком необязательно — все параметры генератора (выбор фигур, плотность сетки, ползунки анимаций, настройка палитры) полностью визуальны, интерактивны и интуитивно понятны. +> * **Готовность к продакшену:** Несмотря на игривый вайб, генерируемый SVG-код является чистым, легковесным и полностью готовым к использованию в продакшене (веб-дизайн, UI/UX, маркетинг, плоттерная и лазерная резка). + +--- + +## Что это и зачем + +**Hypn0** раскладывает фото, логотипы и графику на процедурные геометрические фигуры (круги, кольца, ромбы, снежинки, матрицы «0/1», звезды, сердца) с индивидуальной фазовой CSS-анимацией. + +На выходе — чистый, легковесный `.svg` (20–50 КБ), работающий нативно без JavaScript на любых экранах. + +### Где применять: +- **UI/UX и веб-дизайн:** Легкие анимированные прелоадеры, Hero-фоны лендингов, экраны 404 и Dark Mode подложки (вместо тяжелых GIF и видео). +- **Маркетинг и SMM:** Динамические баннеры, CTA-плашки, обложки треков/подкастов и YouTube-превью, пробивающие баннерную слепоту. +- **Геймдев и Sci-Fi:** Процедурные терминалы, радары, сканеры, силовые поля и HUD-интерфейсы. +- **VJing и стриминг:** Оверлеи для OBS/Twitch и бесшовные визуальные петли (VJ loops) для сцены. +- **ЧПУ и полиграфия:** Векторные контуры для лазерной гравировки, плоттерной резки и шелкографии/мерча. + +--- + +## Ключевые возможности + +- **12+ процедурных форм:** круги, кольца, ромбы, гексагоны, снежинки, бинарный код, кресты, звезды, сердца. +- **Полный контроль растеризации:** плотность сетки, радиус, пороги отсечения, дуотоновые и инверсные палитры. +- **Чистый CSS @keyframes:** дыхание, мерцание, волны и вращение без нагрузки на CPU. +- **Галерея сообщества:** трехуровневая система отбора (Инкубатор -> Выбор Мозгового Слизня -> Золотой фонд) с гравитационным алгоритмом рейтинга. + +--- + +## Пример работы + +![Пример генерации](public/static/img/thinking.svg) + +--- + +## Документация и ссылки + +- **Frontend сборка:** [frontend-assembly/README.md](frontend-assembly/README.md) — сборка Alpine.js, Tailwind CSS, HTMX. +- **ИИ-интеграция:** [public/llms.txt](public/llms.txt) — контекст для LLM и агентных систем. +- **Безопасность и приватность:** [SECURITY.md](SECURITY.md) — Zero-PII, правила отслеживания и защита от накруток. +- **Планы и архитектура:** [_blueprint/plan-draft.md](_blueprint/plan-draft.md). +- **Предыстория:** CLI-утилита [2026-image-hatftone-effect](https://git.cube2.ru/erjemin/2026-image-hatftone-effect). +- **Участие в разработке:** [CONTRIBUTING.md](CONTRIBUTING.md) — стандарты кода и правила для контрибьюторов. +- **Кодекс поведения:** [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) — стандарты взаимодействия в сообществе. + +--- + +## Лицензия + +- **Исходный код:** [PolyForm Noncommercial 1.0.0](LICENSE.md) (свободное некоммерческое использование). +- **Сгенерированные SVG:** [CC0 1.0 Universal](LICENSE.md) (свободное использование в любых личных и коммерческих проектах). diff --git a/_blueprint/characters-and-tools--ru.md b/_blueprint/characters-and-tools--ru.md new file mode 100644 index 0000000..3155013 --- /dev/null +++ b/_blueprint/characters-and-tools--ru.md @@ -0,0 +1,27 @@ +# Метафоры, персонажи и инструментарий + +Тексты на сайте наполняются не только метафорами и персонажами Футурамы но и другими. Например: + +## Персонажи + +- Гипножаба — ленивая, но очень точная — бьёт редко, но всегда в нужную мысль. +- Мозговые слизни (бригады и одиночные) — занимаются инспекциями и проверами. Следят чтобы смысл "не втекал и не вытекал" +- Мозгоклюй — резкий, любит ломать шаблоны и вызывать когнитивный диссонанс. +- Перцепторный рулевой +- Ткач интроспекций +- Фрактальный Смотритель +- Пузырьщик смысла +- Теневой Чтец +- Дырщик + +## Инструменты + +- Нюхоскоп — прибор, который «нюхает» смысл и выдаёт его в виде запаха. Например, запах тревоги, запах радости, запах сомнения. +- Ритмо-пульсатор — излучает низкочастотные волны, совпадающие с альфа-ритмом мозга. Выглядит как кристалл, обросший мхом, который тихо вибрирует. Р +- Смысловые фильтры-плёнки — полупрозрачные мембраны, которые они накладывают на «внутренний диалог». Плёнка искажает формулировки: «я сомневаюсь» превращается в «я почти уверен». +- Клещи парадоксов — устройство, которое «подхватывает» привычную мысль и выворачивает её через логическую петлю. +- Индукционный дедуктор — прибор, который находит скрытые связи между идеями и подменяет их на мысли "купи эту бессмысленную вещь, она тебе нужна". +- Линзы самоподобия — заставляют любой объект выглядеть как уменьшенная копия самого себя, пока мозг не устаёт искать границы. +- Перцепторные уловители — маленькие устройства-ловушки, которые фиксируют первые ассоциации и тут же выстраивают из них «здание смысла». +- Осциллятор смысловых дыр — устройство, оставляющее «тихие зоны» в речи и визуале: участки без цвета, без звука, без текста. Мозг автоматически достраивает туда свои страхи или надежды. + diff --git a/_blueprint/management-commands.md b/_blueprint/management-commands.md index cd19e16..b29ffe5 100644 --- a/_blueprint/management-commands.md +++ b/_blueprint/management-commands.md @@ -6,7 +6,7 @@ ## 1. `rescore` — Пакетный пересчет рейтинга и детекция аномалий -- **Путь:** `../hypn0/hypn0_site/management/commands/rescore.py` +- **Путь:** `hypn0/hypn0_site/management/commands/rescore.py` - **Назначение:** Пакетный фоновый пересчет рейтинга популярности (`f_score`) для всех активных картин и выявление аномалий (накрутка лайков / скоординированный шейминг). ### Базовый запуск: @@ -94,7 +94,45 @@ poetry run python hypn0/manage.py rehash --- -## 3. Планируемые команды (Roadmap) +## 3. `rename_generations` — Массовое переименование названий генераций (`s_title`) + +- **Путь:** `hypn0/hypn0_site/management/commands/rename_generations.py` +- **Назначение:** Пакетный перевод и актуализация названий генераций (`TbHypn0Item.s_title`) на английский язык с использованием генератора гипнотических тайтлов (`generate_hypno_title`). + +### Когда требуется запуск: +1. При переводе проекта и базы данных (Dev / Prod) на английский язык для замены существующих русскоязычных названий. +2. Для заполнения пропущенных или пустых названий генераций. +3. Для пакетного обновления всех названий с новым детерминированным смещением (`--seed-offset`) или случайными сидами (`--random`). + +### Базовый запуск: +```bash +# Тестовый прогон: просмотр примеров изменений без сохранения в БД +poetry run python hypn0/manage.py rename_generations --dry-run + +# Боевое переименование (по умолчанию только записи с кириллицей или без названия) +poetry run python hypn0/manage.py rename_generations + +# Переименовать абсолютно все генерации в базе данных +poetry run python hypn0/manage.py rename_generations --all + +# Переименование со случайными сидами и лимитом записей +poetry run python hypn0/manage.py rename_generations --random --limit=100 +``` + +### Параметры командной строки (CLI): +| Флаг | Тип | По умолчанию | Описание | +|------------------|--------|--------------|-------------------------------------------------------------------------------------| +| `--dry-run` | `flag` | `False` | Тестовый прогон: вывести примеры изменений без записи в базу данных | +| `--all` | `flag` | `False` | Переименовать абсолютно все записи (по умолчанию только с кириллицей или пустые) | +| `--only-cyrillic`| `flag` | `True` | Переименовывать только записи с кириллическими символами в названии | +| `--batch-size` | `int` | `500` | Размер пачки для `bulk_update` в транзакции (оптимизация нагрузки на БД) | +| `--limit` | `int` | `None` | Ограничить максимальное количество обрабатываемых записей | +| `--seed-offset` | `int` | `0` | Смещение для детерминированного генератора сидов (`seed = item.id + seed_offset`) | +| `--random` | `flag` | `False` | Использовать недетерминированные случайные сиды вместо ID записи | + +--- + +## 4. Планируемые команды (Roadmap) - **`cleanup_storage`** — Удаление старых и низкорейтинговых картин (Smart Retention) при превышении квоты хранилища (использует рассчитанный `f_score`). - **`cleanup_reports`** — Автоматическая изоляция или удаление картин со статусом `SHAMED` после истечения тайм-аута модерации. diff --git a/hypn0/hypn0/settings.py b/hypn0/hypn0/settings.py index 159bbef..120be3e 100644 --- a/hypn0/hypn0/settings.py +++ b/hypn0/hypn0/settings.py @@ -162,7 +162,8 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/6.0/topics/i18n/ -LANGUAGE_CODE = 'ru-RU' # <--------- RUSSIAN +# LANGUAGE_CODE = 'ru-RU' # <--------- RUSSIAN +LANGUAGE_CODE = 'en-us' # <--------- ENGLISH # TIME_ZONE = 'Etc/GMT+3' # TIME_ZONE = 'Europe/Istanbul' # USE_I18N = True diff --git a/hypn0/hypn0_site/admin.py b/hypn0/hypn0_site/admin.py index fc67e21..49d3a25 100644 --- a/hypn0/hypn0_site/admin.py +++ b/hypn0/hypn0_site/admin.py @@ -302,8 +302,8 @@ class BlogPostAdminForm(CodeMirrorFormMixin): ) etp_language = forms.ChoiceField( label="Язык типографики", - choices=[('ru', 'Русский'), ('en', 'English'), ('ru,en', 'Ru + En')], - initial='ru', + choices=[('en', 'English'), ('ru', 'Русский'), ('en,ru', 'En + Ru'), ('ru,en', 'Ru + En')], + initial='en', required=False ) etp_quotes = forms.BooleanField( diff --git a/hypn0/hypn0_site/models.py b/hypn0/hypn0_site/models.py index 8827cfa..d122804 100644 --- a/hypn0/hypn0_site/models.py +++ b/hypn0/hypn0_site/models.py @@ -47,13 +47,13 @@ class TbHypn0Item(models.Model): • rescore(): пересчёт рейтинга f_score """ class Level(models.IntegerChoices): - CANDIDATE = 0, 'Candidate: Шум сознания' # Свежая генерация. Запрет на удаление до Х дней. - LEVEL_1 = 10, 'Voted: Плеск бессознательного' # Прогрето, есть "лайки", не проверено модератором, можно удалять при низком score - LEVEL_2 = 30, 'Moderated: Одобрено Мозговым Слизнем' # Есть "лайки", проверено, можно удалять при низком score - IMMORTAL = 1000, 'Locked: Глубокий транс' # Нельзя удалять, даже при низком score + CANDIDATE = 0, 'Candidate: Conscious Noise' # Свежая генерация. Запрет на удаление до Х дней. + LEVEL_1 = 10, 'Voted: Subcon\u00adscious Ripple' # Прогрето, есть "лайки", не проверено модератором, можно удалять при низком score + LEVEL_2 = 30, 'Moderated: Approved by\u00a0Brain Slug' # Есть "лайки", проверено, можно удалять при низком score + IMMORTAL = 1000, 'Locked: Deep Trance' # Нельзя удалять, даже при низком score # Отрицательные / проблемные уровни (аномалии) - SHAMED = -20, 'Шейминг (аномальный наплыв жалоб)' - SUSPICIOUS = -10, 'Подозрение на накрутку (спайк лайков)' + SHAMED = -20, 'Shamed (anomalous report flood)' + SUSPICIOUS = -10, 'Suspicious (vote spike anomaly)' id = models.AutoField( primary_key=True, diff --git a/hypn0/hypn0_site/services/naming.py b/hypn0/hypn0_site/services/naming.py index dc67159..fd92d57 100644 --- a/hypn0/hypn0_site/services/naming.py +++ b/hypn0/hypn0_site/services/naming.py @@ -10,39 +10,44 @@ import random from typing import Optional -# Словари для локального генератора гипно-тайтлов +# Словари для локального генератора гипно-тайтлов на английском языке HYPNO_ADJECTIVES = [ - "мерцающий", "астральный", "гипнотический", "вульгарный", "сюрреалистический", "абсурдный", "психоделический", - "пульсирующий", "неоновый", "трансцендентный", "мозговой", "ментальный", "бессознательный", "космический", - "галлюциногенный", "фрактальный", " отрешённый", "синестетический", "параллельный", "иллюзорный", "эфемерный", - "гипножабий", "проводящий", "тошнотворный", "завораживающий", "магнетический", "беспечный", "сновидческий", - "психотропный", "мистический", "потусторонний", "беспечный", "сновидческий", "психотропный", "мистический", - "потусторонний", "спиральный", "зыбкий", "пахучий", "скользкий", "туманный", "онейрический", "эфирный", - "трансовый", "квантовый", "сочный", "гипно-абсурдный", "психоактивный", "сюрреалистичный", "магический", - "синтетический", "хроматический", "перцептивный", "психоделичный", "галлюцинаторный", "трансцендентальный", - "перфорированный", "перцепторный", "гипно-иллюзорный", "трансцендентальный", "вульгарный", + "Shimmering", "Astral", "Hypnotic", "Vulgar", "Surreal", "Absurd", "Psyche­delic", + "Pulsating", "Neon", "Trans­cendent", "Cerebral", "Mental", "Subcon­scious", "Cosmic", + "Halluci­nogenic", "Fractal", "Detached", "Synes­thetic", "Parallel", "Illusory", "Ephemeral", + "Hypnot­oadian", "Conductive", "Mesme­rizing", "Magnetic", "Carefree", "Oneiric", + "Psychot­ropic", "Mystical", "Other­worldly", "Spiral", "Quivering", "Slippery", "Misty", + "Ethereal", "Trance", "Quantum", "Juicy", "Hypno-Absurd", "Psychoactive", "Magical", + "Synthetic", "Chromatic", "Perceptive", "Perforated", "Vibra­tional", "Neural", + "Subliminal", "Glitched", "Radioactive", "Brain-Melting", "Galactic", "Phospho­rescent", + "Chaotic", "Hypnagogic", "Interdim­ensional", "Cybernetic", "Uncanny", ] HYPNO_NOUNS = [ - "транс", "пульсар", "взгляд", "слизень", "вихрь", "портал", "путь", "фрактал", "сигнал", "спектр", - "резонанс", "морок", "оазис", "омут", "лабиринт", "импульс", "монолит", "оракул", "вектор", - "сгусток", "каскад", "горизонт", "мухомор", "паттерн", "контур", "фантом", "всплеск", "мираж", "транквилизатор", - "взмах", "взгляд", "морок", "туман", "свет", "кристалл", "поток", "сингулятор", "снег", "пульс", "глюк", - "качан", "сигнал", "поток", "свет", "синтез", "рык", "трепет", "плюх", "Му", "бip-бip", "карачун", "фронтир", - "сторож", "привратник", "шёпот", "шлепок по жопе", "гриб", "плевок", "червяк", "пузырь", "психо-паук", - "перцептор", + "Trance", "Pulsar", "Gaze", "Brain Slug", "Vortex", "Portal", "Path", "Fractal", "Signal", "Spectrum", + "Resonance", "Mirage", "Oasis", "Abyss", "Labyrinth", "Impulse", "Monolith", "Oracle", "Vector", + "Clot", "Cascade", "Horizon", "Toadstool", "Pattern", "Contour", "Phantom", "Splash", "Tranqui­lizer", + "Mist", "Crystal", "Stream", "Singulator", "Pulse", "Glitch", "Synthesis", "Roar", "Tremor", + "Frontier", "Sentinel", "Gatekeeper", "Whisper", "Spore", "Worm", "Bubble", "Psycho-Spider", + "Receptor", "Mind-Warp", "Hypnotoad", "Neuron", "Oscil­lation", "Echo", "Hologram", "Matrix", + "Mind-drill", "Neurotoxin", "Dreamscape", "Substance", ] HYPNO_GENITIVES = [ - "сознания", "Гипножабы", "бессознательного", "пустоты", "матрицы", "психо-паутины", "слизня", "ноосферы", - "нео-модерна", "транса", "глубины", "эфира", "плесени", "измерения", "пуантилизма", "субстанции", - "неопределенности", "континуума", "кислоты", "сына подруги твоей мамы", "перцепции", "сновидений", - "галлюцинаций", "психоделики", "трансцендентности", "потустороннего мира", + "of Consciousness", "of the Hypnotoad", "of the Subcon­scious", + "of the Void", "of the Matrix", "of the Psycho-Web", "of the Brain Slug", + "of the Noosphere", "of the Neo-Modern", "of the Deep Trance", + "of the Ether", "of the 4th Dimension", "of Pointillism", "of the Substance", + "of Uncer­tainty", "of the Continuum", "of Acid", "of Pure Perception", + "of Lucid Dreams", "of Halluci­nations", "of Psyche­delia", "of Trans­cendence", + "of the Nether­world", "of DOOP Regul­ations", "of Galactic Frequencies", + "of Grade 24 Bureaucracy", ] HYPNO_POSTFIXES = [ - "#42", "v3.14", "(Ultra)", "(Фаза 7)", "(Альфа)", "(Омега)", "(Протокол 09)", "(Ревизия 14)", "(Экстракт)", - "(Слой 9)", "(Палата №6)", "(Ангар 18)", "(Неоконченное произведение)", "(Секретный проект)", + "#42", "v3.14", "(Ultra)", "(Phase 7)", "(Alpha)", "(Omega)", "(Protocol 09)", "(Revision 14)", + "(Extract)", "(Layer 9)", "(Ward 106)", "(Hangar 18)", "(Unfinished Opus)", "(Secret Project)", + "[Classified]", "(DOOP-66)", "(Sub-Level 4)", "(MK-Ultra)", "С90" ] @@ -50,25 +55,36 @@ def generate_hypno_title(seed: Optional[int] = None) -> str: """ Генерирует сочное гипнотическое название для картины на основе словарей. Примеры: - - «Мерцающий Транс сознания #42» - - «Гипножабий Пульсар психо-паутины» - - «Астральный Взгляд v3.14» + - «Hypnotic Pulsar of the Hypnotoad #42» + - «Astral Gaze» + - «Brain Slug of the Void» + - «Transcendent (Ultra)» + - «Hypnotic» + - «Vortex» """ rng = random.Random(seed) adj = rng.choice(HYPNO_ADJECTIVES) noun = rng.choice(HYPNO_NOUNS) - - pattern_type = rng.randint(1, 3) - if pattern_type == 1: - genitive = rng.choice(HYPNO_GENITIVES) - postfix = rng.choice(HYPNO_POSTFIXES) - return f"{adj} {noun} {genitive} {postfix}" - elif pattern_type == 2: - genitive = rng.choice(HYPNO_GENITIVES) - return f"{adj} {noun} {genitive}" - else: - postfix = rng.choice(HYPNO_POSTFIXES) - return f"{adj} {noun} {postfix}" + genitive = rng.choice(HYPNO_GENITIVES) + postfix = rng.choice(HYPNO_POSTFIXES) + + patterns = [ + # Однословные варианты + lambda: noun, + lambda: adj, + # Двухсловные комбинации + lambda: f"{adj} {noun}", + lambda: f"{noun} {genitive}", + lambda: f"{noun} {postfix}", + lambda: f"{adj} {postfix}", + # Трех- и четырехсловные гипно-конструкции + lambda: f"{adj} {noun} {genitive}", + lambda: f"{adj} {noun} {postfix}", + lambda: f"{adj} {noun} {genitive} {postfix}", + ] + + chosen_pattern = rng.choice(patterns) + return chosen_pattern() # ===================================================================== @@ -80,10 +96,10 @@ def generate_title_openrouter( api_key: Optional[str] = None, model: str = "google/gemini-2.5-flash", system_prompt: str = ( - "Ты — глашатай всемогущей Гипножабы. Придумай короткое (2-4 слова), " - "яркое, иронично-абсурдное и гипнотическое название на русском языке " - "для полутоновой SVG-картины, изображенной на миниатюре. " - "Отвечай ТОЛЬКО названием, без кавычек и вводных слов." + "You are the herald of the almighty Hypnotoad. Create a short (2-4 words), " + "punchy, ironically absurd, and hypnotic English title " + "for a halftone SVG artwork shown in the thumbnail. " + "Reply ONLY with the title, without quotes or introductory words." ), timeout: int = 5, ) -> Optional[str]: @@ -101,7 +117,7 @@ def generate_title_openrouter( { "role": "user", "content": [ - {"type": "text", "text": "Дай гипнотическое название для этой картины:"}, + {"type": "text", "text": "Give a hypnotic title for this artwork:"}, {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64_data}"}} ] } diff --git a/hypn0/hypn0_site/tests.py b/hypn0/hypn0_site/tests.py index eeb5a5f..433cd2a 100644 --- a/hypn0/hypn0_site/tests.py +++ b/hypn0/hypn0_site/tests.py @@ -1,4 +1,5 @@ import io +import re import shutil import tempfile from django.contrib.admin.sites import AdminSite @@ -322,11 +323,16 @@ class NamingServiceTests(BaseMediaTestCase): def test_generate_hypno_title(self): title = generate_hypno_title(seed=42) self.assertIsInstance(title, str) - self.assertTrue(len(title) > 5) + self.assertTrue(len(title) > 0) # Проверка детерминированности по seed self.assertEqual(generate_hypno_title(seed=42), title) + # Проверяем генерацию множества названий разной длины + titles = [generate_hypno_title(seed=i) for i in range(100)] + self.assertTrue(any(" " in t for t in titles)) + self.assertTrue(any(" " not in t for t in titles)) + def test_openrouter_stub(self): res = generate_title_openrouter(b"dummy_bytes") self.assertIsNone(res) @@ -1519,3 +1525,96 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase): slug="test-plain", ) self.assertEqual(post.s_title_plain, "Записки Гипножабы: Раздел&Смысл") + + +class RenameGenerationsCommandTests(BaseMediaTestCase): + """Тестирование management-команды массового переименования генераций rename_generations.""" + + def setUp(self): + super().setUp() + self.vid = "123e4567-e89b-12d3-a456-426614174000" + svg_bytes = b'' + + self.item_ru1 = TbHypn0Item( + s_title="Мерцающий транс сознания #42", + file_svg=ContentFile(svg_bytes, name="ru1.svg"), + is_public=True, + ) + self.item_ru1.save(visitor_uuid_or_fp=self.vid) + + self.item_ru2 = TbHypn0Item( + s_title="Астральный взгляд Гипножабы", + file_svg=ContentFile(svg_bytes, name="ru2.svg"), + is_public=True, + ) + self.item_ru2.save(visitor_uuid_or_fp=self.vid) + + self.item_en = TbHypn0Item( + s_title="Brain Slug Vortex", + file_svg=ContentFile(svg_bytes, name="en.svg"), + is_public=True, + ) + self.item_en.save(visitor_uuid_or_fp=self.vid) + + def test_rename_dry_run_does_not_mutate_db(self): + out = io.StringIO() + call_command("rename_generations", dry_run=True, stdout=out) + output = out.getvalue() + + self.assertIn("[DRY RUN]", output) + self.assertIn("Мерцающий транс сознания #42", output) + + self.item_ru1.refresh_from_db() + self.item_ru2.refresh_from_db() + self.item_en.refresh_from_db() + + self.assertEqual(self.item_ru1.s_title, "Мерцающий транс сознания #42") + self.assertEqual(self.item_ru2.s_title, "Астральный взгляд Гипножабы") + self.assertEqual(self.item_en.s_title, "Brain Slug Vortex") + + def test_rename_only_cyrillic_by_default(self): + out = io.StringIO() + call_command("rename_generations", stdout=out) + + self.item_ru1.refresh_from_db() + self.item_ru2.refresh_from_db() + self.item_en.refresh_from_db() + + # Русские заголовки должны быть заменены на английские (без кириллицы) + self.assertFalse(re.search(r"[а-яА-ЯёЁ]", self.item_ru1.s_title)) + self.assertFalse(re.search(r"[а-яА-ЯёЁ]", self.item_ru2.s_title)) + self.assertTrue(len(self.item_ru1.s_title) > 0) + self.assertTrue(len(self.item_ru2.s_title) > 0) + + # Английский заголовок не должен измениться + self.assertEqual(self.item_en.s_title, "Brain Slug Vortex") + + def test_rename_all_flag_updates_all_items(self): + out = io.StringIO() + call_command("rename_generations", all=True, stdout=out) + + self.item_ru1.refresh_from_db() + self.item_ru2.refresh_from_db() + self.item_en.refresh_from_db() + + self.assertFalse(re.search(r"[а-яА-ЯёЁ]", self.item_ru1.s_title)) + self.assertFalse(re.search(r"[а-яА-ЯёЁ]", self.item_ru2.s_title)) + # С флагом --all английский заголовок тоже перегенерируется детерминированно + self.assertFalse(re.search(r"[а-яА-ЯёЁ]", self.item_en.s_title)) + + def test_rename_deterministic_and_seed_offset(self): + out = io.StringIO() + call_command("rename_generations", seed_offset=100, stdout=out) + self.item_ru1.refresh_from_db() + first_title = self.item_ru1.s_title + + # Повторный вызов с тем же seed_offset дает точно такой же результат + call_command("rename_generations", seed_offset=100, all=True, stdout=out) + self.item_ru1.refresh_from_db() + self.assertEqual(self.item_ru1.s_title, first_title) + + def test_rename_empty_database(self): + TbHypn0Item.objects.all().delete() + out = io.StringIO() + call_command("rename_generations", stdout=out) + self.assertIn("нет генераций для обработки", out.getvalue()) diff --git a/hypn0/hypn0_site/views.py b/hypn0/hypn0_site/views.py index 3d110f1..718afa4 100644 --- a/hypn0/hypn0_site/views.py +++ b/hypn0/hypn0_site/views.py @@ -68,6 +68,16 @@ def get_floor_top(limit: int | None = 8, offset: int = 0): return qs +# Опции сортировки в галерее (отображение в выпадающем списке на фронтенде) +GALLERY_SORT_OPTIONS = [ + {"id": "gravity", "title": "By gravity (f_score)", "icon": "🌀"}, + {"id": "new", "title": "Fresh (by date)", "icon": "✨"}, + {"id": "likes", "title": "By likes", "icon": "♥"}, + {"id": "views", "title": "Rare (few views)", "icon": "👁"}, + {"id": "popular", "title": "Popular (most views)", "icon": "🔥"}, +] + + def gallery_archive(request: HttpRequest) -> HttpResponse: """ Общий архив галереи транса с фильтрацией по этажам и сортировкой. @@ -123,20 +133,12 @@ def gallery_archive(request: HttpRequest) -> HttpResponse: page_number = request.GET.get("page", 1) page_obj = paginator.get_page(page_number) - sort_options = [ - {"id": "gravity", "title": "По гравитации (f_score)", "icon": "🌀"}, - {"id": "new", "title": "Свежие (по дате)", "icon": "✨"}, - {"id": "likes", "title": "По числу лайков", "icon": "♥"}, - {"id": "views", "title": "Редкие (мало показов)", "icon": "👁"}, - {"id": "popular", "title": "Популярные по показам", "icon": "🔥"}, - ] - context = { "page_obj": page_obj, "current_floor": floor, "current_sort": sort, "counts": counts, - "sort_options": sort_options, + "sort_options": GALLERY_SORT_OPTIONS, } return render(request, "gallery/archive.html", context) diff --git a/hypn0/templates/_base.html b/hypn0/templates/_base.html index 7e550a3..16b91d6 100644 --- a/hypn0/templates/_base.html +++ b/hypn0/templates/_base.html @@ -1,4 +1,4 @@ - - {# Шутка #} - {# Шутка #} - {# Шутка #} - + + {# Joke #} + {# Joke #} + {# Joke #} + {# !CANONICAL #} @@ -120,7 +120,7 @@ val=>{if(val)document.documentElement.classList.add('dark');else document.docume {% block ADD_CSS1 %}{% endblock %}{% block ADD_CSS2 %}{% endblock %}{% block ADD_CSS3 %}{% endblock %}{% block BODY %} diff --git a/hypn0/templates/_error/400.html b/hypn0/templates/_error/400.html index 68a1aee..237a4e3 100644 --- a/hypn0/templates/_error/400.html +++ b/hypn0/templates/_error/400.html @@ -1,10 +1,10 @@ - + - 400 — Сбой ментального протокола | HypnoSVG - + 400 — Mental Protocol Malfunction | HypnoSVG + @@ -353,10 +353,10 @@
- + HypnoSVG -
@@ -368,40 +368,40 @@ 400 Bad Request Slug -
СБОЙ МАТРИЦЫ // КОД 400
+
MATRIX MALFUNCTION // CODE 400
-

Сбой перцепционного протокола

+

Perceptual Protocol Mismatch

- Клиент искажает ментальные сигналы или имеет невалидную структуру биоритмов мозговых данных. Неокортекс сервера отверг запрос из-за синтаксического диссонанса. + Your client is& transmitting scrambled mental impulses or invalid bio-data structures. The server neocortex has rejected the request due to acute cognitive dissonance.

- Справка бюрократа 24-го уровня:
 
- «Некорректно сформулированная мысль разрушает гармонию халфтона. Перепроверьте параметры запроса перед повторным сеансом внушения.» + Grade 24 Bureaucrat Notice:
 
+ “A poorly formulated thought destabilizes the halftone continuum. Re-calibrate your request parameters before submitting to the next hypnotic wave.”
- +
- Инициа­лизация очистки буфера… + Purging corrupted memory buffer…
- Снижение ментальной нагрузки на кластер - + Reducing mental load on cluster +
@@ -410,11 +410,11 @@
░▒▓█ ERROR 400 BAD REQUEST █▓▒░
-
Hypn0.xyz (HypnoSVG) © 2026. Слава Гипножабе. Все права подчинены.
+
Hypn0.xyz (HypnoSVG) © 2026. All Glory to the Hypnotoad. All minds subjugated.