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, raw_scud_df=None, raw_staff_df=None, date_str="27.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_count": len(staff_matches), "staff_records_1C": staff_matches, "scud_records_LOGS": scud_matches }) cols_crit = [c for c in ['Сотрудник', 'Подразделение', 'Должность', 'Вид_отсутствия'] if c in scud_present_but_absent_in_1c.columns] critical_list = scud_present_but_absent_in_1c[cols_crit].to_dict(orient='records') # Исходные точные цифры Python total_staff = len(merged_df) present_cnt = len(merged_df[merged_df['Пришел'] == True]) explained_cnt = len(absent_explained) unexplained_cnt = len(absent_unexplained) prompt = f""" Ты — старший аудитор кадровой безопасности и систем СКУД. Проанализируй данные за {date_str} и составь итоговый отчет. 📌 ДИНАМИЧЕСКИЕ ПРАВИЛА И НАКОПЛЕННЫЙ ОПЫТ ПРЕДПРИЯТИЯ: {custom_rules_str} 🚨 СТРОГИЕ ПРАВИЛА МАТЕМАТИКИ И КАТЕГОРИЙ: 1. Цифры сводки ДОЛЖНЫ СТРОГО СОВПАДАТЬ с первично посчитанным массивом: - Всего офисных сотрудников: {total_staff} - Присутствуют на рабочем месте: {present_cnt} - Официально отсутствуют: {explained_cnt} - Неизвестно (истинно неотмеченные): {unexplained_cnt} чел. 2. Заголовок "#### Неизвестные случаи: {unexplained_cnt}" должен иметь ровно ту же цифру {unexplained_cnt}. 3. Нумерованный список ниже ДОЛЖЕН содержать ровно {unexplained_cnt} сотрудников из массива `detailed_cases_for_ai` без пропусков. --- 📊 ВХОДНЫЕ МИКРО-ДАННЫЕ НЕИЗВЕСТНЫХ ({unexplained_cnt} чел): {json.dumps(detailed_cases_for_ai, ensure_ascii=False, indent=2)} 🚨 СПИСОК КРИТИЧЕСКИХ АНОМАЛИЙ: {json.dumps(critical_list, ensure_ascii=False, indent=2)} СТРОГИЙ ШАБЛОН ОТВЕТА: **Сводка контроллинга СКУД и 1С:ЗУП на {date_str} в 12:00** - Всего офисных сотрудников: **{total_staff}** - Присутствуют на рабочем месте: **{present_cnt}** - Официально отсутствуют: **{explained_cnt}** - Неизвестно (истинно неотмеченные после проверки ИИ): **{unexplained_cnt}** чел. #### ⚠️ Подозрения на ложные срабатывания СКУД / Совмещения: (Укажи сотрудников с аномалиями/вторыми проходами, например Кондрашову Е.В., если у нее дубликат карты) #### Критические аномалии (Работа в отпуске / на больничном): (Детально опиши сотрудников из списка КРИТИЧЕСКИХ АНОМАЛИЙ. Если их нет — напиши "Критических нарушений не выявлено.") #### Неизвестные случаи: {unexplained_cnt} (Выведи НУМЕРОВАННЫЙ СПИСОК ВСЕХ {unexplained_cnt} человек строго в формате: 1. ФИО — [Аббревиатура], Должность 2. ФИО — [Аббревиатура], Должность ... {unexplained_cnt}. ФИО — [Аббревиатура], Должность) ### Рекомендации: ... """ sys_prompt = "Ты — кадровый аудитор. Выводи ровно то количество людей в списке, которое указано в переменной unexplained_cnt." return ask_ollama(prompt, system_prompt=sys_prompt)