промежуточный этап. Работает.

This commit is contained in:
2026-08-04 22:54:00 +03:00
parent 4ee7bd3a3d
commit 0fd145df88
10 changed files with 235 additions and 70 deletions
+25 -11
View File
@@ -2,6 +2,7 @@ import os
import sys
import argparse
import pandas as pd
from datetime import datetime
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
@@ -11,7 +12,7 @@ from services.share_copier import copy_1c_files_from_share
from services.data_validator import check_file_freshness
from services.data_loader import load_all_data
from services.excel_exporter import generate_summary_excel, generate_detailed_excel
from services.ai_verifier import ai_verify_scud_against_staff
from services.ai_verifier import ai_verify_scud_against_staff, analyze_scud_mass_failure_ai
from services.text_reporter import generate_markdown_report
from services.feedback_loop import review_ai_decisions
from services.knowledge_base import load_knowledge_base
@@ -42,12 +43,6 @@ def load_static_reason_workers():
def detect_all_anomalies(merged_df, static_reasons_dict, kb_rules):
"""
Автоматически находит ИСТИННЫЕ аномалии.
ПРАВИЛА КОМПАНИИ:
1. Удалённые сотрудники имеют право беспрепятственно работать в офисе (НЕ аномалия).
2. Документы отсутствий из 1С имеют 100% приоритет над стат. реестром (НЕ аномалия).
3. АНОМАЛИЯ — Физический выход по СКУД при наличии в 1С ЛЮБЫХ причин отсутствия
(отпуск, больничный, уход за ребенком и т.д.), КРОМЕ командировок и служебных поездок.
"""
anomalies = []
kb_rules_text = " ".join(kb_rules).lower() if kb_rules else ""
@@ -60,9 +55,11 @@ def detect_all_anomalies(merged_df, static_reasons_dict, kb_rules):
is_present = row.get('Пришел', False)
reason_1c = str(row.get('Вид_отсутствия', '')).strip()
has_1c_reason = pd.notna(row.get('Вид_отсутствия')) and reason_1c != ''
anom_flag = row.get('anomaly_flag', 'NONE')
is_fio_whitelisted_in_kb = fio_clean.lower() in kb_rules_text
# АНОМАЛИЯ 1: Физическое присутствие при документе отсутствия 1С
if is_present and has_1c_reason:
reason_lower = reason_1c.lower()
is_allowed_trip = any(kw in reason_lower for kw in ALLOWED_WORK_TRIP_KEYWORDS)
@@ -74,6 +71,15 @@ def detect_all_anomalies(merged_df, static_reasons_dict, kb_rules):
"details": f"Сотрудник пришел по СКУД, но в 1С оформлен документ: '{reason_1c}'"
})
# АНОМАЛИЯ 2: Перемещение внутри здания без отметки утреннего входа
if anom_flag == 'ANOMALY_NO_IN_HAS_ACTIVITY':
first_act = row.get('Первая_активность', '—')
anomalies.append({
"type": "АНОМАЛИЯ СКУД: ПЕРЕМЕЩЕНИЕ БЕЗ ВХОДА",
"fio": fio,
"details": f"Отсутствует регистрация входа на КПП при зафиксированной первой активности в {first_act}"
})
return anomalies
@@ -106,6 +112,7 @@ def main():
args = parser.parse_args()
DEBUG = args.debug
snapshot_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print("=" * 60)
print(f"ЗАПУСК СИСТЕМЫ МОДУЛЬНОГО КОНТРОЛЛИНГА СКУД ⟷ 1С {'[DEBUG MODE]' if DEBUG else ''}")
@@ -116,7 +123,7 @@ def main():
kb_data = load_knowledge_base()
kb_rules = kb_data.get("rules", [])
# 0.1 Выгрузка из БД СКУД напрямую в SQLite и Excel (save_xlsx=True требуется для data_loader)
# 0.1 Выгрузка из БД СКУД напрямую в SQLite и Excel
if not args.skip_export:
try:
run_export(save_xlsx=True, debug=DEBUG)
@@ -219,13 +226,12 @@ def main():
generate_detailed_excel(merged_df=merged_yesterday, date_str=DATE_YESTERDAY)
# 💾 СОХРАНЕНИЕ ВЧЕРАШНИХ ДАННЫХ В SQLite:
save_scud_to_db(merged_yesterday, DATE_YESTERDAY)
save_scud_to_db(merged_yesterday, DATE_YESTERDAY, snapshot_time=snapshot_time)
# ============================================================
# 🎯 ЧАСТЬ 2: ЕЖЕДНЕВНАЯ СВОДКА ЗА СЕГОДНЯ
# ============================================================
# 💾 СОХРАНЯЕМ СЕГОДНЯШНИЙ СКУД В SQLite (Даже если 1С еще не пришла):
save_scud_to_db(raw_scud_today_df, DATE_TODAY)
save_scud_to_db(raw_scud_today_df, DATE_TODAY, snapshot_time=snapshot_time)
if has_today_1c and df_staff_today is not None and df_absent_today is not None:
print("\n[4/5] Обработка данных за СЕГОДНЯ...")
@@ -269,6 +275,14 @@ def main():
anomalies_list = detect_all_anomalies(merged_today, static_reasons_dict, kb_rules)
# ⭐️ ПРОВЕРКА МАССОВОГО СБОЯ ОБОРУДОВАНИЯ ЧЕРЕЗ ИИ
mass_failure_result = analyze_scud_mass_failure_ai(merged_today)
if mass_failure_result and mass_failure_result.get("is_mass_failure"):
print("\n" + "!" * 60)
print(f"🚨 ВНИМАНИЕ! ИИ ОБНАРУЖИЛ МАССОВЫЙ СБОЙ ТУРНИКЕТОВ ВХОДА ({mass_failure_result['anomaly_percent']}% СМЕНЫ)")
print(mass_failure_result["alert_text"])
print("!" * 60 + "\n")
# 💾 СОХРАНЕНИЕ СЕГОДНЯШНИХ АНОМАЛИЙ В БАЗУ ДАННЫХ SQLite
save_anomalies_to_db(anomalies_list, DATE_TODAY)