Раздел 9 опросника «Значения для расчета критериев значимости»: общие поля + блоки по сферам

- Опросный_лист_187_ФЗ.xlsx: добавлен раздел 9 (общие: доход за 5 лет, прибыль, налог; сферные блоки: здравоохранение, наука, связь, транспорт, банки, ТЭК/энергетика, ОПК)
- import_settings.json: keywords прибыли/налога под новые формулировки, поле income_5y, секции sphere_fields и sphere_headers
- importer.py: _import_187_criteria9() — сбор сферных блоков в data['sphereInput'] и вливание в criterion_input объектов по сфере; фикс поиска по ключам (пустое первое совпадение → ищем дальше)
This commit is contained in:
LocoAgent
2026-08-06 10:47:49 +04:00
parent 691b167725
commit 27706ce6cc
4 changed files with 260 additions and 12 deletions
Binary file not shown.
+161 -5
View File
@@ -140,16 +140,16 @@
"keywords": "право владения зданием"
},
{
"name": "Сумма прибыли за год",
"name": "Прибыль организации за прошлый год",
"var": "profit",
"cell": "",
"keywords": "сумма прибыли"
"keywords": "сумма прибыли|прибыль организации|прибыль за прошлый год"
},
{
"name": "Сумма налога с прибыли",
"name": "Налог на прибыль за прошлый год",
"var": "profitTax",
"cell": "",
"keywords": "сумма налога с прибыли"
"keywords": "сумма налога с прибыли|налог на прибыль"
},
{
"name": "Доменное имя и внешний IP",
@@ -318,6 +318,12 @@
"var": "physicalVisitorLog",
"cell": "",
"keywords": "журнал учета посетителей|журнал учёта посетителей"
},
{
"name": "Суммарный доход (выручка) за 5 лет",
"var": "income_5y",
"cell": "",
"keywords": "суммарный доход|доход.*5 лет|выручка.*5 лет"
}
],
"criterion_fields": [
@@ -441,5 +447,155 @@
"keywords": "кооперации головного исполнителя|статусу в кооперации",
"unit": "вариант"
}
]
],
"sphere_fields": {
"Здравоохранение": [
{
"keywords": "специализированную или скорую",
"key": "health_emergency"
},
{
"keywords": "аптечная сеть с выручкой от 5 млрд",
"key": "health_pharmacy"
},
{
"keywords": "штат от 250",
"key": "health_staff"
},
{
"keywords": "времени нужно на устранение последствий сбоя",
"key": "8_t"
}
],
"Наука": [
{
"keywords": "проводите ли нир",
"key": "science_niokr"
},
{
"keywords": "вредные вещества при исследованиях",
"key": "science_hazard"
},
{
"keywords": "международным договорам",
"key": "7"
},
{
"keywords": "рамках гоз",
"key": "science_goz"
}
],
"Связь": [
{
"keywords": "абонентам станут недоступны",
"key": "4а"
},
{
"keywords": "органа власти вы обеспечиваете",
"key": "4б"
}
],
"Транспорт": [
{
"keywords": "территории нарушится сообщение",
"key": "3а"
},
{
"keywords": "без транспортных услуг",
"key": "3б"
},
{
"keywords": "перевозки выполняете",
"key": "3в"
},
{
"keywords": "категория у объекта транспортной",
"key": "3г"
},
{
"keywords": "времени нужно на устранение последствий сбоя",
"key": "8_t"
},
{
"keywords": "полностью недоступен",
"key": "hours_down"
}
],
"Банковская сфера и финансовый рынок": [
{
"keywords": "типу относится организация",
"key": "10_type"
},
{
"keywords": "объём активов или операций",
"key": "10_value"
}
],
"Топливно-энергетический комплекс": [
{
"keywords": "масштабе возможны последствия",
"key": "2а"
},
{
"keywords": "человек могут пострадать",
"key": "1"
},
{
"keywords": "вредное воздействие на окружающую среду",
"key": "11б"
},
{
"keywords": "выполняете ли гоз",
"key": "goz_flag"
}
],
"Оборонная промышленность": [
{
"keywords": "компенсации пострадавшим",
"key": "8_a"
},
{
"keywords": "компьютерные инциденты",
"key": "8_x"
},
{
"keywords": "может выпустить максимум",
"key": "13a_vmax"
},
{
"keywords": "успеет выпустить объект, пока устраняют",
"key": "13a_vinc"
},
{
"keywords": "полный объём продукции требуется",
"key": "13a_vgoz"
},
{
"keywords": "выпуск единицы продукции в обычном режиме",
"key": "13b_tnorm"
},
{
"keywords": "выпуск единицы продукции при сбое",
"key": "13b_tnonorm"
},
{
"keywords": "декларация промышленной безопасности",
"key": "8_decl"
},
{
"keywords": "перечень стратегических предприятий",
"key": "8_strategic"
}
]
},
"sphere_headers": {
"здравоохранен": "Здравоохранение",
"наука": "Наука",
"связи": "Связь",
"транспорта": "Транспорт",
"банковская": "Банковская сфера и финансовый рынок",
"тэк": "Топливно-энергетический комплекс",
"энергетик": "Топливно-энергетический комплекс",
"опк": "Оборонная промышленность"
}
}
+99 -7
View File
@@ -168,6 +168,9 @@ def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
_import_187_common(wb, data, settings, log_fn)
_import_187_is_list(wb, data, settings, log_fn)
_import_187_employees(wb, data, settings, log_fn)
# Раздел 9 «Значения для расчета критериев значимости» — после импорта объектов,
# чтобы сферные значения влились в criterion_input уже созданных объектов.
_import_187_criteria9(wb, data, settings, log_fn)
except Exception as e:
log_fn(f"❌ Ошибка при импорте: {e}")
log_fn(traceback.format_exc())
@@ -290,16 +293,105 @@ def _import_187_common(wb, data, settings, log_fn):
if rx:
for field_lower, val in row_cache.items():
if rx.search(field_lower):
if val:
if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'):
val = _extract_single_fio(val)
elif 'дата' in name.lower() or 'date' in key.lower():
val = clean_value(val, as_date=True)
data[key] = val
log_fn(f" {name} [по ключу]: {val}")
if not val:
# Пустое значение в первой строке-совпадении — ищем дальше
# (старый вопрос пуст, а новый раздел 9 заполнен).
continue
if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'):
val = _extract_single_fio(val)
elif 'дата' in name.lower() or 'date' in key.lower():
val = clean_value(val, as_date=True)
data[key] = val
log_fn(f" {name} [по ключу]: {val}")
break
def _import_187_criteria9(wb, data, settings, log_fn):
"""Импорт раздела 9 «Значения для расчета критериев значимости» (лист «Общие сведения»).
Общие для всех сфер поля (суммарный доход за 5 лет, прибыль, налог на прибыль)
подхватываются штатным поиском по keywords (fields). Здесь собираются сферные блоки:
для каждого блока «СФЕРА …» значения вопросов сохраняются в
data['sphereInput'][сфера][ключ] = значение, где ключ — из settings['sphere_fields'].
При импорте объектов значения вливаются в criterion_input объекта с той же сферой.
"""
sphere_fields = settings.get('sphere_fields', {}) or {}
sphere_headers = settings.get('sphere_headers', {}) or {}
if not sphere_fields:
return
sheet_name = None
sheet_kw = settings.get('common_sheet_keyword', 'общие')
for sn in wb.sheetnames:
if sheet_kw in sn.lower():
sheet_name = sn
break
if not sheet_name:
return
ws = wb[sheet_name]
value_col = int(settings.get('value_column', 1))
sphere_input = {}
current_sphere = None
started = False
for row in ws.iter_rows(min_row=1, values_only=True):
field_raw = clean_value(row[0]) if row[0] is not None else ''
if not field_raw:
continue
field_lower = field_raw.strip().lower()
# начало раздела 9
if '9. значения' in field_lower or 'значения для расчета критериев значимости' in field_lower:
started = True
continue
if not started:
continue
# заголовок «ОБЩИЕ для всех сфер» — общие поля уже через fields, сферу сбрасываем
if 'общие для всех сфер' in field_lower:
current_sphere = None
continue
# заголовок сферы: «СФЕРА ЗДРАВООХРАНЕНИЯ», «БАНКОВСКАЯ СФЕРА»
if 'сфера' in field_lower or 'банковская' in field_lower:
matched = None
for kw, canon in sphere_headers.items():
if kw in field_lower:
matched = canon
break
if matched:
current_sphere = matched
sphere_input.setdefault(current_sphere, {})
continue
if current_sphere is None:
continue
val = clean_value(row[value_col]) if len(row) > value_col else ''
if not val:
continue
for rule in sphere_fields.get(current_sphere, []):
kws = rule.get('keywords', '')
if not kws:
continue
try:
rx = re.compile(kws, re.IGNORECASE)
except re.error:
continue
if rx.search(field_lower):
key = rule.get('key', '')
sphere_input[current_sphere][key] = str(val).strip()
log_fn(f" [сфера {current_sphere}] {key}: {str(val)[:60]}")
break
if sphere_input:
data['sphereInput'] = sphere_input
# Вливаем сферные значения в criterion_input объектов с той же сферой
for is_obj in data.get('informationSystems', []):
obj_sphere = (is_obj.get('sphere') or '').strip()
if not obj_sphere or obj_sphere not in sphere_input:
continue
ci = is_obj.setdefault('criterion_input', {})
for k, v in sphere_input[obj_sphere].items():
ci.setdefault(k, v)
def _import_187_is_list(wb, data, settings, log_fn):
"""Импорт объектов КИИ из остальных листов (ОБЪЕКТ №1, №2, ...).