import json from modules.ai_verifier import ask_ollama from modules.knowledge_base import load_knowledge_base def generate_markdown_report(merged_df, absent_explained, absent_unexplained, scud_present_but_absent_in_1c, anomalies_list=None, raw_scud_df=None, raw_staff_df=None, date_str="28.07.2026"): kb = load_knowledge_base() custom_rules = kb.get("rules", []) custom_rules_str = "\n".join([f"- {r}" for r in custom_rules]) if custom_rules else "Специфических правил компании пока нет." detailed_cases_for_ai = [] unexplained_fios = absent_unexplained['fio_clean'].unique() if 'fio_clean' in absent_unexplained.columns else [] for fio_clean in unexplained_fios: staff_matches = [] if raw_staff_df is not None and not raw_staff_df.empty and 'fio_clean' in raw_staff_df.columns: st_m = raw_staff_df[raw_staff_df['fio_clean'] == fio_clean] cols_staff = [c for c in ['ФИО', 'Подразделение', 'Должность', 'Таб_№'] if c in st_m.columns] staff_matches = st_m[cols_staff].to_dict(orient='records') scud_matches = [] if raw_scud_df is not None and not raw_scud_df.empty and 'fio_clean' in raw_scud_df.columns: sc_m = raw_scud_df[raw_scud_df['fio_clean'] == fio_clean] cols_scud = [c for c in ['Сотрудник', 'Подразделение', 'Должность', 'Начало_дня', 'Статус'] if c in sc_m.columns] scud_matches = sc_m[cols_scud].to_dict(orient='records') detailed_cases_for_ai.append({ "fio_clean": fio_clean, "staff_records_1C": staff_matches, "scud_records_LOGS": scud_matches }) total_staff = len(merged_df) is_working_mask = (merged_df['Пришел'] == True) | ( merged_df['Вид_отсутствия'].astype(str).str.lower().str.contains('командировк|удален|дистанцион|разъездн', regex=True, na=False) ) present_cnt = len(merged_df[is_working_mask]) explained_cnt = len(absent_explained) unexplained_cnt = len(absent_unexplained) anomalies_cnt = len(anomalies_list) if anomalies_list else 0 prompt = f""" Ты — старший аудитор кадровой безопасности и контроллинга СКУД. Проанализируй аномалии и пересечения данных за {date_str}. 📌 НАКОПЛЕННЫЕ ПРАВИЛА ПРЕДПРИЯТИЯ И ИСКЛЮЧЕНИЯ: {custom_rules_str} 🚨 ВХОДНЫЕ МЕТРИКИ: - Всего сотрудников: {total_staff} - На работе / Удаленно / Командировка: {present_cnt} - Официально отсутствуют (1С/Реестры): {explained_cnt} - Неизвестно (требуют выяснения): {unexplained_cnt} - Выявлено аномалий и конфликтов реестров: {anomalies_cnt} --- 🚨 ОБНАРУЖЕННЫЕ АНОМАЛИИ И КОНФЛИКТЫ МЕЖДУ СКУД, 1С, СТАТИЧЕСКИМИ РЕЕСТРАМИ И ИСКЛЮЧЕНИЯМИ ({anomalies_cnt} шт.): {json.dumps(anomalies_list, ensure_ascii=False, indent=2)} --- 📊 СПИСОК НЕИЗВЕСТНЫХ СЛУЧАЕВ ({unexplained_cnt} чел): {json.dumps(detailed_cases_for_ai, ensure_ascii=False, indent=2)} СТРОГИЙ ШАБЛОН ОТВЕТА: **Сводка контроллинга СКУД и 1С:ЗУП на {date_str}** - Всего офисных сотрудников: **{total_staff}** - Работают (офис / удаленка / командировки): **{present_cnt}** - Официально отсутствуют: **{explained_cnt}** - Неизвестно (истинно неотмеченные): **{unexplained_cnt}** чел. - Выявлено аномалий/конфликтов реестров: **{anomalies_cnt}** шт. #### 🚨 Выявленные ИИ аномалии и конфликты источников ({anomalies_cnt}): (Подробно разбери каждый конфликт из `anomalies_list`. Опиши суть нарушения. Если аномалий нет — напиши "Аномалий и конфликтов не обнаружено.") #### ⚠️ Подозрения на ложные срабатывания / Совмещения: (Укажи сотрудников с повторными проходами или несколькими картами, если такие есть) #### Неизвестные случаи: {unexplained_cnt} (Выведи НУМЕРОВАННЫЙ СПИСОК ВСЕХ {unexplained_cnt} человек в формате: 1. ФИО — Подразделение, Должность 2. ФИО — Подразделение, Должность) ### Рекомендации: (Сформулируй 2-3 общие системные рекомендации для кадровой службы. КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО перечислять конкретные ФИО сотрудников в скобках или тексте рекомендаций — они уже перечислены выше.) """ sys_prompt = "Ты — эксперт кадрового аудита. Пиши сжато, лаконично, без дублирования ФИО сотрудников в блоке рекомендаций." return ask_ollama(prompt, system_prompt=sys_prompt)