add: Django Management Command для "склейки" корневых серий типового строительства
This commit is contained in:
@@ -9,10 +9,11 @@
|
|||||||
|
|
||||||
## Каталог команд
|
## Каталог команд
|
||||||
|
|
||||||
1. `generate_sitemaps` — оффлайн генерация sitemap-файлов.
|
1. `regenerate_seria_roots` — пересчет корневых серий (иерархия и консолидация).
|
||||||
2. `regenerate_seria_prerender` — оффлайн пересборка pre-render шаблонов для `catalog_seria_info`.
|
2. `generate_sitemaps` — оффлайн генерация sitemap-файлов.
|
||||||
3. `populate_seo_fields` — автозаполнение SEO-полей блога из существующих данных.
|
3. `regenerate_seria_prerender` — оффлайн пересборка pre-render шаблонов для `catalog_seria_info`.
|
||||||
4. `make_rating` — пересчёт рейтингов профилей и стеклопакетов методом Манна-Уитни.
|
4. `populate_seo_fields` — автозаполнение SEO-полей блога из существующих данных.
|
||||||
|
5. `make_rating` — пересчёт рейтингов профилей и стеклопакетов методом Манна-Уитни.
|
||||||
|
|
||||||
## Общие правила запуска
|
## Общие правила запуска
|
||||||
|
|
||||||
@@ -28,7 +29,210 @@ cd /Users/e-serg/PRJ/2022-oknardia
|
|||||||
poetry run python oknardia/manage.py <command> [args]
|
poetry run python oknardia/manage.py <command> [args]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 1) Команда `generate_sitemaps`
|
## 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 | Алиас (через родителя)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Базовый запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/e-serg/PRJ/2022-oknardia
|
||||||
|
poetry run python oknardia/manage.py regenerate_seria_roots
|
||||||
|
```
|
||||||
|
|
||||||
|
### Параметры запуска
|
||||||
|
|
||||||
|
**`--verbosity 0`** — только ошибки (минимум информации):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 0
|
||||||
|
```
|
||||||
|
|
||||||
|
**`--verbosity 1`** — точки/плюсы (стандартный режим):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry run python oknardia/manage.py regenerate_seria_roots
|
||||||
|
# или явно
|
||||||
|
poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**`--verbosity 2`** — подробный вывод (названия серий):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry run python oknardia/manage.py regenerate_seria_roots --verbosity 2
|
||||||
|
```
|
||||||
|
|
||||||
|
**`--verbosity 3`** — очень подробный вывод в виде таблицы:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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с
|
||||||
|
```
|
||||||
|
|
||||||
|
### Когда запускать
|
||||||
|
|
||||||
|
- **После первого развертывания** — консолидировать иерархию серий.
|
||||||
|
- **После ручного редактирования иерархии** (добавления родитель-потомков в админку).
|
||||||
|
- **После загрузки новых данных** с разными орфографическими вариантами серий.
|
||||||
|
- **По расписанию** (опционально, например раз в месяц):
|
||||||
|
```bash
|
||||||
|
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** — если нужно очистить поле (перед запуском рекомендуется бэкап):
|
||||||
|
```sql
|
||||||
|
UPDATE oknardia_seria_info SET kRoot_id = NULL;
|
||||||
|
```
|
||||||
|
- **Проверка результатов** — после запуска можно проверить:
|
||||||
|
```bash
|
||||||
|
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_sitemaps`
|
||||||
|
|
||||||
Назначение:
|
Назначение:
|
||||||
- пересобрать `sitemap.xml` и chunk-файлы в `MEDIA_ROOT/_serv_sitemap`.
|
- пересобрать `sitemap.xml` и chunk-файлы в `MEDIA_ROOT/_serv_sitemap`.
|
||||||
@@ -76,7 +280,7 @@ location = /sitemap.xml {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2) Команда `regenerate_seria_prerender`
|
## 3) Команда `regenerate_seria_prerender`
|
||||||
|
|
||||||
Назначение:
|
Назначение:
|
||||||
- пересобрать pre-render шаблоны для страниц серий (`catalog_seria_info`) в каталоге `seria_info/prepared/`.
|
- пересобрать pre-render шаблоны для страниц серий (`catalog_seria_info`) в каталоге `seria_info/prepared/`.
|
||||||
@@ -114,7 +318,7 @@ poetry run python oknardia/manage.py regenerate_seria_prerender --seria-id 843 -
|
|||||||
- после массового обновления данных серий/окон/квартир;
|
- после массового обновления данных серий/окон/квартир;
|
||||||
- после очистки `seria_info/prepared/`.
|
- после очистки `seria_info/prepared/`.
|
||||||
|
|
||||||
## 3) Команда `populate_seo_fields`
|
## 4) Команда `populate_seo_fields`
|
||||||
|
|
||||||
Назначение:
|
Назначение:
|
||||||
- автозаполнить SEO-поля (`sSlug`, `sMetaDescription`, `sMetaKeywords`) для всех существующих записей блога.
|
- автозаполнить SEO-поля (`sSlug`, `sMetaDescription`, `sMetaKeywords`) для всех существующих записей блога.
|
||||||
@@ -226,7 +430,7 @@ print(f'Пусто sMetaKeywords: {posts.filter(sMetaKeywords=\"\").count()}')
|
|||||||
- ✅ **Откат через SQL** — если нужно очистить, используй: `UPDATE oknardia_blogposts SET sSlug='', sMetaDescription='', sMetaKeywords='';`
|
- ✅ **Откат через SQL** — если нужно очистить, используй: `UPDATE oknardia_blogposts SET sSlug='', sMetaDescription='', sMetaKeywords='';`
|
||||||
- ✅ **Всегда используй `--dry-run`** перед первым запуском для проверки.
|
- ✅ **Всегда используй `--dry-run`** перед первым запуском для проверки.
|
||||||
|
|
||||||
## 4) Команда `make_rating`
|
## 5) Команда `make_rating`
|
||||||
|
|
||||||
Назначение:
|
Назначение:
|
||||||
- пересчитать рейтинги оконных профилей, стеклопакетов и наборов услуг используя адаптированный метод Манна-Уитни (Mann-Whitney U Step Rank).
|
- пересчитать рейтинги оконных профилей, стеклопакетов и наборов услуг используя адаптированный метод Манна-Уитни (Mann-Whitney U Step Rank).
|
||||||
|
|||||||
202
oknardia/web/management/commands/regenerate_seria_roots.py
Normal file
202
oknardia/web/management/commands/regenerate_seria_roots.py
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Django management command: regenerate_seria_roots
|
||||||
|
|
||||||
|
Пересчет корневых серий (root series) для всех серий домов.
|
||||||
|
|
||||||
|
Идея:
|
||||||
|
Серии домов могут иметь сложную иерархию (дерево потомственности) из-за того,
|
||||||
|
что на разных сайтах одна и та же серия обозначалась по-разному.
|
||||||
|
Например серия II-57 записывалась как 2-57, И-57, П-57 и т.п.
|
||||||
|
|
||||||
|
Эта команда:
|
||||||
|
1. Находит "корневые" серии - те, что используются в Apartment_Type (у них есть квартиры)
|
||||||
|
2. Для всех остальных серий ищет их корневую серию, двигаясь вверх по дереву иерархии
|
||||||
|
3. Устанавливает найденную корневую серию в поле kRoot_id каждой серии
|
||||||
|
|
||||||
|
Результат:
|
||||||
|
- Все серии-алиасы (синонимы) указывают на одну корневую серию
|
||||||
|
- Это позволяет консолидировать данные по сериям дома
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from oknardia.models import Seria_Info, Apartment_Type
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Пересчитывает корневые серии для всей иерархии серий домов'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
# Django уже добавляет --verbosity автоматически
|
||||||
|
# 0 = минимум (только ошибки)
|
||||||
|
# 1 = нормально (точки)
|
||||||
|
# 2 = подробно (информация)
|
||||||
|
# 3 = очень подробно (таблица)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
verbose = int(options.get('verbosity', 1))
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS('=== ПЕРЕСЧЕТ КОРНЕВЫХ СЕРИЙ ===\n'))
|
||||||
|
|
||||||
|
time_start = self.get_time()
|
||||||
|
|
||||||
|
# ========== ЭТАП 1: Находим корневые серии ==========
|
||||||
|
if verbose >= 1:
|
||||||
|
self.stdout.write('Этап 1: Ищем корневые серии в таблице квартир...\n')
|
||||||
|
|
||||||
|
# Получаем все УНИКАЛЬНЫЕ серии, которые используются в квартирах
|
||||||
|
root_series_ids = list(
|
||||||
|
Apartment_Type.objects.values_list('kSeria_id', flat=True).distinct()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Получаем объекты корневых серий (для вывода их названий)
|
||||||
|
root_series = Seria_Info.objects.filter(id__in=root_series_ids)
|
||||||
|
|
||||||
|
if verbose >= 1:
|
||||||
|
self.stdout.write(f'✓ Найдено корневых серий: {len(root_series_ids)}\n')
|
||||||
|
|
||||||
|
# Устанавливаем для корневых серий kRoot_id = own_id
|
||||||
|
for seria in root_series:
|
||||||
|
seria.kRoot_id = seria.id # Серия сама себе корень
|
||||||
|
seria.save()
|
||||||
|
|
||||||
|
if verbose >= 3:
|
||||||
|
# Очень подробный вывод - таблица
|
||||||
|
self.stdout.write(
|
||||||
|
f' ✓ {seria.id:04d} | {seria.sName:<30} | корневая'
|
||||||
|
)
|
||||||
|
elif verbose >= 2:
|
||||||
|
# Подробный - с названием
|
||||||
|
self.stdout.write(f' ✓ {seria.id:04d} {seria.sName}')
|
||||||
|
elif verbose >= 1:
|
||||||
|
# Нормально - точки
|
||||||
|
self.stdout.write('.', ending='')
|
||||||
|
|
||||||
|
if verbose < 2:
|
||||||
|
self.stdout.write('\n')
|
||||||
|
self.stdout.write('')
|
||||||
|
|
||||||
|
# ========== ЭТАП 2: Обрабатываем все серии ==========
|
||||||
|
if verbose >= 1:
|
||||||
|
self.stdout.write('\nЭтап 2: Главная магия - обрабатываем все серии в иерархии...\n')
|
||||||
|
|
||||||
|
if verbose >= 3:
|
||||||
|
# Заголовок таблицы для очень подробного режима
|
||||||
|
self.stdout.write('-' * 110)
|
||||||
|
self.stdout.write(
|
||||||
|
f'{"ID":>5} | {"Название":<35} | {"Родитель":>10} | '
|
||||||
|
f'{"Путь":<40} | {"Результат":<20}'
|
||||||
|
)
|
||||||
|
self.stdout.write('-' * 110)
|
||||||
|
|
||||||
|
all_series = Seria_Info.objects.all()
|
||||||
|
count_with_root = 0
|
||||||
|
count_without_root = 0
|
||||||
|
count_errors = 0
|
||||||
|
count_root = 0
|
||||||
|
|
||||||
|
for seria in all_series:
|
||||||
|
try:
|
||||||
|
# Если это уже корневая серия, пропускаем
|
||||||
|
if seria.id in root_series_ids:
|
||||||
|
count_root += 1
|
||||||
|
if verbose >= 3:
|
||||||
|
self.stdout.write(
|
||||||
|
f'{seria.id:>5} | {seria.sName:<35} | '
|
||||||
|
f'{str(seria.kParent_id or "-"):>10} | '
|
||||||
|
f'(корневая) | ✓'
|
||||||
|
)
|
||||||
|
elif verbose >= 2:
|
||||||
|
self.stdout.write(f' {seria.id:04d}: корневая')
|
||||||
|
elif verbose >= 1:
|
||||||
|
self.stdout.write('.', ending='')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Движемся вверх по дереву потомок → предок
|
||||||
|
current_id = seria.kParent_id
|
||||||
|
path_trace = []
|
||||||
|
|
||||||
|
# Рекурсивно ищем корневую серию
|
||||||
|
while current_id is not None:
|
||||||
|
path_trace.append(current_id)
|
||||||
|
try:
|
||||||
|
parent_seria = Seria_Info.objects.get(id=current_id)
|
||||||
|
|
||||||
|
# Проверяем: либо у родителя нет родителя, либо родитель - корневая
|
||||||
|
if parent_seria.kParent_id is None or current_id in root_series_ids:
|
||||||
|
break
|
||||||
|
|
||||||
|
current_id = parent_seria.kParent_id
|
||||||
|
except Seria_Info.DoesNotExist:
|
||||||
|
current_id = None
|
||||||
|
break
|
||||||
|
|
||||||
|
# Проверяем, что нашли корневую серию
|
||||||
|
if current_id and current_id in root_series_ids:
|
||||||
|
seria.kRoot_id = current_id
|
||||||
|
seria.save()
|
||||||
|
|
||||||
|
if verbose >= 3:
|
||||||
|
path_str = ' → '.join(str(i) for i in path_trace)
|
||||||
|
self.stdout.write(
|
||||||
|
f'{seria.id:>5} | {seria.sName:<35} | '
|
||||||
|
f'{str(seria.kParent_id or "-"):>10} | '
|
||||||
|
f'{path_str:<40} | ✓ Корень #{current_id}'
|
||||||
|
)
|
||||||
|
elif verbose >= 2:
|
||||||
|
self.stdout.write(f' {seria.id:04d}: корень → {current_id}')
|
||||||
|
elif verbose >= 1:
|
||||||
|
self.stdout.write('+', ending='')
|
||||||
|
|
||||||
|
count_with_root += 1
|
||||||
|
else:
|
||||||
|
seria.kRoot_id = None
|
||||||
|
seria.save()
|
||||||
|
|
||||||
|
if verbose >= 3:
|
||||||
|
path_str = ' → '.join(str(i) for i in path_trace) if path_trace else '(нет)'
|
||||||
|
self.stdout.write(
|
||||||
|
f'{seria.id:>5} | {seria.sName:<35} | '
|
||||||
|
f'{str(seria.kParent_id or "-"):>10} | '
|
||||||
|
f'{path_str:<40} | ✗ Нет корня'
|
||||||
|
)
|
||||||
|
elif verbose >= 2:
|
||||||
|
self.stdout.write(f' {seria.id:04d}: Нет корня')
|
||||||
|
elif verbose >= 1:
|
||||||
|
self.stdout.write('-', ending='')
|
||||||
|
|
||||||
|
count_without_root += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if verbose >= 3:
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.ERROR(f'{seria.id:>5} | ОШИБКА: {str(e):<80}')
|
||||||
|
)
|
||||||
|
elif verbose >= 1:
|
||||||
|
self.stdout.write('E', ending='')
|
||||||
|
count_errors += 1
|
||||||
|
|
||||||
|
if verbose >= 3:
|
||||||
|
self.stdout.write('-' * 110)
|
||||||
|
elif verbose < 2:
|
||||||
|
self.stdout.write('\n')
|
||||||
|
|
||||||
|
# ========== РЕЗУЛЬТАТЫ ==========
|
||||||
|
self.stdout.write(self.style.SUCCESS('\n\n=== РЕЗУЛЬТАТЫ ==='))
|
||||||
|
self.stdout.write(f'✓ Корневых серий (обработаны на этапе 1): {count_root}')
|
||||||
|
self.stdout.write(f'✓ Серий с найденным корнем: {count_with_root}')
|
||||||
|
self.stdout.write(f'⚠ Серий без корня: {count_without_root}')
|
||||||
|
if count_errors > 0:
|
||||||
|
self.stdout.write(self.style.ERROR(f'✗ Ошибок: {count_errors}'))
|
||||||
|
|
||||||
|
time_elapsed = self.get_time() - time_start
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.SUCCESS(f'\n✅ Пересчет завершен! Время: {time_elapsed:.2f}с')
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_time():
|
||||||
|
import time
|
||||||
|
return time.perf_counter()
|
||||||
|
|
||||||
Reference in New Issue
Block a user