feat: add fetch_tickets.py with group, description, assignee, category
This commit is contained in:
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Получение списка заявок из ServiceDesk Plus с 20 июля 2026. Экспорт: Группа, Описание, Назначено, Категория в JSONL."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
import urllib3
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from html import unescape
|
||||||
|
import re
|
||||||
|
|
||||||
|
BASE_URL = "https://192.168.1.237:8080"
|
||||||
|
API_TOKEN = "062CE056-C1F5-490C-A6DF-FD2C653A71DD"
|
||||||
|
|
||||||
|
FROM_DATE_MS = int(datetime(2026, 7, 20, 0, 0, 0, tzinfo=timezone.utc).timestamp() * 1000)
|
||||||
|
|
||||||
|
def get_ts(req):
|
||||||
|
ct = req.get("created_time", {})
|
||||||
|
if isinstance(ct, dict):
|
||||||
|
v = ct.get("value", "0")
|
||||||
|
return int(v) if v else 0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def clean_html(text):
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
text = re.sub(r'<[^>]+>', '', text)
|
||||||
|
text = unescape(text)
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
def get_name(obj):
|
||||||
|
"""Извлечь имя из объекта группы/техника/категории."""
|
||||||
|
if not obj:
|
||||||
|
return ""
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return obj.get("name", "") or obj.get("display_value", "")
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
|
async def fetch_ticket_detail(client, req_id):
|
||||||
|
try:
|
||||||
|
resp = await client.get(
|
||||||
|
f"{BASE_URL}/api/v3/requests/{req_id}",
|
||||||
|
headers={"authtoken": API_TOKEN, "Accept": "application/vnd.manageengine.sdp.v3+json"},
|
||||||
|
timeout=15.0
|
||||||
|
)
|
||||||
|
data = resp.json()
|
||||||
|
return data.get("request", data)
|
||||||
|
except Exception as e:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def list_tickets():
|
||||||
|
url = f"{BASE_URL}/api/v3/requests"
|
||||||
|
headers = {"authtoken": API_TOKEN, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(verify=False, timeout=30.0) as client:
|
||||||
|
resp = await client.get(url, headers=headers)
|
||||||
|
data = resp.json()
|
||||||
|
all_reqs = data.get("requests", [])
|
||||||
|
|
||||||
|
from_20 = [r for r in all_reqs if get_ts(r) >= FROM_DATE_MS]
|
||||||
|
|
||||||
|
# Получаем детали для каждой заявки
|
||||||
|
tasks = [fetch_ticket_detail(client, r.get("request_id", r.get("id", "?"))) for r in from_20]
|
||||||
|
details = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for req, detail in zip(from_20, details):
|
||||||
|
group = req.get("group", {})
|
||||||
|
group_name = get_name(group)
|
||||||
|
|
||||||
|
desc = detail.get("description", "")
|
||||||
|
desc_clean = clean_html(desc)
|
||||||
|
|
||||||
|
# Назначено (специалист)
|
||||||
|
tech = req.get("technician", {})
|
||||||
|
assignee = get_name(tech)
|
||||||
|
|
||||||
|
# Категория
|
||||||
|
cat = req.get("category", {})
|
||||||
|
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__":
|
||||||
|
urllib3.disable_warnings()
|
||||||
|
asyncio.run(list_tickets())
|
||||||
Reference in New Issue
Block a user