refactor: use requests sync, graceful config import
This commit is contained in:
+60
-51
@@ -1,18 +1,28 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Получение списка заявок из ServiceDesk Plus с 20 июля 2026. Экспорт: Группа, Описание, Назначено, Категория в JSONL."""
|
"""Получение списка заявок из ServiceDesk Plus. Экспорт: Группа, Описание, Назначено, Категория в JSONL."""
|
||||||
|
|
||||||
import asyncio
|
import requests
|
||||||
import httpx
|
|
||||||
import json
|
import json
|
||||||
import urllib3
|
import urllib3
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime
|
||||||
from html import unescape
|
from html import unescape
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Добавляем путь к конфигу
|
# Отключаем ворнинги для чистой работы консоли
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# --- ИМПОРТ КОНФИГУРАЦИИ ---
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config'))
|
||||||
|
|
||||||
|
# Пытаемся импортировать конфиг, но не падаем если Passwork недоступен
|
||||||
|
try:
|
||||||
|
from config import *
|
||||||
|
config_loaded = True
|
||||||
|
except Exception as e:
|
||||||
|
config_loaded = False
|
||||||
|
print(f"⚠️ Конфиг не загружен: {e}")
|
||||||
|
|
||||||
# Локальный Service Desk (для выгрузки данных)
|
# Локальный Service Desk (для выгрузки данных)
|
||||||
LOCAL_SD_URL = "https://192.168.1.237:8080"
|
LOCAL_SD_URL = "https://192.168.1.237:8080"
|
||||||
@@ -43,67 +53,66 @@ def get_name(obj):
|
|||||||
return obj.get("name", "") or obj.get("display_value", "")
|
return obj.get("name", "") or obj.get("display_value", "")
|
||||||
return str(obj)
|
return str(obj)
|
||||||
|
|
||||||
async def fetch_ticket_detail(client, req_id):
|
def get_ticket_detail(ticket_id):
|
||||||
|
"""Получить детальную информацию по заявке."""
|
||||||
try:
|
try:
|
||||||
resp = await client.get(
|
url = f"{LOCAL_SD_URL}/api/v3/requests/{ticket_id}"
|
||||||
f"{LOCAL_SD_URL}/api/v3/requests/{req_id}",
|
headers = {"authtoken": LOCAL_SD_API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
||||||
headers={"authtoken": LOCAL_SD_API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"},
|
resp = requests.get(url, headers=headers, verify=False, timeout=15.0)
|
||||||
timeout=15.0
|
resp.raise_for_status()
|
||||||
)
|
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
return data.get("request", data)
|
return data.get("request", data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
async def list_tickets(from_date=DEFAULT_FROM_DATE):
|
def list_tickets(from_date=DEFAULT_FROM_DATE):
|
||||||
url = f"{LOCAL_SD_URL}/api/v3/requests"
|
url = f"{LOCAL_SD_URL}/api/v3/requests"
|
||||||
headers = {"authtoken": LOCAL_SD_API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
headers = {"authtoken": LOCAL_SD_API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
||||||
|
|
||||||
# Вычисляем timestamp с указанной даты
|
# Вычисляем timestamp с указанной даты
|
||||||
date_obj = datetime.strptime(from_date, "%Y-%m-%d")
|
date_obj = datetime.strptime(from_date, "%Y-%m-%d")
|
||||||
from_date_ms = int(date_obj.replace(tzinfo=timezone.utc).timestamp() * 1000)
|
from_date_ms = int(date_obj.replace(tzinfo=__import__('datetime').timezone.utc).timestamp() * 1000)
|
||||||
|
|
||||||
async with httpx.AsyncClient(verify=False, timeout=30.0) as client:
|
resp = requests.get(url, headers=headers, verify=False, timeout=30.0)
|
||||||
resp = await client.get(url, headers=headers)
|
resp.raise_for_status()
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
all_reqs = data.get("requests", [])
|
all_reqs = data.get("requests", [])
|
||||||
|
|
||||||
|
from_date_reqs = [r for r in all_reqs if get_ts(r) >= from_date_ms]
|
||||||
|
|
||||||
|
# Получаем детали для каждой заявки
|
||||||
|
rows = []
|
||||||
|
for req in from_date_reqs:
|
||||||
|
req_id = req.get("request_id", req.get("id", "?"))
|
||||||
|
detail = get_ticket_detail(req_id)
|
||||||
|
|
||||||
from_date_reqs = [r for r in all_reqs if get_ts(r) >= from_date_ms]
|
# Группа — из списка
|
||||||
|
group = req.get("group", {})
|
||||||
|
group_name = get_name(group)
|
||||||
|
|
||||||
# Получаем детали для каждой заявки
|
# Описание — из детального запроса
|
||||||
tasks = [fetch_ticket_detail(client, r.get("request_id", r.get("id", "?"))) for r in from_date_reqs]
|
desc = detail.get("description", "")
|
||||||
details = await asyncio.gather(*tasks)
|
desc_clean = clean_html(desc)
|
||||||
|
|
||||||
rows = []
|
# Назначено (специалист) — из детального запроса
|
||||||
for req, detail in zip(from_date_reqs, details):
|
tech = detail.get("technician", {})
|
||||||
# Группа — из списка
|
assignee = get_name(tech)
|
||||||
group = req.get("group", {})
|
|
||||||
group_name = get_name(group)
|
|
||||||
|
|
||||||
# Описание — из детального запроса
|
|
||||||
desc = detail.get("description", "")
|
|
||||||
desc_clean = clean_html(desc)
|
|
||||||
|
|
||||||
# Назначено (специалист) — из детального запроса
|
|
||||||
tech = detail.get("technician", {})
|
|
||||||
assignee = get_name(tech)
|
|
||||||
|
|
||||||
# Категория — из детального запроса
|
|
||||||
cat = detail.get("category", {})
|
|
||||||
category = get_name(cat)
|
|
||||||
|
|
||||||
rows.append({
|
|
||||||
"group": group_name,
|
|
||||||
"description": desc_clean,
|
|
||||||
"assignee": assignee,
|
|
||||||
"category": category
|
|
||||||
})
|
|
||||||
|
|
||||||
# Выводим JSONL
|
# Категория — из детального запроса
|
||||||
for row in rows:
|
cat = detail.get("category", {})
|
||||||
print(json.dumps(row, ensure_ascii=False))
|
category = get_name(cat)
|
||||||
|
|
||||||
|
rows.append({
|
||||||
|
"group": group_name,
|
||||||
|
"description": desc_clean,
|
||||||
|
"assignee": assignee,
|
||||||
|
"category": category
|
||||||
|
})
|
||||||
|
|
||||||
|
# Выводим JSONL
|
||||||
|
for row in rows:
|
||||||
|
print(json.dumps(row, ensure_ascii=False))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
urllib3.disable_warnings()
|
|
||||||
from_date = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_FROM_DATE
|
from_date = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_FROM_DATE
|
||||||
asyncio.run(list_tickets(from_date))
|
list_tickets(from_date)
|
||||||
|
|||||||
Reference in New Issue
Block a user