""" Справочники Intradesk. Содержит: 1. Статические константы (известные ID → имена) 2. Загрузку справочников из API Intradesk в локальную БД """ import logging import requests import config import database log = logging.getLogger(__name__) # ── Статические справочники ──────────────────────────────────────────────────── # Используются как fallback если данные ещё не загружены из API, # а также сидируются в БД при старте. STATUSES: dict[int, str] = { 68046: "Открыта", 68048: "Требует уточнения", 68051: "В работе", 68045: "Выполнена", } PRIORITIES: dict[int, str] = { 29749: "Наивысший", 29598: "Критический", 29597: "Высокий", 29596: "Низкий", } USER_TYPES: dict[int, str] = { 10: "Клиент", 20: "Сотрудник", } # Все типы событий из lifecycle заявки EVENT_TYPES: dict[int, str] = { 10: "Смена статуса", 20: "Смена приоритета", 25: "Смена оценки", 30: "Смена исполнителя", 40: "Смена группы исполнителей", 45: "Изменение доп. данных", 50: "Комментарий", 55: "Приватный комментарий", 60: "Добавлен наблюдатель", 61: "Добавлен координатор", 65: "Добавлена группа наблюдателей", 70: "Удалён наблюдатель", 75: "Удалена группа наблюдателей", 80: "Добавлен файл", 85: "Добавлен скрытый файл", 90: "Удалён файл", 95: "Удалён скрытый файл", 100: "Добавлен тег", 105: "Добавлен актив", 110: "Удалён тег", 115: "Удалён актив", 120: "Изменён плановый срок решения", 125: "Изменён фактический срок решения", 126: "Изменено оставшееся время", 130: "Изменён плановый срок реакции", 135: "Изменён фактический срок реакции", 136: "Истёк срок решения", 137: "Истёк срок реакции", 138: "Изменена дата закрытия", 140: "Изменён сервис", 150: "Изменён тип заявки", 160: "Изменён инициатор", 161: "Изменён клиент", 165: "Изменена группа инициатора", 170: "Изменено доп. поле", 180: "Изменена дата создания", 185: "Изменена дата обновления", 190: "Изменён создатель", 195: "Изменён последний редактор", 200: "Изменено название", 205: "Изменено описание", 210: "Изменён бизнес-процесс", 220: "Добавлен не ответственный исполнитель", 225: "Добавлена не ответственная группа исполнителей", 230: "Удалён не ответственный исполнитель", 235: "Удалена не ответственная группа исполнителей", 240: "Изменён SLA", 250: "Изменена история", 260: "Изменён канал создания", 270: "Изменён канал обновления", 280: "Архивировано", 290: "Добавлены координаты", 300: "Выполнен макрос", 310: "Изменён признак массового инцидента", 320: "Изменён массовый инцидент", 330: "Изменена координация", 340: "Изменена критичность приоритета", 350: "Изменено влияние приоритета", 400: "Эскалация заявки", } # ── API-справочники (загружаются из Intradesk) ───────────────────────────────── _TASKS_URL = "https://apigw.intradesk.ru/tasklist/odata/v3/tasks" # Маппинг поля "type" из dictionaries → ключ entity в БД _DICT_TYPE_TO_ENTITY: dict[str, str] = { "user": "employees", "usergroup": "groups", "status": "statuses", "priority": "priorities", "tasktype": "tasktypes", "tags": "tags", "workflow": "workflows", "asset": "assets", } # ── вспомогательные ──────────────────────────────────────────────────────────── def resolve(entity: str, item_id: int | None) -> str | None: """ Резолвит ID в имя. Сначала смотрит в БД (актуальные данные из API), затем в статические словари-fallback. """ if item_id is None: return None name = database.resolve(entity, item_id) if name: return name _fallbacks = { "statuses": STATUSES, "priorities": PRIORITIES, } fb = _fallbacks.get(entity, {}) return fb.get(item_id) def event_name(event_type: int) -> str: """Возвращает человекочитаемое имя события по его числовому коду.""" return EVENT_TYPES.get(event_type, f"Событие {event_type}") def user_type_name(user_type: int) -> str: """Возвращает тип пользователя (Клиент / Сотрудник).""" return USER_TYPES.get(user_type, str(user_type)) # ── загрузка из API ──────────────────────────────────────────────────────────── def seed_static(): """Сидирует в БД известные статические данные (статусы, приоритеты).""" database.save_ref_data("statuses", list(STATUSES.items())) database.save_ref_data("priorities", list(PRIORITIES.items())) log.info("Static reference data seeded (statuses, priorities)") def load_all(): """ Загружает справочники из поля dictionaries ответа tasklist API. Один запрос заменяет 7+ отдельных запросов к settings/odata. Обновление накопительное: новые/обновлённые записи добавляются, старые не удаляются (они могут встретиться в старых заявках). """ log.info("Loading reference data from tasklist dictionaries...") try: resp = requests.get( _TASKS_URL, params={"ApiKey": config.API_KEY, "$top": 1}, timeout=30, ) resp.raise_for_status() data = resp.json() except requests.exceptions.RequestException as e: log.error(f"Failed to fetch tasklist for dictionaries: {e}") return dictionaries = data.get("dictionaries", []) if not dictionaries: log.warning("dictionaries field is empty or missing in tasklist response") return # Группируем по entity buckets: dict[str, list[tuple[int, str]]] = {} for item in dictionaries: dtype = item.get("type", "") entity = _DICT_TYPE_TO_ENTITY.get(dtype) if not entity: continue item_id = item.get("id") name = str(item.get("name") or "").strip() if item_id and name: buckets.setdefault(entity, []).append((item_id, name)) for entity, pairs in buckets.items(): database.save_ref_data(entity, pairs) log.info(f"[{entity}] updated {len(pairs)} records from dictionaries") log.info("Reference data loading complete.")