промежуточный этап. Работает.
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user