Files
2022_oknardia/MANAGEMENT_RUNBOOK.md

52 KiB
Raw Permalink Blame History

MANAGEMENT_RUNBOOK.md

Единый runbook по management-командам проекта.

Документ отвечает на 3 вопроса:

  • что запускать;
  • когда запускать;
  • как безопасно откатываться/повторять запуск.

Каталог команд

  1. regenerate_seria_roots — пересчет корневых серий (иерархия и консолидация).
  2. generate_map_js — генерация JavaScript для карт с геоданными зданий.
  3. generate_sitemaps — оффлайн генерация sitemap-файлов.
  4. regenerate_seria_prerender — оффлайн пересборка pre-render шаблонов для catalog_seria_info.
  5. populate_seo_fields — автозаполнение SEO-полей блога из существующих данных.
  6. make_rating — пересчёт рейтингов профилей и стеклопакетов методом Манна-Уитни.

Общие правила запуска

  • Запускать команды из корня репозитория.
  • Для локального/CI запуска использовать poetry.
  • Не запускать тяжелые операции через HTTP-эндпоинты /service/*.
  • Перезапуск веб-сервера (gunicorn/uWSGI) делать отдельным шагом оркестрации, а не из кода Django.

Базовый шаблон запуска:

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py <command> [args]

1) Команда regenerate_seria_roots

Назначение:

  • пересчитать корневые серии (root) для всей иерархии серий домов.
  • консолидировать различные написания одной и той же серии в одну "корневую" серию.

Контекст

На разных источниках данные о типовых сериях домов были записаны с разными орфографическими вариантами. Например серия II-57 могла быть обозначена как:

  • 2-57 (цифра вместо кириллицы)
  • И-57 (кириллица И вместо латинской II)
  • П-57 (опечатка)
  • и т.п.

При парсинге данных эти варианты могли оказаться в БД как отдельные серии, хотя на самом деле это одна и та же серия. Функция regenerate_seria_roots связывает все эти варианты (алиасы) с одной корневой серией.

Когда это нужно

При добавлении новых адресов и серий типового строительства, при ручном редактировании иерархии серий в админке, при загрузке новых данных с разными орфографическими вариантами серий. Кроме того, если для уже существующих в базе серий будут получены данные о типовых размерах оконных проёмов и типов квартирах (сейчас в базе около 4500 серий, из
них всего 31 корневых серий и 1957 серий с найденным корнем... ещё 2502 неописанных серии без корня, т.е. больше половины, могут пополнить каталог Окнардии.

Как это работает

  1. Этап 1: Находит "корневые" серии (root series) — те, что реально используются в таблице Apartment_Type (т.е. у которых есть квартиры). Для каждой такой серии устанавливает kRoot_id = own_id.

  2. Этап 2: Для всех остальных серий:

    • Движется вверх по дереву иерархии (kParent_id)
    • Ищет корневую серию (ту, которая либо не имеет родителя, либо она в списке корневых)
    • Устанавливает найденную корневую серию в поле kRoot_id
    • Если не находит корневую серию → kRoot_id = None

Пример структуры после обработки

Таблица: Seria_Info

id  | sSeriaName | kParent_id | kRoot_id  | Комментарий
----|------------|------------|-----------|-------------------
123 | II-57      | NULL       | 123       | Корневая серия
124 | 2-57       | 123        | 123       | Алиас (через родителя)
125 | И-57       | 123        | 123       | Алиас (через родителя)
126 | П-57       | 123        | 123       | Алиас (через родителя)

Базовый запуск

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_roots

Параметры запуска

--verbosity 0 — только ошибки (минимум информации):

poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 0

--verbosity 1 — точки/плюсы (стандартный режим):

poetry run python oknardia/manage.py regenerate_seria_roots
# или явно
poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 1

--verbosity 2 — подробный вывод (названия серий):

poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 2

--verbosity 3 — очень подробный вывод в виде таблицы:

poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 3

Примеры вывода

Verbosity 0 (только ошибки - чистый вывод):

✅ Пересчет завершен! Время: 2.18с

Verbosity 1 (магический режим - точки и символы):

=== ПЕРЕСЧЕТ КОРНЕВЫХ СЕРИЙ ===
Этап 1: Ищем корневые серии в таблице квартир...
✓ Найдено корневых серий: 241
...............................

Этап 2: Главная магия - обрабатываем все серии в иерархии...
-----++..--.++--...--.+.++++.-+++++++-.-.++.-+--++--++++-+++++++-++.+--.+++-+-..++++++-++++++++++--.-++++++-++++
+--+-+++++++++-++++++++++-++++--+++.+++--+++++++++++++++++++---++.+-+-+++++-++++++++++-+++-+----+.-+++-+--++++++
+++----+--+-+++++-+--+++--+++-.+++++-++++++++-+---++-+++++---+++------++----++-+--++----+--++--++++--+++++++++++
+++++++-------++++---+++-[... очень много символов ...]

=== РЕЗУЛЬТАТЫ ===
✓ Корневых серий (обработаны на этапе 1): 31
✓ Серий с найденным корнем: 1957
⚠ Серий без корня: 2502

✅ Пересчет завершен! Время: 2.18с

Легенда магического режима:

  • . = корневая серия (обработана на этапе 1)
  • + = серия с найденным корнем
  • - = серия без найденного корня
  • E = ошибка при обработке

Verbosity 2 (подробный):

=== ПЕРЕСЧЕТ КОРНЕВЫХ СЕРИЙ ===
Этап 1: Ищем корневые серии в таблице квартир...
✓ Найдено корневых серий: 241
  ✓ 0008 П-44
  ✓ 0009 П-3
  ✓ 0012 II-49
  ✓ 0017 КОПЭ
  ...

Этап 2: Главная магия - обрабатываем все серии в иерархии...
  0001: Нет корня
  0002: Нет корня
  0006: корень → 12
  0007: корень → 9
  0008: корневая
  ...

Verbosity 3 (очень подробный - таблица):

=== ПЕРЕСЧЕТ КОРНЕВЫХ СЕРИЙ ===
Этап 1: Ищем корневые серии в таблице квартир...
✓ Найдено корневых серий: 241
  ✓ 0008 | П-44  | корневая
  ✓ 0009 | П-3   | корневая
  ✓ 0012 | II-49 | корневая
  ...

Этап 2: Главная магия - обрабатываем все серии в иерархии...
--------------------------------------------------------------------------------------------------------------
   ID | Название                            |   Родитель | Путь                                     | Результат           
--------------------------------------------------------------------------------------------------------------
 ...
 ...
 2565 | Г-ЗИ                                |          9 | 9                                        | ✓ Корень #9
 2566 | I-528КП-809/69                      |          - | (нет)                                    | ✗ Нет корня
 2567 | 464Д-0154                           |       3339 | 3339 → 963 → 375                         | ✓ Корень #375
 2568 | УЛГ-507-4/64                        |       2105 | 2105                                     | ✓ Корень #2105
 2569 | ЛГ-507-4                            |       2105 | 2105                                     | ✓ Корень #2105
 2570 | 1ЛГ-600-И-1                         |          - | (нет)                                    | ✗ Нет корня
 2571 | 464Д-0154 Новополоцкого ДСК         |       3339 | 3339 → 963 → 375                         | ✓ Корень #375
 2572 | 121-0142,13,87                      |          - | (нет)                                    | ✗ Нет корня
 ...
 ...
--------------------------------------------------------------------------------------------------------------

=== РЕЗУЛЬТАТЫ ===
✓ Корневых серий (обработаны на этапе 1): 31
✓ Серий с найденным корнем: 1957
⚠ Серий без корня: 2502

✅ Пересчет завершен! Время: 2.18с

Когда запускать

  • После первого развертывания — консолидировать иерархию серий.
  • После ручного редактирования иерархии (добавления родитель-потомков в админку).
  • После загрузки новых данных с разными орфографическими вариантами серий.
  • По расписанию (опционально, например раз в месяц):
    0 2 * * 1 cd /home/user/app-path/2022-oknardia && poetry run python oknardia/manage.py regenerate_seria_roots >> /var/log/oknardia-seria-roots.log 2>&1
    

Откат и безопасность

  • Безопасна для повторного запуска — просто пересчитывает все kRoot_id.
  • Откат через SQL — если нужно очистить поле (перед запуском рекомендуется бэкап):
    UPDATE oknardia_seria_info SET kRoot_id = NULL;
    
  • Проверка результатов — после запуска можно проверить:
    poetry run python oknardia/manage.py shell -c "
    from oknardia.models import Seria_Info
    count_null = Seria_Info.objects.filter(kRoot_id__isnull=True).count()
    count_with_root = Seria_Info.objects.filter(kRoot_id__isnull=False).count()
    print(f'Серий без корня: {count_null}')
    print(f'Серий с корнем: {count_with_root}')
    "
    

2) Команда generate_map_js

Назначение:

  • сгенерировать JavaScript-файл для отрисовки карты всех зданий типовых серий в Яндекс.Картах.
  • файл содержит геоданные (latitude/longitude), ID адресов, привязку к сериям и информацию для balloon-окон на картах.

Что происходит

  1. Сбор геоданных — для всех корневых серий (где id = kRoot_id)

    • Запрашиваются здания из таблицы Building_Info с non-zero координатами
    • Для каждого здания собирается: широта, долгота, ID адреса, адрес в латинице, ID серии
  2. Генерация JavaScript — на основе шаблона service/JavaScript4AllSeriaMap.js.html

    • Генерируется массив цветов для каждой серии
    • Объявляются переменные с ID и названиями серий
    • Инициализируется Yandex.Maps с PlaceMarks для каждого здания
  3. Минификация через Terser — уменьшение размера JavaScript

    • Удаляются ненужные пробелы и переносы строк
    • Сокращаются имена переменных (mangling)
    • Удаляются console.log и debugger
  4. Запись в файлы:

    • public/static/js/4maps/_ALL_seria_on_map.js — исходный форматированный файл (715 KB)
    • public/static/js/4maps/_ALL_seria_on_map.mini.js — минифицированный файл (639 KB)

Оптимизация размера

Файл был оптимизирован в три этапа:

Этап Размер Сжатие
Исходный (2016 год) 2.5 MB
Уровень 1: функция-фабрика m() 715 KB 71%
Уровень 2: Terser минификация 639 KB +10.6%
Уровень 3: Gzip в браузере 188 KB +29.4%
Итого сжатие 188 KB 92.5%

Примечание: Gzip применяется автоматически браузером и веб-сервером при наличии в заголовках Content-Encoding: gzip

Содержимое:

  • Маркеры на карте: 18,228 зданий
  • Серии с цветами: 31
  • Корневые серии: 31

Базовый запуск

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py generate_map_js

Параметры запуска

--force — пересгенерировать файл (перезаписать если существует):

poetry run python oknardia/manage.py generate_map_js --force

--verbosity 2 — подробный вывод со статистикой:

poetry run python oknardia/manage.py generate_map_js --verbosity 2

Когда запускать

  • После первого развертывания — создать файл карты один раз.
  • После добавления новых зданий в БД (через парсеры или импорт).
  • По расписанию (опционально, если здания редко добавляются):
    0 3 * * 0 cd /home/user/app-path/2022-oknardia && poetry run python oknardia/manage.py generate_map_js >> /var/log/oknardia-map-js.log 2>&1
    

Пример вывода

=== ГЕНЕРАЦИЯ JAVASCRIPT ДЛЯ КАРТ ===
                                                                                                                                                                                                                                                                                          
Этап 1: Сбор информации о корневых сериях...
✓ Найдено корневых серий: 31

Этап 2: Генерация единого JS-файла для ВСЕ серий...
✓ Написан исходный файл: _ALL_seria_on_map.js
  Размер: 734.0 KB

Этап 3: Минификация JavaScript (rjsmin)...
[*] Минификация успешна!
    Исходный файл: 734.015 KB
    Минифицированный: 732.952 KB
    Сжатие: 0.14%
    Время: 0.0017с
[i] Полная статистика по сериям:
    - Жилых м²: 125,749,341
    - Муниципальных м²: 11,302,860
    - Жильцов: 6,342,742
    - Квартир: 2,769,800

=== РЕЗУЛЬТАТЫ ===                                                                                                                                                                                                                                                                        
✓ Серий обработано: 31
✓ Зданий на карте: 18228
✓ JS-файлов создано: 2 (исходный + минифицированный)
✓ Исходный файл: _ALL_seria_on_map.js
✓ Минифицированный: _ALL_seria_on_map.mini.js
✓ Обфускация: Base64 кодирование координат

[OK] Генерация завершена! Время: 1.10с

3) Команда generate_sitemaps

Назначение:

  • пересобрать sitemap.xml и chunk-файлы в MEDIA_ROOT/_serv_sitemap.

Базовый запуск:

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py generate_sitemaps

Запуск с параметрами:

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py generate_sitemaps \
  --compare-min-depth 2 \
  --compare-max-depth 4 \
  --max-items 40000 \
  --max-file-size 5242880 \
  --max-files-qty 998

Когда запускать:

  • после деплоя;
  • по расписанию (cron/systemd timer);
  • после крупных изменений данных каталога/блога.

Важные замечания

Чтобы sitemap.xml отдавал прокси-nginx напрямую из файловой системы, нужно, чтобы он физически лежал в MEDIA_ROOT/_serv_sitemap/sitemap.xml.

Допустимо, что файл доступен по двум URL (корневой и media), но в robots.txt должен быть указан один канонический вариант sitemap.xml

NGINX snippet (alias для корневого sitemap)

# Корневой sitemap.xml (для привычного для поисковиков URL)
location = /sitemap.xml {
    alias /<путь-к-каталогку-с-докер-приложением>/media/_serv_sitemap/sitemap.xml;
    default_type application/xml;
    add_header Cache-Control "public, max-age=300";
}

4) Команда regenerate_seria_prerender

Назначение:

  • пересобрать 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

Проверка без записи файлов (dry-run):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_prerender --dry-run

Пересборка только отсутствующих файлов (стандартный запуск):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_prerender

Вывод:

OK    seria 210: 3 кеш-файла созданы
OK    seria 100: 3 кеш-файла созданы
...
Готово. Обработано: 31. Создано/пересоздано: 31 × 3 файла. Пропущено: 0.

Принудительная пересборка всех root-серий (даже если кеш существует):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_prerender --force

Выборочная пересборка (конкретные серии):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_prerender --seria-id 843 --seria-id 2100 --force

Когда запускать

  • После первого развертывания — сгенерировать кеш всех 31 серии один раз.
  • После обновления логики catalog_seria_info — изменились параметры графика или карты.
  • После изменения координат зданий — geo-данные обновлены.
  • После добавления новых зданий в серию — карта и список зда<D0B4><D0B0>ий изменились.
  • По расписанию (опционально, если данные по геокоординатам обновляются):
    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

Назначение:

  • автозаполнить SEO-поля (sSlug, sMetaDescription, sMetaKeywords) для всех существующих записей блога.

Используется:

  • при первом развертывании новой версии с автогенерацией SEO-полей;
  • при восстановлении из бэкапа где SEO-поля пусты;
  • при изменении логики автогенерации (с флагом --force).

Базовый запуск

Заполнить только пустые SEO-поля (стандартный вариант):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py populate_seo_fields

Параметры запуска

--dry-run — только показать что будет сделано (без сохранения в БД):

poetry run python oknardia/manage.py populate_seo_fields --dry-run

--force — переполнить ВСЕ SEO-поля, даже уже заполненные:

poetry run python oknardia/manage.py populate_seo_fields --force

--clean — очистить все SEO-поля перед заполнением (для переделки):

poetry run python oknardia/manage.py populate_seo_fields --clean

Комбинация флагов — сухой прогон переполнения всех полей:

poetry run python oknardia/manage.py populate_seo_fields --dry-run --force

Что заполняется

Поле Источник Результат
sSlug sPostHeader URL-безопасный слаг (max 200 символов)
sMetaDescription sPostContent Первые 160 символов (исключая теги <cut>)
sMetaKeywords sPostHeader Заголовок + префикс "oknardia, окнардия, блог, публикация" (max 256 символов)

Пример результата:

sPostHeader = "Профиль Brusbox Super Aero"

sSlug = "profil-brusbox-super-aero"
sMetaDescription = "brusbox-super-aero-pyatikamernaya-profil-sistema..."
sMetaKeywords = "oknardia, окнардия, блог, публикация, Профиль Brusbox Super Aero"

Когда запускать

  • После первого развертывания — заполнить SEO-поля всех 29 существующих постов одной командой.
  • Один раз — команда идемпотентна (при повторном запуске не будет ничего менять, т.к. пустые поля остатся).
  • При изменении логики — использовать --clean --force для полной переделки всех SEO-полей.

Пример полного сценария

cd /Users/e-serg/PRJ/2022-oknardia

# Шаг 1: Проверить что будет заполнено
poetry run python oknardia/manage.py populate_seo_fields --dry-run

# Шаг 2: Если результат устраивает — запустить реально
poetry run python oknardia/manage.py populate_seo_fields

# Шаг 3: Проверить что заполнилось
poetry run python oknardia/manage.py shell -c "
from oknardia.models import BlogPosts
posts = BlogPosts.objects.all()
print(f'Пусто sSlug: {posts.filter(sSlug=\"\").count()}')
print(f'Пусто sMetaDescription: {posts.filter(sMetaDescription=\"\").count()}')
print(f'Пусто sMetaKeywords: {posts.filter(sMetaKeywords=\"\").count()}')
"

Возвращаемая информация

======================================================================
ИТОГОВЫЙ ОТЧЕТ
======================================================================

✓ sSlug заполнено:              28 раз
✓ sMetaDescription заполнено:  28 раз
✓ sMetaKeywords заполнено:     28 раз
✓ Записей обновлено в БД:      28
✗ Ошибок при обработке:        0

Обновлено 28 записей успешно!

Откат и безопасность

  • Безопасна для повторного запуска — пустые поля не изменяются при повторной работе.
  • Откат через SQL — если нужно очистить, используй: UPDATE oknardia_blogposts SET sSlug='', sMetaDescription='', sMetaKeywords='';
  • Всегда используй --dry-run перед первым запуском для проверки.

6) Команда make_rating

Назначение:

  • пересчитать рейтинги оконных профилей, стеклопакетов и наборов услуг используя адаптированный метод Манна-Уитни (Mann-Whitney U Step Rank).
  • сохранить результаты в поля fProfileRating, fGlazingRating, fSetRating (0.0 … 5.0 звёзд).
  • заполнить JSON-состав рейтинга (детальный разбор по каждому параметру) в поля sProfileDescription, sGlazingDescription, sSetDescription.
  • алгоритм рассчитывает три этапа ранжирования: профили → стеклопакеты → наборы (которые зависят от профилей и стеклопакетов).

Базовый запуск

Пересчитать рейтинги всех профилей и стеклопакетов (стандартный режим):

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py make_rating

Параметры запуска

--verbosity 0 — минимум информации (только ошибки): --verbosity 1 — стандартная информация (по умолчанию): --verbosity 3 — очень подробный вывод (для отладки, для каждого профиля/стеклопакета таблица):

Пример использования с параметром --verbosity:

poetry run python oknardia/manage.py make_rating --verbosity 3 | head -500

АЛГОРИТМ: Метод Манна-Уитни (Mann-Whitney U Step Rank)

Команда использует адаптированный вариант критерия Манна-Уитни для ранжирования параметров качества оконных предложений и комопнентов (профилей, стеклопакетов, наборов услуг) на основе их технических характеристик и популярности у поставщиков.

Как это работает:

  1. Сортировка объектов по одному параметру (например, по теплопередаче):

    • Профиль A: 0.60 Ro → ранг = 0.0
    • Профиль B: 0.60 Ro → ранг = 0.0 (то же значение, ранг не меняется)
    • Профиль C: 0.80 Ro → ранг = 1.0 (новое значение, добавляем вес параметра)
    • Профиль D: 0.95 Ro → ранг = 2.0 (ещё новое значение)
  2. Направление ранжирования определяется флагом revers:

    • revers=FalseБОЛЬШЕ = ЛУЧШЕ (например, теплопередача, звукоизоляция)
    • revers=TrueМЕНЬШЕ = ЛУЧШЕ (например, высота в проёме для прочности)
  3. Нормализация рангов к диапазону 0.0 … 1.0:

    • Профиль A: 0.0 / 2.0 = 0.0
    • Профиль B: 0.0 / 2.0 = 0.0
    • Профиль C: 1.0 / 2.0 = 0.5
    • Профиль D: 2.0 / 2.0 = 1.0
  4. Суммирование рангов по всем параметрам:

    • TmpRating = Σ(ранг_параметра × вес_параметра)
  5. Преобразование в звёзды (0.0 … 5.0):

    • ТmpRating нормализуется к 0..1
    • Умножается на 5.0 для получения финального рейтинга

Пример итогового рейтинга профиля:

Профиль "Brusbox Super Aero" 
  Теплопередача: 0.60 Ro (ранг 0.9, вес 1.0)
  Звукоизоляция: 33 дБ (ранг 0.8, вес 1.0)
  Высота в проёме: 112 мм (ранг 0.6, вес 0.3)
  Количество камер: 6 шт (ранг 0.7, вес 0.1)
  
  Итого: (0.9×1.0 + 0.8×1.0 + 0.6×0.3 + 0.7×0.1) / 2.3 ≈ 3.8 звёзд ⭐⭐⭐⭐

ПРОФИЛИ: какие параметры учитываются

Параметр Поле БД ЛУЧШЕ Вес Описание
1 Звукоизоляция fProfileSoundproofing БОЛЬШЕ дБ 1.0 Сопротивление шуму (дБ)
2 Теплопередача fProfileHeatTransf БОЛЬШЕ Ro 1.0 Сопротивление теплопередаче (м²×°C/Вт)
3 Высота в проёме iProfileHeight МЕНЬШЕ мм 0.3 Видимая высота в световом проёме (экономия)
4 Высота фальца iProfileRabbet БОЛЬШЕ мм 0.2 Глубина фальца для герметизации
5 Толщина стеклопакета iProfileGlazingThickness БОЛЬШЕ мм 0.2 Максимальная толщина стеклопакета
6 Толщина профиля iProfileThickness БОЛЬШЕ мм 0.2 Монтажная (боковая) ширина профиля
7 Контуры уплотнения fProfileSeals БОЛЬШЕ контуров 1.2 Количество контуров уплотнения
8 Количество камер iProfileCameras БОЛЬШЕ шт 0.1 Число камер в профиле (из рамки + створки)
9 Популярность NumOffer БОЛЬШЕ предложений 0.1 Используется ли профиль в коммерческих предложениях

Примеры интерпретации:

  • Профиль с рейтингом 5.0 : отличная теплопередача + звукоизоляция + много камер + многоконтурные уплотнения.
  • Профиль с рейтингом 2.0 : среднее качество, слабые характеристики.
  • Профиль с рейтингом 0.5 : слабые характеристики или производить не предоставил данных и их нет в отрытых источниках.

СТЕКЛОПАКЕТЫ: какие параметры учитываются

Параметр Поле БД ЛУЧШЕ Вес Описание
1 Звукоизоляция fGlazingSoundproofing БОЛЬШЕ дБ 1.0 Звукоизоляционный коэффициент (дБ)
2 Теплопередача fGlazingHeatTransfer БОЛЬШЕ Ro 1.0 Сопротивление теплопередаче (м²×°C/Вт)
3 Светопропускание fGlazingLightTransmission БОЛЬШЕ % 0.25 Коэффициент пропускания видимого света (%), отражение света снаружи
4 Солнцепропускание fGlazingPassingSun МЕНЬШЕ % 0.15 Коэффициент солнечного излучения (SHGC) — В России меньше = лучше для охлаждения летом
5 Толщина iGlazingThickness БОЛЬШЕ мм 0.1 Общая толщина стеклопакета
6 Количество камер iGlazingCamerasN БОЛЬШЕ шт 0.1 Число воздушных/аргоновых камер

Особенности стеклопакетов:

  • Светопропускание = как много естественного света проходит в помещение (больше = лучше)
  • Солнцепропускание = как много солнечного тепла/излучения проходит (в России: меньше = лучше, потому что внутри есть отражающее напыление)
  • Двухкамерный (с аргоном) почти всегда лучше однокамерного
  • Трёхкамерные = премиум для холодного климата

Примеры интерпретации:

  • 5.0 : трёхкамерный с хорошей теплопередачей, звукоизоляцией (обычно с аргоном и напылением).
  • 3.0 : двухкамерный, среднее качество
  • 1.0 : однокамерный старого образца или с плохими характеристиками

НАБОРЫ: какие параметры учитываются

Параметр Поле БД ЛУЧШЕ Вес Описание
1 Актуальность dModify МЕНЬШЕ (свежее) 0.3 Дата последнего обновления (timestamp)
2 Доставка bSetDelivery ДА (1) 0.8 Включена ли доставка в стоимость
3 Монтаж/демонтаж bSetUninstallInstall ДА (1) 1.0 Включены ли услуги монтажа и демонтажа
4 Подоконник sSetSill ДА (1) 0.5 Включен ли подоконник
5 Водоотлив sSetPanes ДА (1) 0.8 Включен ли водоотлив/козырёк
6 Откос sSetSlope ДА (1) 0.5 Включены ли откосы
7 Климат-контроль sSetClimateControl ДА (1) 0.3 Включено ли управление микроклиматом
8 Число предложений NumOffer БОЛЬШЕ 0.2 Популярность набора (кол-во активных предложений)
9 Гибкость скидок iDiscountVariantsCount БОЛЬШЕ вариантов 0.5 Кол-во вариантов скидок из формулы офиса
10 Размер скидок fDiscountMax БОЛЬШЕ % 1.0 Максимальная скидка из всех вариантов

ВАЖНО: Итоговый рейтинг набора состоит из трёх компонентов:

  • Рейтинг параметров услуг (Актуальность, Доставка, Монтаж, Подоконник и т.д.)
  • Рейтинг входящего стеклопакета (ранжируется отдельно)
  • Рейтинг входящего профиля (ранжируется отдельно)

Формула итогового рейтинга набора (fSetRating):

k1 = нормализованный TmpRating (услуги) * вес услуг
k2 = нормализованный рейтинг стеклопакета * RARING_WEIGHT_GLAZING_IN_SET (обычно 1.5)
k3 = нормализованный рейтинг профиля * RARING_WEIGHT_PVC_PROFILE_IN_SET (обычно 1.5)

fSetRating = k1 + k2 + k3  (итого от 0.0 до 5.0 звёзд)

Примеры интерпретации:

  • 5.0 : набор с премиум компонентами (хороший профиль и стеклопакет) + полный пакет услуг (доставка, монтаж, подоконник, откос, климат-контроль) + значительные скидки.
  • 3.5 : хороший профиль/стеклопакет + базовые услуги (доставка, монтаж) + скромные скидки.
  • 2.0 : эконом компоненты или слабые услуги (нет доставки, нет откосов).
  • 1.0 : минимальный пакет или устаревшие предложения (давно не обновлялись).

Когда запускать

  • После первого развертывания — заполнить рейтинги всех профилей, стеклопакетов и наборов.

  • После изменения каталога (добавление нового профиля/стеклопакета/набора).

  • После уточнения характеристик (например, поставщик предоставил новые данные).

    poetry run python oknardia/manage.py make_rating
    
  • По расписанию (например, ежемесячно, чтобы пересчитать популярность):

    30 2 * * 1 cd /home/user/app-path/2022-oknardia && poetry run python oknardia/manage.py make_rating >> /var/log/oknardia-rating.log 2>&1
    
  • После обновления весов в settings.py (константы RANK_PVCP_*, RANK_GLAZ_*).

Откат и безопасность

  • Безопасна для повторного запуска — пересчитывает все рейтинги заново.
  • Всегда обновляет только рейтинги — другие данные в таблицах не меняются.
  • Откат через SQL — если нужно установить нулевые значения (перед запуском рекомендуется бэкап базы):
    -- Очистить рейтинги профилей
    UPDATE oknardia_pvcprofiles SET fProfileRating = 0.0, sProfileDescription = '{}';
    
    -- Очистить рейтинги стеклопакетов
    UPDATE oknardia_glazing SET fGlazingRating = 0.0, sGlazingDescription = '{}';
    
    -- Очистить рейтинги наборов
    UPDATE oknardia_setkit SET fSetRating = 0.0, sSetDescription = '{}';
    

Примеры из реальных данных

Пример вывода --verbosity 1:

=== НАЧАЛИ ПЕРЕСЧЁТ РЕЙТИНГОВ ===

========================================
[ЭТАП 1]: Пересчёт рейтингов ПРОФИЛЕЙ...
========================================
  ✓ Обнулены рейтинги у 94 профилей
  ✓ Найдено 94 профилей для ранжирования
  ✓ Сохранено 94 профилей с финальными рейтингами

=============================================
[ЭТАП 2]: Пересчёт рейтингов СТЕКЛОПАКЕТОВ...
=============================================
  ✓ Обнулены рейтинги у 97 стеклопакетов
  ✓ Найдено 97 стеклопакетов для ранжирования
  ✓ Сохранено 97 стеклопакетов с финальными рейтингами

================================================
[ЭТАП 3]: Пересчёт рейтингов НАБОРОВ (SetKit)...
================================================
  ✓ Обнулены рейтинги у 27 наборов
  ✓ Найдено 27 наборов для ранжирования
  ✓ Сохранено 27 наборов с финальными рейтингами

[OK!] ПЕРЕСЧЁТ РЕЙТИНГОВ ЗАВЕРШЁН УСПЕШНО!
   • Обновлено профилей: 94
   • Обновлено стеклопакетов: 97
   • Обновлено наборов: 27

Пример вывода --verbosity 3 (наиболее подробный):

=== НАЧАЛИ ПЕРЕСЧЁТ РЕЙТИНГОВ ===

========================================
[ЭТАП 1]: Пересчёт рейтингов ПРОФИЛЕЙ...
========================================

  ✓ Обнулены рейтинги у 94 профилей
  ✓ Найдено 94 профилей для ранжирования
  ...
  ...
  ====================================================================================================
  ПРОФИЛЬ: politech W80 (ID: 78)
  ====================================================================================================
  Характеристика                 Значение        Ранг (0..1)     Вклад          
  ----------------------------------------------------------------------------------------------------
  Высота в проёме                120 мм          0.368 *              
  Популярность                   0 предл.        0.000                
  Теплопередача                  0.91 Ro         0.657 ***            
  Толщина профиля                80 мм           0.588 **             
  Толщина стеклопакета           42 мм           0.409 **             
  Уплотнители                    3 контуров      1.000 *****          
  Фальц                          14 мм           0.150                
  Число камер                    12 шт           0.714 ***            
  Шумоизоляция                   44.00 дБ        0.909 ****           
  ----------------------------------------------------------------------------------------------------
  ИТОГО: Рейтинг = 4.94/5.0 ****
  ...
  ...
  ✓ Сохранено 94 профилей с финальными рейтингами

=============================================
[ЭТАП 2]: Пересчёт рейтингов СТЕКЛОПАКЕТОВ...
=============================================

  ✓ Обнулены рейтинги у 97 стеклопакетов
  ✓ Найдено 97 стеклопакетов для ранжирования
  ...
  ...
  ====================================================================================================
  СТЕКЛОПАКЕТ: Однокамерный 5-4, 25 мм (И+аргон) (ID: 60) | Марка:СПО 5М1-Ar16-И4
  ====================================================================================================
  Характеристика                      Значение             Ранг (0..1)     Вклад          
  ----------------------------------------------------------------------------------------------------
  Камеры                              —                    0.000                
  Светопропускание                    74.00%               0.824 ****           
  Солнцепропускание                   58.00%               0.450 **             
  Теплопередача                       0.91 Ro              0.936 ****           
  Толщина                             25 мм                0.400 **             
  Шумоизоляция                        —                    0.429 **             
  ----------------------------------------------------------------------------------------------------
  ИТОГО: Рейтинг = 4.87/5.0 ****
  ...    
  ...    
  ✓ Сохранено 97 стеклопакетов с финальными рейтингами
  
================================================
[ЭТАП 3]: Пересчёт рейтингов НАБОРОВ (SetKit)...
================================================
  ✓ Обнулены рейтинги у 27 наборов
  ✓ Найдено 27 наборов для ранжирования
  ...  
  ...  
  ========================================================================================================================
  НАБОР: Элит (ID: 3)
  ========================================================================================================================
  Параметр                            Значение             Ранг (0..1)     Вклад          
  ------------------------------------------------------------------------------------------------------------------------
  Актуальность                        свежий               0.375 *              
  Водоотлив                           ✓ Да                 1.000 *****          
  Гибкость скидок                     0 вариантов          0.500 **             
  Доставка                            ✓ Да                 1.000 *****          
  Климат-контроль                     ✓ Да                 1.000 *****          
  Монтаж                              ✓ Да                 1.000 *****          
  Откос                               ✓ Да                 1.000 *****          
  Подоконник                          ✓ Да                 1.000 *****          
  Размер скидок                       0.0%                 0.500 **             
  Число предложений                   46 шт                0.250 *              
  ------------------------------------------------------------------------------------------------------------------------
  ИТОГО: Рейтинг = 4.16/5.0 ****
  ...  
  ...  

[OK!] ПЕРЕСЧЁТ РЕЙТИНГОВ ЗАВЕРШЁН УСПЕШНО!
   • Обновлено профилей: 94
   • Обновлено стеклопакетов: 97
   • Обновлено наборов: 27

Оркестрация и reload веб-сервера

Важно:

  • reload веб-сервера не встроен в management-команды;
  • это отдельная операция окружения.

Пример для systemd + gunicorn:

sudo systemctl reload gunicorn

Рекомендуемый batch-сценарий:

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py regenerate_seria_prerender --force
poetry run python oknardia/manage.py generate_sitemaps
sudo systemctl reload gunicorn

Cron/systemd timer (пример)

Пример cron (раз в сутки в 03:20):

20 3 * * * cd /Users/e-serg/PRJ/2022-oknardia && poetry run python oknardia/manage.py regenerate_seria_prerender --force && poetry run python oknardia/manage.py generate_sitemaps >> /var/log/oknardia-maintenance.log 2>&1

Если нужен reload после batch, добавляй отдельной строкой/шагом оркестратора.

Диагностика

Быстрая проверка конфигурации:

cd /Users/e-serg/PRJ/2022-oknardia
poetry run python oknardia/manage.py check

Типовые причины проблем:

  • нет прав записи в директории templates/seria_info/prepared или MEDIA_ROOT/_serv_sitemap;
  • устаревшее виртуальное окружение / неустановленные зависимости;
  • запуск не из того каталога.

План миграции /service/* -> management commands

Текущее направление:

  • все тяжелые и административные операции переносить из HTTP в management-команды;
  • /service/* оставлять только как thin UI/мониторинг или убрать полностью.

См. также:

  • SETUP.md
  • README.md