- Загрузка/сканирование видео, извлечение кадров, анализ через SDK ФПГ - Понятный диагноз с сохранением в SQLite и share-ссылками - Экспорт PDF/DOCX, скачивание записанного видео - Камерное сканирование на мобильных (canvas + MediaRecorder) - Секреты вынесены в переменные окружения
196 lines
6.7 KiB
Python
196 lines
6.7 KiB
Python
"""
|
|
Тестовый скрипт для SDK ФПГ (GigaDoc).
|
|
Покрывает: авторизацию → создание сессии → WebSocket (изображения) → удаление сессии.
|
|
|
|
Использование:
|
|
python3 test_fpg.py [--ws-host HOST] [--ws-port PORT] [--image-dir DIR]
|
|
"""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import glob
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
import requests
|
|
|
|
# ─── Конфигурация ─────────────────────────────────────────────
|
|
OAUTH_URL = "https://oauth.sbermed.ai/token"
|
|
CLIENT_ID = "client_fpg_front"
|
|
CLIENT_SECRET = os.environ.get("FPG_CLIENT_SECRET", "")
|
|
|
|
REST_HOST = "gigadoc-sdk.sbermed.ai"
|
|
REST_BASE = f"https://{REST_HOST}"
|
|
|
|
DEVICE_ID = "web_prod"
|
|
FORM_FACTOR = "web_prod"
|
|
|
|
|
|
def get_token() -> str:
|
|
"""Получить access token через client_credentials."""
|
|
resp = requests.post(
|
|
OAUTH_URL,
|
|
data={
|
|
"grant_type": "client_credentials",
|
|
"client_id": CLIENT_ID,
|
|
"client_secret": CLIENT_SECRET,
|
|
},
|
|
timeout=10,
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
print(f"[AUTH] Токен получен (действует {data['expires_in']} сек)")
|
|
return data["access_token"]
|
|
|
|
|
|
def create_session(token: str) -> str:
|
|
"""Создать сессию и вернуть uid."""
|
|
resp = requests.post(
|
|
f"{REST_BASE}/api/v1/predict/new",
|
|
params={"device_id": DEVICE_ID, "form_factor": FORM_FACTOR},
|
|
headers={
|
|
"Authorization": f"Bearer {token}",
|
|
"Accept": "application/json",
|
|
},
|
|
timeout=10,
|
|
)
|
|
resp.raise_for_status()
|
|
uid = resp.json()["uid"]
|
|
print(f"[SESSION] Сессия создана: {uid}")
|
|
return uid
|
|
|
|
|
|
def delete_session(token: str, uid: str):
|
|
"""Удалить сессию."""
|
|
resp = requests.delete(
|
|
f"{REST_BASE}/api/v1/predict/{uid}",
|
|
headers={"Authorization": f"Bearer {token}"},
|
|
timeout=10,
|
|
)
|
|
print(f"[SESSION] Сессия {uid} удалена ({resp.status_code})")
|
|
|
|
|
|
async def ws_scan(token: str, uid: str, ws_host: str, ws_port: int, image_dir: str):
|
|
"""Подключиться по WebSocket, отправить изображения, получить результат."""
|
|
import websockets
|
|
|
|
uri = f"wss://{ws_host}:{ws_port}/api/v2/ws/predict/{uid}/image"
|
|
print(f"[WS] Подключение: {uri}")
|
|
|
|
async with websockets.connect(
|
|
uri,
|
|
open_timeout=10,
|
|
additional_headers={"Authorization": f"Bearer {token}"},
|
|
) as ws:
|
|
print("[WS] Соединение установлено")
|
|
|
|
# Собираем изображения
|
|
patterns = ["*.jpg", "*.jpeg", "*.png"]
|
|
images = []
|
|
for p in patterns:
|
|
images.extend(glob.glob(os.path.join(image_dir, p)))
|
|
images.sort()
|
|
|
|
if not images:
|
|
print(f"[WS] Нет изображений в {image_dir}, отправляю тестовое изображение")
|
|
images = ["/tmp/test_face.jpg"]
|
|
|
|
print(f"[WS] Отправляю {len(images)} изображений...")
|
|
|
|
for i, img_path in enumerate(images):
|
|
with open(img_path, "rb") as f:
|
|
data = f.read()
|
|
await ws.send(data)
|
|
|
|
try:
|
|
resp = await asyncio.wait_for(ws.recv(), timeout=5)
|
|
result = json.loads(resp)
|
|
progress = result.get("progress", "?")
|
|
|
|
# Краткий вывод прогресса
|
|
if i % 30 == 0 or progress == 1.0:
|
|
print(
|
|
f" [{i+1}/{len(images)}] progress={progress} "
|
|
f"face_ok={result.get('face_ok')}"
|
|
)
|
|
|
|
# Финальный результат
|
|
if progress == 1.0 or (isinstance(progress, (int, float)) and progress >= 1.0):
|
|
print("\n" + "=" * 60)
|
|
print("📊 ФИНАЛЬНЫЙ РЕЗУЛЬТАТ ДИАГНОСТИКИ:")
|
|
print("=" * 60)
|
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
return result
|
|
|
|
except asyncio.TimeoutError:
|
|
if i % 50 == 0:
|
|
print(f" [{i+1}/{len(images)}] ожидание ответа...")
|
|
|
|
print("[WS] Соединение закрыто без финального результата")
|
|
return None
|
|
|
|
|
|
def create_test_image():
|
|
"""Создать тестовое JPEG-изображение если нет своих."""
|
|
try:
|
|
from PIL import Image
|
|
|
|
img = Image.new("RGB", (1280, 720), color=(128, 128, 128))
|
|
img.save("/tmp/test_face.jpg", "JPEG", quality=80)
|
|
print("[INIT] Создано тестовое изображение /tmp/test_face.jpg")
|
|
except ImportError:
|
|
print("[INIT] PIL не установлен, тестовое изображение не создано")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Тест SDK ФПГ")
|
|
parser.add_argument("--ws-host", default="sdk.sbermed.ai", help="WebSocket host")
|
|
parser.add_argument("--ws-port", type=int, default=8443, help="WebSocket port")
|
|
parser.add_argument("--image-dir", default=".", help="Директория с JPEG-файлами")
|
|
parser.add_argument("--rest-only", action="store_true", help="Только REST API (без WS)")
|
|
args = parser.parse_args()
|
|
|
|
print("=" * 60)
|
|
print("🏥 Тестирование SDK ФПГ (GigaDoc)")
|
|
print("=" * 60)
|
|
|
|
# 0. Тестовое изображение
|
|
create_test_image()
|
|
|
|
# 1. Авторизация
|
|
token = get_token()
|
|
|
|
# 2. Создание сессии
|
|
uid = create_session(token)
|
|
|
|
# 3. WebSocket
|
|
if not args.rest_only:
|
|
try:
|
|
result = asyncio.run(
|
|
ws_scan(token, uid, args.ws_host, args.ws_port, args.image_dir)
|
|
)
|
|
except Exception as e:
|
|
print(f"\n[WS] Ошибка: {type(e).__name__}: {e}")
|
|
print("[WS] Проверьте доступность WebSocket хоста")
|
|
result = None
|
|
else:
|
|
result = None
|
|
print("[SKIP] WebSocket тест пропущен (--rest-only)")
|
|
|
|
# 4. Удаление сессии
|
|
delete_session(token, uid)
|
|
|
|
# Итог
|
|
print("\n" + "=" * 60)
|
|
if result:
|
|
print("✅ Тест завершён успешно — результат получен")
|
|
else:
|
|
print("⚠️ REST API работает, WebSocket недоступен")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|