feat: подбор типового отраслевого объекта с фильтром по сфере

- detect_sphere(): автоопределение сферы по ключевым словам названия/
  назначения/описания объекта (здравоохранение, наука, транспорт, связь,
  энергетика, банки, ТЭК, атомная, ОПК, ракетно-космическая, горная,
  металлургия, химия)
- suggest_typical_object_program: фильтр по сфере в приоритете —
  1) сфера из комбобокса, 2) сфера объекта (isys.sphere), 3) автодетект,
  4) иначе все сферы. ОПК-объекту НЕ подбираются объекты из
  здравоохранения и других сфер
- если в своей сфере совпадений нет — возвращается пусто, оператор
  выбирает сферу вручную
This commit is contained in:
2026-08-04 22:52:45 +04:00
parent 52b3bb8477
commit 0098f483d2
+58 -2
View File
@@ -456,9 +456,24 @@ def suggest_typical_object_program(isys, limit=10, sphere=None):
if not obj_words:
return []
# Фильтр по сфере (важно: не подбирать ОПК-объекту сферу из здравоохранения):
# 1) сфера, переданная явно (выбрана в комбобоксе);
# 2) сфера, уже заполненная у объекта (isys.sphere);
# 3) автодетект по названию/назначению/описанию;
# 4) иначе — все сферы (подбор без фильтра).
used_sphere = sphere
if not used_sphere:
used_sphere = (getattr(isys, 'sphere', '') or '').strip() or None
if not used_sphere:
used_sphere = detect_sphere(isys)
cands = _load_all_typical()
if sphere:
cands = [c for c in cands if c['sphere'] == sphere]
if used_sphere:
cands = [c for c in cands if c['sphere'] == used_sphere]
if not cands:
# Сфера определена, но объектов нет — не расширяем на другие сферы,
# возвращаем пусто (оператор выберет сферу вручную)
return []
weights = _idf_weights(cands)
@@ -474,3 +489,44 @@ def suggest_typical_object_program(isys, limit=10, sphere=None):
scored.sort(key=lambda x: (-x['score'], x['name']))
return scored[:limit]
# ---------------------------------------------------------------
# Автоопределение сферы деятельности объекта по ключевым словам
# ---------------------------------------------------------------
SPHERE_KEYWORDS = [
('Здравоохранение', ['медицин', 'здрав', 'поликлиник', 'больниц', 'лечеб', 'клиник', 'фармацевт', 'аптек', 'пациент', 'врач', 'диагностик', 'лабораторн', 'медпомощ']),
('Наука', ['научн', 'исследован', 'разработк', 'лаборатор', 'академи', 'институт', 'ниокр', 'наука']),
('Транспорт', ['транспорт', 'перевоз', 'автомобил', 'железнодорожн', 'авиа', 'морск', 'речн', 'метрополитен', 'вокзал', 'логистик', 'движени']),
('Связь', ['связ', 'телекоммуникац', 'оператор связи', 'интернет-провайдер', 'передач данных', 'сети связи', 'телефон']),
('Энергетика', ['энерг', 'электро', 'теплоснабж', 'электроснабж', 'генерац', 'подстанц', 'котельн', 'энергосистем']),
('Государственная регистрация прав', ['регистрац', 'кадастр', 'недвижим', 'реестр']),
('Банковская сфера и финансовый рынок', ['банк', 'финанс', 'кредит', 'платеж', 'расчет', 'страхов', 'клирин', 'депозитар', 'пенсионн', 'фондов']),
('Топливно-энергетический комплекс', ['нефт', 'газ', 'топлив', 'трубопровод', 'месторожден', 'переработк', 'уголь', 'нефте']),
('Атомная энергетика', ['атомн', 'ядерн', 'радиацион', 'аэс', 'реактор', 'атом']),
('Оборонная промышленность', ['оборон', 'военн', 'вооружен', 'оружи', 'оборонно', 'опк', 'госзаказ', 'госконтракт', 'боеприпас', 'ввт', 'минобороны']),
('Ракетно-космическая промышленность', ['ракет', 'космическ', 'спутник', 'космодром', 'орбит', 'космос', 'пуск']),
('Горнодобывающая промышленность', ['горн', 'добыч', 'шахт', 'рудник', 'карьер', 'месторожден']),
('Металлургическая промышленность', ['металлург', 'прокат', 'стал', 'чугун', 'цветн', 'сплав']),
('Химическая промышленность', ['химическ', 'хим', 'нефтехим', 'взрывчат', 'кислот', 'реактив', 'удобрен']),
]
def detect_sphere(isys):
"""Определяет сферу деятельности объекта КИИ по названию/назначению/описанию.
Возвращает название сферы или None, если определить не удалось.
"""
text = ' '.join([
getattr(isys, 'name', '') or '',
getattr(isys, 'purpose', '') or '',
getattr(isys, 'description', '') or '',
]).lower()
best = None
best_score = 0
for sphere, keywords in SPHERE_KEYWORDS:
score = sum(1 for kw in keywords if kw in text)
if score > best_score:
best = sphere
best_score = score
return best if best_score > 0 else None