Compare commits
32 Commits
3090adcec7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b269e7d6ec | |||
| 23d073f9d8 | |||
| 9c80ff716e | |||
| 00630cd18c | |||
| 748b262936 | |||
| f03cd70b48 | |||
| 16b0b9f063 | |||
| 23a18b7396 | |||
| 08220a6858 | |||
| 6c8c79cf70 | |||
| a1d391cf5f | |||
| b7b099cb38 | |||
| 6494ed1e17 | |||
| 46cbcde05b | |||
| a980bea083 | |||
| f974f678f8 | |||
| 3dff2c2a95 | |||
| 167e79fa2c | |||
| 8aa13afad9 | |||
| 08f6b67971 | |||
| 42335ce015 | |||
| 13f99579f5 | |||
| 3396e3e65b | |||
| a48b484ea1 | |||
| 1ee2b6387a | |||
| d14930aa39 | |||
| 4ad93cfdd5 | |||
| cf1d526304 | |||
| 074379aa22 | |||
| 179f1b1a74 | |||
| 33a480a0cd | |||
| d70b83543a |
Binary file not shown.
+5
-4
@@ -6,7 +6,7 @@ from dotenv import load_dotenv
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Загружаем приватные переменные окружения из вашего .env
|
||||
load_dotenv('/opt/GigaAM/config/.env')
|
||||
load_dotenv(os.path.join(BASE_DIR, 'config', '.env'))
|
||||
|
||||
# Карта соответствия: Переменная диспетчера -> Ключ из вашего .env
|
||||
# Объявлена в самом начале, чтобы скрипт знал имена карточек до обращения к API
|
||||
@@ -74,7 +74,7 @@ FTP_ARCHIVE = "/zabbix/archive"
|
||||
WEBHOOK_PORT = 5000
|
||||
|
||||
# Путь к файлу сквозного отладочного лога диспетчера
|
||||
WEBHOOK_LOG_FILE = "/opt/GigaAM/dispatcher_log.txt"
|
||||
WEBHOOK_LOG_FILE = os.path.join(BASE_DIR, 'dispatcher_log.txt')
|
||||
|
||||
# Включение профилирования (замеры времени выполнения этапов обработки заявки)
|
||||
ENABLE_PROFILING = True
|
||||
@@ -93,8 +93,8 @@ CLI_LOG_MAX_CHARS = 80
|
||||
ENABLE_TEXT_SUMMARIZATION = False
|
||||
|
||||
# Рабочие директории для хранения входящих аудиозаписей и результатов транскрибации
|
||||
AUDIO_DIR = "/opt/GigaAM/audio"
|
||||
JSON_DIR = "/opt/GigaAM/json"
|
||||
AUDIO_DIR = os.path.join(BASE_DIR, 'audio')
|
||||
JSON_DIR = os.path.join(BASE_DIR, 'json')
|
||||
|
||||
# Длина одного чанка (куска аудио) при нарезке для модели распознавания (в секундах)
|
||||
CHUNK_SECONDS = 25
|
||||
@@ -115,6 +115,7 @@ ENABLE_FUZZY_CORRECTION = True # Нечёткое исправление по
|
||||
SERVER_URL = "https://servicedesk.sibcem.ru:8080"
|
||||
ENDPOINT = f"{SERVER_URL}/api/v3/requests"
|
||||
|
||||
|
||||
# Системный email-адрес учетной записи ИИ-сортировщика в Service Desk
|
||||
DEFAULT_REQUESTER = "ai@sibcem.ru"
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Получение списка заявок из ServiceDesk Plus. Экспорт: Группа, Описание, Назначено, Категория в JSONL/JSON/CSV."""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import csv
|
||||
import urllib3
|
||||
from datetime import datetime
|
||||
from html import unescape
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Отключаем ворнинги для чистой работы консоли
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
# --- ПОЛУЧЕНИЕ API_KEY ИЗ PASSWORK ---
|
||||
SERVER_URL = "https://servicedesk.sibcem.ru:8080"
|
||||
API_KEY = ""
|
||||
|
||||
# 1. Пробуем passwork напрямую
|
||||
try:
|
||||
sys.path.insert(0, "/opt/passwork")
|
||||
from passwork import get_passwork_secrets
|
||||
passwork_pool = get_passwork_secrets(required_cards=["sdesktoken"])
|
||||
card = passwork_pool.get("sdesktoken")
|
||||
if card:
|
||||
API_KEY = card.get("password", "").strip()
|
||||
print("LOG: API_KEY получен из passwork (sdesktoken)")
|
||||
else:
|
||||
print("LOG: Карточка sdesktoken не найдена в passwork", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"LOG: Passwork ошибка: {e}", file=sys.stderr)
|
||||
|
||||
# 2. Пробуем config.py
|
||||
if not API_KEY:
|
||||
try:
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
import config as cfg
|
||||
API_KEY = getattr(cfg, 'API_KEY', "")
|
||||
if API_KEY:
|
||||
print("LOG: API_KEY получен из config.py")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. Пробуем env var
|
||||
if not API_KEY:
|
||||
API_KEY = os.environ.get("SD_API_KEY", "")
|
||||
if API_KEY:
|
||||
print("LOG: API_KEY получен из SD_API_KEY")
|
||||
|
||||
if not API_KEY:
|
||||
print("LOG: ОШИБКА: API_KEY не найден нигде! Нужен passwork или SD_API_KEY", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"LOG: Подключение к {SERVER_URL}")
|
||||
|
||||
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)
|
||||
|
||||
def get_ticket_detail(ticket_id):
|
||||
try:
|
||||
url = f"{SERVER_URL}/api/v3/requests/{ticket_id}"
|
||||
headers = {"authtoken": API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
||||
resp = requests.get(url, headers=headers, verify=False, timeout=15.0)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
return data.get("request", data)
|
||||
except Exception as e:
|
||||
return {}
|
||||
|
||||
def list_tickets(from_date="2026-07-20", json_file=None, csv_file=None):
|
||||
url = f"{SERVER_URL}/api/v3/requests"
|
||||
headers = {"authtoken": API_KEY, "Accept": "application/vnd.manageengine.sdp.v3+json"}
|
||||
|
||||
date_obj = datetime.strptime(from_date, "%Y-%m-%d")
|
||||
from_date_ms = int(date_obj.replace(tzinfo=__import__('datetime').timezone.utc).timestamp() * 1000)
|
||||
|
||||
print(f"LOG: Загрузка заявок с {from_date}...")
|
||||
resp = requests.get(url, headers=headers, verify=False, timeout=30.0)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
all_reqs = data.get("requests", [])
|
||||
|
||||
from_date_reqs = [r for r in all_reqs if get_ts(r) >= from_date_ms]
|
||||
print(f"LOG: Найдено заявок с {from_date}: {len(from_date_reqs)}")
|
||||
|
||||
rows = []
|
||||
for i, req in enumerate(from_date_reqs):
|
||||
if (i + 1) % 10 == 0:
|
||||
print(f" Обработано {i + 1}/{len(from_date_reqs)}...")
|
||||
|
||||
req_id = req.get("request_id", req.get("id", "?"))
|
||||
detail = get_ticket_detail(req_id)
|
||||
|
||||
group = req.get("group", {})
|
||||
group_name = get_name(group)
|
||||
|
||||
desc = detail.get("description", "")
|
||||
desc_clean = clean_html(desc).replace('\n', ' ').replace('\r', ' ')
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
# Определяем путь для файлов по умолчанию (та же директория что и скрипт)
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
if json_file is None:
|
||||
json_file = os.path.join(script_dir, f"tickets_{timestamp}.json")
|
||||
if csv_file is None:
|
||||
csv_file = os.path.join(script_dir, f"tickets_{timestamp}.csv")
|
||||
|
||||
# Сохраняем JSON
|
||||
with open(json_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(rows, f, ensure_ascii=False, indent=2)
|
||||
print(f"LOG: JSON сохранён: {json_file}")
|
||||
|
||||
# Сохраняем CSV (utf-8-sig для корректного отображения кириллицы в Excel)
|
||||
with open(csv_file, 'w', encoding='utf-8-sig', newline='') as f:
|
||||
writer = csv.DictWriter(f, fieldnames=["group", "description", "assignee", "category"], quoting=csv.QUOTE_ALL)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
print(f"LOG: CSV сохранён: {csv_file}")
|
||||
|
||||
# Выводим JSONL в консоль
|
||||
for row in rows:
|
||||
print(json.dumps(row, ensure_ascii=False))
|
||||
|
||||
print(f"LOG: Выведено {len(rows)} заявок", file=sys.stderr)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Выгрузка заявок из ServiceDesk Plus")
|
||||
parser.add_argument("--from", dest="from_date", default="2026-07-20", help="Дата начала (YYYY-MM-DD)")
|
||||
parser.add_argument("--json", dest="json_file", default=None, help="Путь к JSON файлу")
|
||||
parser.add_argument("--csv", dest="csv_file", default=None, help="Путь к CSV файлу")
|
||||
args = parser.parse_args()
|
||||
|
||||
list_tickets(from_date=args.from_date, json_file=args.json_file, csv_file=args.csv_file)
|
||||
+1
-1
@@ -25,7 +25,7 @@ from qdrant_client import QdrantClient
|
||||
from ldap3 import Server, Connection, ALL
|
||||
|
||||
# --- ИМПОРТИРУЕМ ВСЕ НАСТРОЙКИ ИЗ ЕДИНОГО КОНФИГА ---
|
||||
sys.path.append('/opt/GigaAM')
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from config.config import *
|
||||
|
||||
# --- ИНИЦИАЛИЗАЦИЯ FLASK ---
|
||||
|
||||
Reference in New Issue
Block a user