/
TurGG
/
AI-Assistant
Обзор
Документация
Войти
/
TurGG
/
AI-Assistant
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tools.py
1 010 строк
39 KB
den
Админ-логика: мероприятия, баллы, база знаний + Django + Docker
23 мар 2026, 16:32
23 мар 2026, 16:32
617abaa
Код
Авторство
О чём код?
# =========================================================== # tools.py — Инструменты Изнанки (Backend Layer) # =========================================================== # Модуль бизнес-логики агента. # # ВАЖНО: Вся работа с данными идёт через storage.py (слой абстракции). # При миграции на БД этот файл потребует минимальных изменений. # =========================================================== import logging import os from pathlib import Path import pandas as pd from config import EXPORT_FILE, LOG_LEVEL, LOG_FILE, LOG_FORMAT from storage import storage # =========================================================== # ЛОГИРОВАНИЕ # =========================================================== logging.basicConfig( level=getattr(logging, LOG_LEVEL), format=LOG_FORMAT, handlers=[ logging.FileHandler(LOG_FILE, encoding="utf-8"), logging.StreamHandler(), ], ) logger = logging.getLogger(__name__) # =========================================================== # ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ # =========================================================== def _get_field(obj, field, default=None): """Безопасное получение поля из dict или объекта.""" if isinstance(obj, dict): return obj.get(field, default) return getattr(obj, field, default) if hasattr(obj, field) else default # =========================================================== # ИНСТРУМЕНТЫ АГЕНТА # =========================================================== def get_top_n(n=10): """ Возвращает общий список лидеров (индивидуальный зачёт). Args: n: Количество записей в топе Returns: Форматированный текст с рейтингом """ try: n = int(n) if n <= 0: logger.warning(f"Некорректный запрос top_n: n={n}") return "[ОШИБКА] N должно быть положительным числом." top_list = storage.get_users_sorted_by_points(limit=n) logger.info(f"Запрошен топ-{len(top_list)} лидеров") result = "=== ИНДИВИДУАЛЬНЫЙ РЕЙТИНГ ===\n" for i, u in enumerate(top_list, 1): # Поддержка как dict, так и User name = u["name"] if isinstance(u, dict) else u.name team = u.get("team", "Без команды") if isinstance(u, dict) else (u.team or "Без команды") points = u["points"] if isinstance(u, dict) else u.points achievements = u.get("achievements", []) if isinstance(u, dict) else u.achievements achievements_str = ", ".join(achievements) result += f"{i}. {name} [{team}] — {points} очк.\n" return result except ValueError: logger.error("Некорректный аргумент n для get_top_n") return "[ОШИБКА] N должно быть числом." except Exception as e: logger.exception(f"Ошибка в get_top_n: {e}") return f"Ошибка базы данных: {e}" def get_team_ranking(): """ Возвращает рейтинг команд (сумма баллов участников). Returns: Форматированный текст с рейтингом команд """ try: logger.info("Запрошен командный рейтинг") team_stats = storage.get_teams_with_scores() if not team_stats: return "Данные о командах отсутствуют в системе." result = "=== РЕЙТИНГ КОМАНД ХОКИНСА ===\n" for rank, (team, points) in enumerate(team_stats, 1): result += f"{rank}. {team} — {points} суммарных очков\n" return result except Exception as e: logger.exception(f"Ошибка в get_team_ranking: {e}") return f"Ошибка расчета командного рейтинга: {e}" def get_team_members_func(team_name): """ Возвращает список участников конкретной команды. Args: team_name: Название команды Returns: Форматированный текст со списком участников """ try: if team_name: team_name = team_name.strip().strip('"\'') if not team_name: logger.warning("Пустое имя команды в get_team_members") return "[ОШИБКА] Укажите имя команды." logger.info(f"Запрошен состав команды: {team_name}") team_users = storage.get_team_members(team_name) if not team_users: logger.warning(f"Команда '{team_name}' не найдена") return f"Команда '{team_name}' не обнаружена на радарах." team_name_val = _get_field(team_users[0], "team") result = f"=== СОСТАВ ОТРЯДА: {team_name_val} ===\n" for u in team_users: name = _get_field(u, "name") points = _get_field(u, "points") result += f"- {name}: {points} очк.\n" return result except Exception as e: logger.exception(f"Ошибка в get_team_members: {e}") return f"Ошибка поиска команды: {e}" def get_user_details(user_id): """ Глубокий анализ баллов пользователя: разделение на Тесты, Ивенты, Проекты. Args: user_id: ID пользователя Returns: Форматированный текст с досье пользователя """ try: user_id = int(user_id) logger.info(f"Запрошено досье пользователя ID={user_id}") user = storage.get_user_by_id(user_id) if not user: logger.warning(f"Пользователь ID={user_id} не найден") return "Объект не найден." # Получаем details из dict или объекта if isinstance(user, dict): details = user.get("details", {}) else: # Для User объекта details - это вложенные атрибуты details = { "tests": [{"name": t.name, "score": t.score} for t in user.tests], "events": [{"name": e.name, "date": e.date, "attended": e.attended} for e in user.events], "projects": [ {"name": p.name, "status": p.status, "points": p.points, "current_stage": p.current_stage} for p in user.projects ], } tests_score = sum(t.get("score", 0) for t in details.get("tests", [])) projects_score = sum(p.get("points", 0) for p in details.get("projects", [])) events_count = len(details.get("events", [])) name = _get_field(user, "name", "Неизвестно") team = _get_field(user, "team", "Одиночка") or "Одиночка" points = _get_field(user, "points", 0) achievements = _get_field(user, "achievements", []) report = ( f"=== ДОСЬЕ: {name} ===\n" f"Команда: {team}\n" f"Общий уровень: {points} XP\n" f"--------------------------------\n" f"[ТЕСТЫ]: {tests_score} очк. (Пройдено: {len(details.get('tests', []))})\n" ) if details.get("tests"): for t in details["tests"]: report += f" • {t.get('name', 'Без названия')}: {t.get('score', 0)} очк.\n" report += f"[ПРОЕКТЫ]: {projects_score} очк. (Завершено: {len(details.get('projects', []))})\n" if details.get("projects"): for p in details["projects"]: status = p.get("status", "Неизвестно") points_val = p.get("points", 0) report += f" • {p.get('name', 'Без названия')}: {status} ({points_val} очк.)\n" report += f"[СОБЫТИЯ]: Посещено {events_count} мероприятий (баллы не начисляются).\n" if details.get("events"): for e in details["events"]: date = e.get("date", "Дата неизвестна") report += f" • {e.get('name', 'Без названия')} ({date})\n" report += f"--------------------------------\n" report += f"Достижения: {', '.join(achievements)}" return report except ValueError: logger.error("Некорректный user_id в get_user_details") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в get_user_details: {e}") return f"Ошибка чтения досье: {e}" def get_user_status(user_id): """Краткий статус пользователя (алиас get_user_details).""" return get_user_details(user_id) def export_ranking_to_excel(): """ Продвинутый экспорт: ID, Имя, Команда, Тесты, Проекты, Итого. Returns: Сообщение об успехе или ошибке """ try: logger.info("Запрошен экспорт в Excel") users = storage.get_all_users() rows = [] for u in users: details = u.get("details", {}) tests_score = sum(t.get("score", 0) for t in details.get("tests", [])) projects_score = sum(p.get("points", 0) for p in details.get("projects", [])) rows.append( { "ID": u["id"], "Имя": u["name"], "Команда": u.get("team", ""), "Баллы за тесты": tests_score, "Баллы за проекты": projects_score, "Всего баллов": u["points"], "Достижения": ", ".join(u.get("achievements", [])), } ) df = pd.DataFrame(rows) df.sort_values(by="Всего баллов", ascending=False, inplace=True) output_path = EXPORT_FILE df.to_excel(output_path, index=False, engine="openpyxl") logger.info(f"Экспорт завершён: {output_path}") return f"Полный отчет выгружен в {output_path}. Данные команды и KPI включены." except Exception as e: logger.exception(f"Ошибка экспорта в Excel: {e}") return f"Сбой экспорта: {e}" def search_user(name_fragment: str): """ Поиск участника по фрагменту имени. Args: name_fragment: Фрагмент имени для поиска Returns: Форматированный текст с результатами поиска """ try: if not name_fragment: logger.warning("Пустой запрос поиска пользователя") return "[ОШИБКА] Укажите фрагмент имени для поиска." logger.info(f"Поиск пользователя по имени: '{name_fragment}'") users = storage.get_all_users() fragment = name_fragment.lower() matches = [u for u in users if fragment in _get_field(u, "name", "").lower()] if not matches: logger.warning(f"Поиск '{name_fragment}' не дал результатов") return f"Участник с именем '{name_fragment}' не найден." result = f"=== НАЙДЕНО: {len(matches)} ===\n" for u in matches: name = _get_field(u, "name") uid = _get_field(u, "id") points = _get_field(u, "points") result += f"- {name} (ID: {uid}, {points} очк.)\n" return result except Exception as e: logger.exception(f"Ошибка в search_user: {e}") return f"Ошибка поиска: {e}" def get_user_rank_func(user_id): """ Возвращает место пользователя в общем рейтинге. Args: user_id: ID пользователя Returns: Форматированный текст с позицией в рейтинге """ try: user_id = int(user_id) logger.info(f"Запрошен ранг пользователя ID={user_id}") user = storage.get_user_by_id(user_id) if not user: logger.warning(f"Пользователь ID={user_id} не найден для ранга") return "Объект не найден." rank = storage.get_user_rank(user_id) if rank is None: return "Не удалось определить позицию." logger.info(f"Пользователь ID={user_id} на позиции #{rank}") all_users = storage.get_all_users() name = _get_field(user, "name") points = _get_field(user, "points") return ( f"=== ПОЗИЦИЯ В РЕЙТИНГЕ ===\n" f"{name} — #{rank} место ({points} очк.)\n" f"Всего участников: {len(all_users)}" ) except ValueError: logger.error("Некорректный user_id в get_user_rank") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в get_user_rank: {e}") return f"Ошибка определения ранга: {e}" def get_progress_summary(): """ Общая сводка по программе. Returns: Форматированный текст со статистикой """ try: logger.info("Запрошена сводка по программе") stats = storage.get_statistics() if stats["total_users"] == 0: return "База данных пуста." top_scorer = stats.get("top_scorer", {}) return ( f"=== СТАТУС ПРОГРАММЫ ===\n" f"Участников: {stats['total_users']}\n" f"Команд: {stats['teams_count']}\n" f"Всего баллов: {stats['total_points']}\n" f"Средний рейтинг: {stats['average_points']:.0f}\n" f"Лидер: {top_scorer.get('name', 'Неизвестно')} ({top_scorer.get('points', 0)} очк.)" ) except Exception as e: logger.exception(f"Ошибка в get_progress_summary: {e}") return f"Ошибка получения сводки: {e}" # =========================================================== # НОВЫЕ ФУНКЦИИ (добавлены в рамках рефакторинга) # =========================================================== def compare_with_leader(user_id): """ Сравнение пользователя с лидером рейтинга. Args: user_id: ID пользователя для сравнения Returns: Форматированный текст со сравнением """ try: user_id = int(user_id) logger.info(f"Запрошено сравнение с лидером для ID={user_id}") user = storage.get_user_by_id(user_id) if not user: return "Объект не найден." sorted_users = storage.get_users_sorted_by_points() leader = sorted_users[0] user_rank = storage.get_user_rank(user_id) user_points = _get_field(user, "points") leader_points = _get_field(leader, "points") user_name = _get_field(user, "name") leader_name = _get_field(leader, "name") diff = leader_points - user_points motivation = "Цель близка!" if diff < 200 else "Нужно поднажать!" return ( f"=== СРАВНЕНИЕ С ЛИДЕРОМ ===\n" f"Ты: {user_name} — {user_points} очк. (#{user_rank})\n" f"Лидер: {leader_name} — {leader_points} очк.\n" f"Разрыв: {diff} очк.\n" f"{motivation}" ) except ValueError: logger.error("Некорректный user_id в compare_with_leader") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в compare_with_leader: {e}") return f"Ошибка сравнения: {e}" def get_all_achievements_func(): """ Вывод всех уникальных достижений в системе. Returns: Форматированный текст со списком достижений """ try: logger.info("Запрошен список всех достижений") achievements = storage.get_all_achievements() if not achievements: return "Достижения не найдены." result = "=== ВСЕ ДОСТИЖЕНИЯ ХОКИНСА ===\n" for i, ach in enumerate(achievements, 1): result += f"{i}. {ach.name} (получили: {ach.count})\n" return result except Exception as e: logger.exception(f"Ошибка в get_all_achievements: {e}") return f"Ошибка: {e}" def get_team_progress(team_name): """ Детальный прогресс команды по категориям. Args: team_name: Название команды Returns: Форматированный текст с прогрессом команды """ try: logger.info(f"Запрошен прогресс команды: {team_name}") stats = storage.get_team_stats(team_name) if not stats.get("exists"): return f"Команда '{team_name}' не найдена." members = stats.get("members", []) members_text = "\n".join( f" • {_get_field(m, 'name')}: {_get_field(m, 'points')} очк." for m in members ) return ( f"=== ПРОГРЕСС: {stats['name']} ===\n" f"Участников: {stats['members_count']}\n" f"Сумма баллов: {stats['total_points']}\n" f"Средний рейтинг: {stats['average_points']:.0f}\n" f"--------------------------------\n" f"[ТЕСТЫ]: {stats['tests_score']} очк.\n" f"[ПРОЕКТЫ]: {stats['projects_score']} очк.\n" f"[СОБЫТИЯ]: {stats['events_count']} посещений\n" f"--------------------------------\n" f"Участники:\n{members_text}" ) except Exception as e: logger.exception(f"Ошибка в get_team_progress: {e}") return f"Ошибка: {e}" # =========================================================== # НОВЫЕ ИНСТРУМЕНТЫ АДМИНИСТРАТОРА (Admin AI Agent Tools) # =========================================================== def get_worst_test_takers_func(limit=5): """ Анализ отстающих: пользователи с наименьшими баллами за тесты. Инструмент для администратора. Args: limit: Количество записей для возврата Returns: Форматированный текст со списком отстающих """ try: limit = int(limit) if limit <= 0: return "[ОШИБКА] Limit должен быть положительным числом." logger.info(f"Запрошен анализ отстающих (top-{limit})") worst_list = storage.get_worst_test_takers(limit) if not worst_list: return "Данные об отстающих отсутствуют." result = "=== АНАЛИЗ ОТСТАЮЩИХ (ТЕСТЫ) ===\n" for i, u in enumerate(worst_list, 1): result += f"{i}. {u['name']} [{u['team']}] — {u['test_score']} очк. за тесты (пройдено: {u['tests_count']})\n" result += "\n[АДМИН] Рекомендуется назначить менторов или дополнительные материалы." return result except ValueError: logger.error("Некорректный аргумент limit для get_worst_test_takers") return "[ОШИБКА] Limit должен быть числом." except Exception as e: logger.exception(f"Ошибка в get_worst_test_takers: {e}") return f"Ошибка анализа: {e}" def get_non_attendees_func(): """ Анализ посещаемости: пользователи, не посещавшие мероприятия. Инструмент для администратора. Returns: Форматированный текст со списком не посещавших """ try: logger.info("Запрошен анализ посещаемости") non_attendees_list = storage.get_non_attendees() if not non_attendees_list: return "Все участники посещают мероприятия. Отличный результат!" result = "=== АНАЛИЗ ПОСЕЩАЕМОСТИ (НЕ БЫЛИ) ===\n" for i, u in enumerate(non_attendees_list, 1): result += f"{i}. {u['name']} [{u['team']}] — не был ни на одном мероприятии\n" result += "\n[АДМИН] Рекомендуется связаться с участниками для уточнения причин." return result except Exception as e: logger.exception(f"Ошибка в get_non_attendees: {e}") return f"Ошибка анализа посещаемости: {e}" def mark_attendance_func(user_id, event_name, attended=True): """ Отметка посещаемости мероприятия. Инструмент для администратора. Args: user_id: ID пользователя event_name: Название мероприятия attended: Статус (True = был, False = не был) Returns: Сообщение об успехе или ошибке """ try: user_id = int(user_id) logger.info(f"Отметка посещаемости: user_id={user_id}, event={event_name}, attended={attended}") result = storage.mark_attendance(user_id, event_name, attended) return result[1] except ValueError: logger.error("Некорректный user_id в mark_attendance") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в mark_attendance: {e}") return f"Ошибка: {e}" def update_project_stage_func(user_id, project_name, stage): """ Обновление стадии проекта. Инструмент для администратора. Доступные стадии: - "анализ и синтез" - "генерация идей" - "прототипирование" - "тестирование" - "сторителлинг" Args: user_id: ID пользователя project_name: Название проекта stage: Новая стадия Returns: Сообщение об успехе или ошибке """ try: user_id = int(user_id) logger.info(f"Обновление стадии проекта: user_id={user_id}, project={project_name}, stage={stage}") result = storage.update_project_stage(user_id, project_name, stage) return result[1] except ValueError: logger.error("Некорректный user_id в update_project_stage") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в update_project_stage: {e}") return f"Ошибка: {e}" def get_pending_achievements_func(): """ Получение списка достижений, ожидающих проверки администратора. Returns: Форматированный текст со списком заявок """ try: logger.info("Запрошен список достижений на проверке") pending_list = storage.get_pending_achievements() if not pending_list: return "Нет достижений на проверке. Все заявки обработаны!" result = "=== ДОСТИЖЕНИЯ НА ПРОВЕРКЕ ===\n" for i, p in enumerate(pending_list, 1): result += ( f"{i}. {p['user_name']} [{p['team']}]\n" f" Достижение: {p['achievement_name']}\n" f" Описание: {p['description']}\n" f" Подано: {p['submitted_at']}\n\n" ) result += "\n[АДМИН] Используйте approve_pending_achievement или reject_pending_achievement для обработки." return result except Exception as e: logger.exception(f"Ошибка в get_pending_achievements: {e}") return f"Ошибка: {e}" def approve_pending_achievement_func(user_id, achievement_name): """ Одобрение достижения администратором. Args: user_id: ID пользователя achievement_name: Название достижения Returns: Сообщение об успехе или ошибке """ try: user_id = int(user_id) logger.info(f"Одобрение достижения: user_id={user_id}, achievement={achievement_name}") result = storage.approve_pending_achievement(user_id, achievement_name) return result[1] except ValueError: logger.error("Некорректный user_id в approve_pending_achievement") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в approve_pending_achievement: {e}") return f"Ошибка: {e}" def reject_pending_achievement_func(user_id, achievement_name, reason=""): """ Отклонение достижения администратором. Args: user_id: ID пользователя achievement_name: Название достижения reason: Причина отклонения Returns: Сообщение об успехе или ошибке """ try: user_id = int(user_id) logger.info(f"Отклонение достижения: user_id={user_id}, achievement={achievement_name}, reason={reason}") result = storage.reject_pending_achievement(user_id, achievement_name, reason) return result[1] except ValueError: logger.error("Некорректный user_id в reject_pending_achievement") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в reject_pending_achievement: {e}") return f"Ошибка: {e}" def add_pending_achievement_func(user_id, achievement_name, description): """ Добавление достижения на проверку администратору. Инструмент для участника. Args: user_id: ID пользователя achievement_name: Название достижения description: Описание (обоснование) Returns: Сообщение об успехе или ошибке """ try: user_id = int(user_id) logger.info(f"Добавление заявки на достижение: user_id={user_id}, achievement={achievement_name}") result = storage.add_pending_achievement(user_id, achievement_name, description) return result[1] except ValueError: logger.error("Некорректный user_id в add_pending_achievement") return "[ОШИБКА] user_id должен быть числом." except Exception as e: logger.exception(f"Ошибка в add_pending_achievement: {e}") return f"Ошибка: {e}" def get_team_leads_func(team_name=None): """ Получение списка тимлидов. Args: team_name: Название команды (None = все тимлиды) Returns: Форматированный текст со списком тимлидов """ try: logger.info(f"Запрошен список тимлидов: {team_name if team_name else 'все'}") leads = storage.get_team_leads(team_name) if not leads: return f"Тимлиды не найдены" + (f" в команде '{team_name}'" if team_name else "") result = f"=== ТИМЛИДЫ{f' КОМАНДЫ {team_name.upper()}' if team_name else ''} ===\n" for i, lead in enumerate(leads, 1): result += f"{i}. {lead['name']} — {lead['team']} ({lead['points']} очк.)\n" return result except Exception as e: logger.exception(f"Ошибка в get_team_leads: {e}") return f"Ошибка: {e}" def get_users_by_stage_func(stage): """ Получение пользователей по стадии проекта. Доступные стадии: - "анализ и синтез" - "генерация идей" - "прототипирование" - "тестирование" - "сторителлинг" Args: stage: Название стадии Returns: Форматированный текст со списком пользователей """ try: logger.info(f"Запрошены пользователи на стадии: {stage}") users = storage.get_users_by_stage(stage) if not users: return f"Нет пользователей на стадии '{stage}'" result = f"=== ПРОЕКТЫ НА СТАДИИ: {stage.upper()} ===\n" for i, u in enumerate(users, 1): result += ( f"{i}. {u['name']} [{u['team']}]\n" f" Проект: {u['project_name']}\n" f" Статус: {u['project_status']}\n" f" Баллы: {u['points']}\n\n" ) return result except Exception as e: logger.exception(f"Ошибка в get_users_by_stage: {e}") return f"Ошибка: {e}" # =========================================================== # ИНСТРУМЕНТЫ АДМИНИСТРАТОРА: МЕРОПРИЯТИЯ # =========================================================== def get_all_events_func(): """Получение списка всех мероприятий.""" try: logger.info("Запрошен список всех мероприятий") events = storage.get_all_events() if not events: return "Нет мероприятий в системе" result = "=== ВСЕ МЕРОПРИЯТИЯ ===\n" for i, e in enumerate(events, 1): result += f"{i}. {e['name']} ({e['date']})\n" return result except Exception as e: logger.exception(f"Ошибка в get_all_events: {e}") return f"Ошибка: {e}" def create_event_func(name, date, description=""): """Создание мероприятия.""" try: logger.info(f"Создание мероприятия: {name}") result = storage.create_event(name, date, description) return result[1] except Exception as e: logger.exception(f"Ошибка в create_event: {e}") return f"Ошибка: {e}" def update_event_func(event_name, new_name="", new_date="", new_description=""): """Обновление мероприятия.""" try: logger.info(f"Обновление мероприятия: {event_name}") result = storage.update_event( event_name, new_name if new_name else None, new_date if new_date else None, new_description if new_description else None, ) return result[1] except Exception as e: logger.exception(f"Ошибка в update_event: {e}") return f"Ошибка: {e}" def delete_event_func(event_name): """Удаление мероприятия.""" try: logger.info(f"Удаление мероприятия: {event_name}") result = storage.delete_event(event_name) return result[1] except Exception as e: logger.exception(f"Ошибка в delete_event: {e}") return f"Ошибка: {e}" # =========================================================== # ИНСТРУМЕНТЫ АДМИНИСТРАТОРА: БАЛЛЫ # =========================================================== def add_points_func(user_id, points, reason=""): """Начисление баллов пользователю.""" try: user_id = int(user_id) points = int(points) logger.info(f"Начисление {points} баллов пользователю {user_id}") result = storage.add_points_to_user(user_id, points, reason) return result[1] except ValueError: logger.error("Некорректные аргументы в add_points") return "[ОШИБКА] user_id и points должны быть числами" except Exception as e: logger.exception(f"Ошибка в add_points: {e}") return f"Ошибка: {e}" def deduct_points_func(user_id, points, reason=""): """Списание баллов у пользователя.""" try: user_id = int(user_id) points = int(points) logger.info(f"Списание {points} баллов у пользователя {user_id}") result = storage.deduct_points_from_user(user_id, points, reason) return result[1] except ValueError: logger.error("Некорректные аргументы в deduct_points") return "[ОШИБКА] user_id и points должны быть числами" except Exception as e: logger.exception(f"Ошибка в deduct_points: {e}") return f"Ошибка: {e}" # =========================================================== # ИНСТРУМЕНТЫ: БАЗА ЗНАНИЙ # =========================================================== def get_knowledge_items_func(category=""): """Получение материалов из базы знаний.""" try: logger.info(f"Запрошены материалы из базы знаний") items = storage.get_all_knowledge_items(category if category else None) if not items: return "База знаний пуста" result = "=== БАЗА ЗНАНИЙ ===\n\n" for i, item in enumerate(items, 1): result += f"{i}. {item['title']}\n" result += f" Категория: {item.get('category', '—')}\n" result += f" ---\n{item['content']}\n\n" return result except Exception as e: logger.exception(f"Ошибка в get_knowledge_items: {e}") return f"Ошибка: {e}" def add_knowledge_item_func(title, content, category=""): """Добавление материала в базу знаний.""" try: logger.info(f"Добавление материала в базу знаний: {title}") result = storage.add_knowledge_item(title, content, category) return result[1] except Exception as e: logger.exception(f"Ошибка в add_knowledge_item: {e}") return f"Ошибка: {e}" def delete_knowledge_item_func(item_id): """Удаление материала из базы знаний.""" try: logger.info(f"Удаление материала из базы знаний: ID={item_id}") result = storage.delete_knowledge_item(item_id) return result[1] except Exception as e: logger.exception(f"Ошибка в delete_knowledge_item: {e}") return f"Ошибка: {e}" # =========================================================== # РЕЕСТР ИНСТРУМЕНТОВ # =========================================================== AVAILABLE_TOOLS = { # Базовые инструменты (доступны всем) "get_top_n": get_top_n, "get_team_ranking": get_team_ranking, "get_team_members": get_team_members_func, "get_user_details": get_user_details, "get_user_status": get_user_status, "export_ranking_to_excel": export_ranking_to_excel, "search_user": search_user, "get_user_rank": get_user_rank_func, "get_progress_summary": get_progress_summary, "compare_with_leader": compare_with_leader, "get_all_achievements": get_all_achievements_func, "get_team_progress": get_team_progress, # Инструменты администратора "get_worst_test_takers": get_worst_test_takers_func, "get_non_attendees": get_non_attendees_func, "mark_attendance": mark_attendance_func, "update_project_stage": update_project_stage_func, "get_pending_achievements": get_pending_achievements_func, "approve_pending_achievement": approve_pending_achievement_func, "reject_pending_achievement": reject_pending_achievement_func, "get_team_leads": get_team_leads_func, "get_users_by_stage": get_users_by_stage_func, # Инструменты администратора: Мероприятия "get_all_events": get_all_events_func, "create_event": create_event_func, "update_event": update_event_func, "delete_event": delete_event_func, # Инструменты администратора: Баллы "add_points": add_points_func, "deduct_points": deduct_points_func, # Инструменты администратора: База знаний "get_knowledge_items": get_knowledge_items_func, "add_knowledge_item": add_knowledge_item_func, "delete_knowledge_item": delete_knowledge_item_func, # Инструменты участника "add_pending_achievement": add_pending_achievement_func, }