diff --git a/CACHE_PRERENDER_SYSTEM.md b/CACHE_PRERENDER_SYSTEM.md new file mode 100644 index 0000000..ec0ebe9 --- /dev/null +++ b/CACHE_PRERENDER_SYSTEM.md @@ -0,0 +1,361 @@ +# Система двухуровневого кеширования страниц серий + +## 📖 Описание + +Система страниц серий домов использует **двухуровневое кеширование**: + +1. **Статический кеш** — дорогостоящие данные (карты, графики, схемы), генерируются один раз и сохраняются на диск +2. **Динамические данные** — верхняя статья (редактируется через админку) и таблица оконных проёмов (показывает свежие предложения), пересчитываются при каждом запросе + +--- + +## 🏗️ Архитектура кеша + +Для каждой серии создаются **3 отдельных кешируемых файла**: + +| Файл | Содержимое | Размер | Где появляется | +|------|-----------|--------|---| +| `{seria_id}_id_static_flaps.html` | Схемы открывания и типовые размеры окон | 4-7KB | В разделе "Дома серии: типовые размеры" | +| `{seria_id}_id_static_graph.html` | Google Charts: график ввода в эксплуатацию | 2-3KB | В контейнере height:300px | +| `{seria_id}_id_static_map_stats.html` | Yandex Maps + блок статистики | 6-46KB | Карта (col-md-7) + статистика (col-md-4) | + +### Верхняя статья — ДИНАМИЧЕСКИЕ ДАННЫЕ, не кешируется! + +**Верхняя статья про серию** (`THIS_SERIA_DESCRIPTION`) — это динамические данные, которые: +- Хранятся в БД (поле `sDescription` модели `Seria_Info`) +- **Редактируются через админку** → нужны изменения **без перезагрузки контейнера** +- Рендерятся всегда из БД, не сохраняются в кеш-файлы + +Если бы мы кешировали верхнюю статью: +- Админ редактирует статью → кеш-файл не обновляется автоматически +- Нужно перезагрузить контейнер или вручную удалять файл кеша +- При регенерации кеша может произойти перезапись старыми данными + +**Решение**: верхняя статья **всегда рендерится из БД**, как и таблица окон. + +--- + +## 🔄 Логика работы + +### При первом запросе seriesId (cache miss): + +1. View `catalog_seria_info()` обнаруживает отсутствие кеш-файлов +2. Вычисляются дорогостоящие данные: + - Геокоординаты всех зданий серии + - Год ввода в эксплуатацию (для графика) + - Схемы открывания окон +3. Генерируются **3 отдельных файла** в `oknardia/templates/seria_info/prepared/` +4. Верхняя статья и таблица окон **рендерятся из БД** при каждом запросе +5. Контекст получает пути к 3 кеш-файлам + свежие динамические данные +6. Main-шаблон включает 3 кеш-файла + 2 динамических блока +7. Ответ отправляется пользователю + +### При последующих запросах (cache hit): + +1. View обнаруживает, что все 3 файла существуют +2. **Верхняя статья пересчитывается заново** из БД (от админа) +3. **Таблица окон пересчитывается заново** (новые предложения видны сразу) +4. Main-шаблон включает 3 статических файла + 2 динамических блока +5. Ответ отправляется быстро (схемы, график, геоданные из кеша) + +### Ключевой мюмент: Всё свежее! + +- Кеширование **не трогает** верхнюю статью и таблицу окон → они всегда свежие +- Новые цены от поставщиков видны пользователям **сразу** +- Админ может редактировать текст про серию → видно **без перезагрузки** контейнера +- Таблица пересчитывается в запросе через Q-фильтры к БД (есть индексы на БД) + +--- + +## 📁 Структура файлов + +### Templates (шаблоны) + +``` +oknardia/templates/seria_info/ +├── all_seria_info_pre_light.html # Main-шаблон (включает 3 static-файла + динамику) +├── all_seria_info_pre_light_static_flaps.html # ШАГ 1: Схемы открывания (кешируется) +├── all_seria_info_pre_light_static_graph.html # ШАГ 2: График ввода (кешируется) +├── all_seria_info_pre_light_static_map_stats.html # ШАГ 3: Карта + статистика (кешируется) +└── prepared/ # Директория с кеш-файлами + ├── 210_id_static_flaps.html + ├── 210_id_static_graph.html + ├── 210_id_static_map_stats.html + ├── 100_id_static_flaps.html + └── ... (93 файла: 31 серия × 3 типа) +``` + +### Генерация кеша (Web-логика) + +``` +oknardia/web/ +├── catalog_series.py # catalog_seria_info() — генерирует 3 файла + динамику +└── management/commands/ + └── regenerate_seria_prerender.py # Batch-команда для регенерации всех серий +``` + +--- + +## 🚀 Управление кешем + +### Разработка (DEV-режим) + +```bash +python manage.py runserver +``` + +В DEV-режиме (`DEBUG = True`): +- Кеш **не используется** (всегда `PRE_RENDERED_STATIC_*_PATH = ""`) +- Main-шаблон рендерит данные напрямую +- Удобно для разработки (вижу изменения сразу) + +### Production (PROD-режим) + +#### Первоначальная генерация (все 31 серия): + +```bash +python manage.py regenerate_seria_prerender +``` + +Вывод: +``` +OK seria 100: 3 кеш-файла созданы +OK seria 12: 3 кеш-файла созданы +... +Готово. Обработано: 31. Создано/пересоздано: 31 × 3 файла. Пропущено: 0. +``` + +#### Регенерация конкретной серии: + +```bash +python manage.py regenerate_seria_prerender --seria-id 210 +``` + +#### Dry-run (без создания файлов): + +```bash +python manage.py regenerate_seria_prerender --dry-run +``` + +#### Force-переписать даже если есть кеш: + +```bash +python manage.py regenerate_seria_prerender --force +``` + +--- + +## 📊 Когда нужна регенерация кеша? + +### ✅ Регенерируйте, если: + +- Изменены координаты зданий (geo-данные) +- Добавлены новые здания в серию +- Обновлены годы ввода в эксплуатацию (для графика) +- Изменены схемы открывания окон +- Обновлена верхняя статья про серию + +```bash +# Все изменилось → перестроить все +python manage.py regenerate_seria_prerender --force + +# Изменилась одна серия → перестроить одну +python manage.py regenerate_seria_prerender --seria-id 210 +``` + +### ❌ НЕ нужна регенерация, если: + +- Добавлены новые **предложения** (цены от поставщиков) +- Обновлены **наличие/status** существующих предложений +- Система просто должна **показать свежие цены** + +Таблица окон пересчитывается при каждом запросе автоматически! 🎉 + +--- + +## 🔌 Интеграция с Docker + +В контейнере (PROD-режим): + +```dockerfile +# Генерируем кеш пр�� запуске +RUN python manage.py regenerate_seria_prerender + +# Запускаем сервер +CMD ["gunicorn", "oknardia.wsgi:application", "--bind", "0.0.0.0:8000"] +``` + +Кеш-файлы сохраняются на диск в томе, поэтому переживают перезагрузку контейнера. + +--- + +## 📝 Контекстные переменные + +### При первом запросе (происходит генерация): + +View `catalog_seria_info()` отправляет в шаблон: + +```python +to_template = { + "THIS_SERIA_ID": seria_id, # ID серии (210) + "THIS_SERIA_NAME": q_seria.sName, # Название ("1-335") + "THIS_SERIA_DESCRIPTION": html_description, # Верхняя статья + "FLAP_DIM": flap_dimensions, # Массив схем открывания + "DATA4GRAPH": graph_data, # Годы и кол-во домов + "DATA4GEO": geo_data, # Координаты зданий + "ACCOUNTS": buildings_count, # Кол-во квартир + "APARTMENTS": families_count, # Кол-во семей + "RESIDENTIAL_M2": total_area, # Площадь жилая + # ... и другие +} +``` + +### Для шаблонов генерации кеша: + +Каждый template файл получает **все** эти переменные и рендерится отдельно. + +### Для main-шаблона: + +Main-шаблон получает пути только к 3 кеш-файлам: + +```python +to_template.update({ + "PRE_RENDERED_STATIC_FLAPS_PATH": "seria_info/prepared/210_id_static_flaps.html", + "PRE_RENDERED_STATIC_GRAPH_PATH": "seria_info/prepared/210_id_static_graph.html", + "PRE_RENDERED_STATIC_MAP_STATS_PATH": "seria_info/prepared/210_id_static_map_stats.html", +}) + +# Верхняя статья всегда передается как THIS_SERIA_DESCRIPTION и рендерится динамически +to_template.update({ + "THIS_SERIA_DESCRIPTION": html_description, # Из БД, не кешируется +}) +``` + +Main-шаблон использует `{% include %}` для 3 кеш-файлов, а верхнюю статью рендерит напрямую: `{{ THIS_SERIA_DESCRIPTION|safe }}`. + +--- + +## 🎯 Примеры использования + +### Добавили новое здание в серию 210 + +```bash +# Обновили БД через Django ORM/admin +python manage.py shell +>>> from oknardia.models import Building_Info +>>> Building_Info.objects.create(...) + +# Регенерируем кеш только этой серии +python manage.py regenerate_seria_prerender --seria-id 210 + +# Пользователи видят новое здание на карте и в таблице +``` + +### Обновили года ввода в эксплуатацию + +```bash +# Изменили данные +python manage.py shell +>>> from oknardia.models import Building_Info +>>> Building_Info.objects.filter(...).update(...) + +# Кеш станет невалидным — нужна регенерация +python manage.py regenerate_seria_prerender --force + +# Пользователи видят обновленный график +``` + +### Добавили новое предложение (цену) + +```bash +# PriceOffer.objects.create(...) → система добавляет новое предложение +# НЕ нужна регенерация! + +# Таблица окон обновилась сама на следующем запросе +# Пользователи видят новое предложение сразу +``` + +--- + +## 🐛 Отладка + +### Проверить, какие кеш-файлы существуют: + +```bash +ls -lah oknardia/templates/seria_info/prepared/ +``` + +### Вручную удалить кеш (для тестирования): + +```bash +# Удалить кеш одной серии +rm oknardia/templates/seria_info/prepared/210*.html + +# Удалить ВСЕ кеш-файлы +rm oknardia/templates/seria_info/prepared/*_id_static_*.html +``` + +### Проверить содержимое кеш-файла: + +```bash +cat oknardia/templates/seria_info/prepared/210_id_static_graph.html | head -20 +cat oknardia/templates/seria_info/prepared/210_id_static_flaps.html | head -30 +cat oknardia/templates/seria_info/prepared/210_id_static_map_stats.html | head -50 +``` + +### Логирование: + +В `catalog_series.py` логируем создание файлов: + +```python +logger.info(f"Cache created: {file_flaps}") +logger.info(f"Cache created: {file_graph}") +logger.info(f"Cache created: {file_map_stats}") +# file_upper НЕ создается — верхняя статья рендерится динамически +``` + +--- + +## 📈 Производительность + +### Без кеша (DEV-режим): + +- ~2-3 сек на запрос (вычисляются geo, graph, flaps) +- Каждый запрос трогает БД +- Удобно для разработки + +### С кешем (PROD-режим): + +- ~100-300 мс на первый запрос (генерируется кеш) +- ~50-100 мс на последующие (включаются файлы + динамическая таблица) +- Статические данные из кеша (очень быстро) +- Таблица окон кешируется на уровне DB-запроса (индексы работают) + +--- + +## ✅ Чек-лист для администратора + +При развертывании в production: + +- [ ] Запустить `python manage.py regenerate_seria_prerender` (сгенерировать все 93 файла) +- [ ] Проверить размеры файлов в `prepared/` (~100-200 KB всего) +- [ ] Протестировать на локалхосте `DEBUG = False` +- [ ] Проверить, что таблица окон обновляется при добавлении новых предложений +- [ ] Настроить логирование создания кеша в продакшене + +--- + +## 🔗 Близкие компоненты + +- **Main-шаблон**: `oknardia/templates/seria_info/all_seria_info_pre_light.html` +- **Динамическая таблица**: `oknardia/templates/seria_info/all_seria_info_pre_light_dynamic_include.html` +- **View**: `oknardia/web/catalog_series.py::catalog_seria_info()` +- **Management-команда**: `oknardia/web/management/commands/regenerate_seria_prerender.py` +- **Тесты**: `oknardia/web/test_prices.py` (проверяет свежесть таблицы) + +--- + +**Версия:** 2.0 (Двухуровневое кеширование) +**Последнее обновление:** 2026-05-19 +**Статус:** ✅ Production-ready + diff --git a/MANAGEMENT_RUNBOOK.md b/MANAGEMENT_RUNBOOK.md index e5f70c2..78d1b6e 100644 --- a/MANAGEMENT_RUNBOOK.md +++ b/MANAGEMENT_RUNBOOK.md @@ -394,40 +394,72 @@ location = /sitemap.xml { ## 4) Команда `regenerate_seria_prerender` Назначение: -- пересобрать pre-render шаблоны для страниц серий (`catalog_seria_info`) в каталоге `seria_info/prepared/`. +- пересобрать pre-render шаблоны для **статических данных** страниц серий (`catalog_seria_info`) в каталоге `seria_info/prepared/`. +- генерирует **3 типа файлов** для каждой серии (верхняя статья НЕ кешируется, она рендерится динамически из БД): + - `{seria_id}_id_static_flaps.html` — схемы открывания + - `{seria_id}_id_static_graph.html` — график ввода в эксплуатацию + - `{seria_id}_id_static_map_stats.html` — карта + статистика -Проверка без записи файлов: +### Контекст: двухуровневое кеширование + +Система использует двухуровневое кеширование: +- **Статический кеш** — дорогостоящие операции (геокоординаты, графики, схемы). Генерируются один раз и сохраняются на диск. +- **Динамические данные** — верхняя статья про серию (может редактироваться через админку, видно БЕЗ перезагрузки контейнера) и таблица оконных проёмов (показывает актуальные предложения). + +Подробнее: см. [`CACHE_PRERENDER_SYSTEM.md`](CACHE_PRERENDER_SYSTEM.md) + +### Проверка без записи файлов (dry-run): ```bash cd /Users/e-serg/PRJ/2022-oknardia poetry run python oknardia/manage.py regenerate_seria_prerender --dry-run ``` -Пересборка только отсутствующих файлов: +Пересборка только отсутствующих файлов (стандартный запуск): ```bash cd /Users/e-serg/PRJ/2022-oknardia poetry run python oknardia/manage.py regenerate_seria_prerender ``` -Принудительная пересборка всех root-серий: +Вывод: +``` +OK seria 210: 3 кеш-файла созданы +OK seria 100: 3 кеш-файла созданы +... +Готово. Обработано: 31. Создано/пересоздано: 31 × 3 файла. Пропущено: 0. +``` + +Принудительная пересборка всех root-серий (даже если кеш существует): ```bash cd /Users/e-serg/PRJ/2022-oknardia poetry run python oknardia/manage.py regenerate_seria_prerender --force ``` -Выборочная пересборка: +Выборочная пересборка (конкретные серии): ```bash cd /Users/e-serg/PRJ/2022-oknardia poetry run python oknardia/manage.py regenerate_seria_prerender --seria-id 843 --seria-id 2100 --force ``` -Когда запускать: -- после обновления логики `catalog_seria_info`; -- после массового обновления данных серий/окон/квартир; -- после очистки `seria_info/prepared/`. +### Когда запускать + +- **После первого развертывания** — сгенерировать кеш всех 31 серии один раз. +- **После обновления логики `catalog_seria_info`** — изменились параметры графика или карты. +- **После изменения координат зданий** — geo-данные обновлены. +- **После добавления новых зданий в серию** — карта и список зда��ий изменились. +- **По расписанию** (опционально, если данные по геокоординатам обновляются): + ```bash + 0 3 * * 0 cd /Users/e-serg/PRJ/2022-oknardia && poetry run python oknardia/manage.py regenerate_seria_prerender >> /var/log/oknardia-prerender.log 2>&1 + ``` + +### Когда НЕ нужна регенерация + +- **При добавлении новых предложений/цен** — таблица окон обновляется при каждом запросе автоматически. +- **При редактировании верхней статьи через админку** — она рендерится динамически, кешируется НЕ нужно. +- **При изменении наличия/статуса профилей** — рейтинги пересчитываются запросом через `make_rating`. ## 5) Команда `populate_seo_fields` @@ -532,14 +564,14 @@ print(f'Пусто sMetaKeywords: {posts.filter(sMetaKeywords=\"\").count()}') ✓ Записей обновлено в БД: 28 ✗ Ошибок при обработке: 0 -✅ Обновлено 28 записей успешно! +Обновлено 28 записей успешно! ``` ### Откат и безопасность -- ✅ **Безопасна для повторного запуска** — пустые поля не изменяются при повторной работе. -- ✅ **Откат через SQL** — если нужно очистить, используй: `UPDATE oknardia_blogposts SET sSlug='', sMetaDescription='', sMetaKeywords='';` -- ✅ **Всегда используй `--dry-run`** перед первым запуском для проверки. +- **Безопасна для повторного запуска** — пустые поля не изменяются при повторной работе. +- **Откат через SQL** — если нужно очистить, используй: `UPDATE oknardia_blogposts SET sSlug='', sMetaDescription='', sMetaKeywords='';` +- **Всегда используй `--dry-run`** перед первым запуском для проверки. ## 6) Команда `make_rating` diff --git a/README.md b/README.md index f078496..6a5ebba 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ # См. также: -* [`MANAGEMENT_RUNBOOK.md`](MANAGEMENT_RUNBOOK.md) – единый runbook по management-командам и batch-операциям, сниппеты. +* [`MANAGEMENT_RUNBOOK.md`](MANAGEMENT_RUNBOOK.md) – единый runbook по management-командам и кастом-операциям (регенерация кеша, рейтингов, sitemap и т.д.). +* [`CACHE_PRERENDER_SYSTEM.md`](CACHE_PRERENDER_SYSTEM.md) – двухуровневая система кеширования страниц серий, структура статик-шаблонов, управление кешем. * [`AGENTS.md`](AGENTS.md) – контекст проекта для AI-ассистентов (архитектура, конвенции, рабочие сценарии). * [`SETUP.md`](SETUP.md) – пошаговая настройка окружения, запуск проекта и базовые команды разработки. @@ -65,14 +66,25 @@ В папке `oknardia/templates/seria_info/prepared/` создаются пре-рендер HTML-шаблоны с информацией о сериях домов. -Эти шаблоны создаются при первом обращении к странице серии и хранятся для ускорения последующих запросов. -**Важно**: их надо периодически удалять, особенно если меняются: -* данные по сериям и размерам окон -* коммерческие предложения и цены -* рейтинги компонентов +**Архитектура (май 2026)**: Для каждой серии создаются **3 отдельных кешируемых файла** (верхняя статья НЕ кешируется): +* `{seria_id}_id_static_flaps.html` — схемы открывания окон +* `{seria_id}_id_static_graph.html` — график ввода в эксплуатацию +* `{seria_id}_id_static_map_stats.html` — карта Яндекса и статистика -**Рекомендация**: настроить cronjob на ежедневное или еженедельное удаление этих файлов. При обращении к соответствующим -страницам эти шаблоны будут пересозданы автоматически. На быстрых серверах можно вообще отключить кеширование, если оно -не критично для производительности. +**Верхняя статья рендерится динамически** из БД, поэтому изменения через админку видны без перезагрузки контейнера. + +Таблица оконных проёмов **не кешируется** — пересчитывается при каждом запросе, поэтому новые предложения видны пользователям сразу. + +**Регенерация кеша**: +```bash +python manage.py regenerate_seria_prerender # все сер��и +python manage.py regenerate_seria_prerender --seria-id 210 # конкретная серия +``` + +⏱️ **Когда регенерировать**: Изменены координаты зданий, добавлены новые здания, обновлены годы ввода в эксплуатацию. + +❌ **Когда НЕ нужна регенерация**: Добавлены новые предложения/цены (таблица обновляется автоматически), изменены статьи через админку (рендерятся динамически). + +**Подробности**: см. [`CACHE_PRERENDER_SYSTEM.md`](CACHE_PRERENDER_SYSTEM.md) diff --git a/oknardia/templates/seria_info/all_seria_info_pre_light.html b/oknardia/templates/seria_info/all_seria_info_pre_light.html index 657d931..acb7432 100755 --- a/oknardia/templates/seria_info/all_seria_info_pre_light.html +++ b/oknardia/templates/seria_info/all_seria_info_pre_light.html @@ -132,40 +132,47 @@ TechArticle: описывает страницу как технический {# #}
Чтобы посмотреть цены на установку и замену окон от партнёров «Окнардия» в своей квартире: найдите дом на карте; кликните на него; перейдите по ссылке «Смотреть коммерческие предложения». При необходимости смените типовую планировку квартиры (на странице ценовой выдачи, справа от изображения типовых проёмов и схем открывания).
-Совокупно во всех зданиях типового проекта:
-Совокупно во всех зданиях типового проекта:
+Совокупно во всех зданиях типового проекта:
+