intrabot/intradesk/reference.py
2026-03-15 20:34:12 +03:00

206 lines
8.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Справочники 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.")