Files
sber_med/interpret.py
apuc 04a7b31a75 Веб-приложение диагностики по видео (ФПГ GigaDoc)
- Загрузка/сканирование видео, извлечение кадров, анализ через SDK ФПГ
- Понятный диагноз с сохранением в SQLite и share-ссылками
- Экспорт PDF/DOCX, скачивание записанного видео
- Камерное сканирование на мобильных (canvas + MediaRecorder)
- Секреты вынесены в переменные окружения
2026-09-07 19:01:06 +03:00

367 lines
18 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.
#!/usr/bin/env python3
"""
Модуль интерпретации результатов SDK ФПГ (GigaDoc).
Переводит JSON-ответ в понятный человеку диагноз.
"""
def interpret_result(data: dict) -> dict:
"""
Преобразует JSON-ответ SDK в структурированный, понятный диагноз.
Возвращает dict с секциями:
- summary: краткое резюме
- metrics: список показателей с оценкой (норма/отклонение)
- warnings: список предупреждений
- advice: рекомендации
"""
if not data or not data.get("face_ok"):
return {
"summary": "Лицо не было распознано на видео. Убедитесь, что лицо хорошо освещено и находится в кадре.",
"metrics": [],
"warnings": ["Лицо не распознано — анализ невозможен"],
"advice": ["Переснимите видео: лицо анфас, хорошее освещение, без очков и головных уборов"],
}
metrics = []
warnings = []
advice = []
# ── Демография ─────────────────────────────────────────────
gender = "мужчина" if data.get("is_male") else "женщина"
age = data.get("age", 0)
ethnicity_map = {
"white": "европеоид",
"black": "негроид",
"asian": "монголоид",
"unknown": "не определён",
}
ethnicity = ethnicity_map.get(data.get("ethnicity", ""), data.get("ethnicity", "не определён"))
emotion_map = {
"happiness": "радость",
"sadness": "грусть",
"neutral": "спокойствие",
"anger": "злость",
"surprise": "удивление",
"fear": "страх",
"disgust": "отвращение",
"contempt": "презрение",
}
emotion = emotion_map.get(data.get("emotion", ""), data.get("emotion", "не определена"))
metrics.append({
"name": "Пол",
"value": "Мужской" if data.get("is_male") else "Женский",
"status": "info",
"comment": "",
})
metrics.append({
"name": "Возраст",
"value": f"{age} лет",
"status": "info",
"comment": "Оценка по лицу",
})
metrics.append({
"name": "Этнос",
"value": ethnicity.capitalize(),
"status": "info",
"comment": "",
})
metrics.append({
"name": "Эмоция",
"value": emotion.capitalize(),
"status": "info",
"comment": "Эмоция в момент съёмки",
})
# ── ИМТ ────────────────────────────────────────────────────
bmi = data.get("bmi", 0)
if bmi > 0:
if bmi < 18.5:
bmi_status, bmi_comment = "warning", "Недостаточный вес"
elif bmi < 25:
bmi_status, bmi_comment = "ok", "Нормальный вес"
elif bmi < 30:
bmi_status, bmi_comment = "warning", "Избыточный вес"
else:
bmi_status, bmi_comment = "danger", "Ожирение"
metrics.append({
"name": "Индекс массы тела (ИМТ)",
"value": f"{bmi:.1f}",
"status": bmi_status,
"comment": bmi_comment,
})
if bmi_status == "warning":
warnings.append(f"Избыточный вес (ИМТ {bmi:.1f}) — рекомендуется контроль питания и физическая активность")
advice.append("Снизить вес до нормы (ИМТ 18.5–24.9): сбалансированное питание, регулярные нагрузки")
elif bmi_status == "danger":
warnings.append(f"Ожирение (ИМТ {bmi:.1f}) — рекомендуется консультация врача")
advice.append("Обратиться к врачу для разработки программы снижения веса")
# ── Диабет ─────────────────────────────────────────────────
diabet = data.get("diabet", "unknown")
diabet_map = {
"small": ("ok", "Низкий риск диабета"),
"middle": ("warning", "Средний риск диабета"),
"big": ("danger", "Высокий риск диабета"),
"unknown": ("info", "Не определён"),
}
d_status, d_comment = diabet_map.get(diabet, ("info", "Не определён"))
metrics.append({
"name": "Риск диабета",
"value": {"small": "Низкий", "middle": "Средний", "big": "Высокий", "unknown": "Не определён"}.get(diabet, "Не определён"),
"status": d_status,
"comment": d_comment,
})
if d_status == "warning":
warnings.append("Средний риск диабета — рекомендуется проверить уровень сахара в крови")
advice.append("Сдать анализ на гликированный гемоглобин и сахар крови натощак")
elif d_status == "danger":
warnings.append("Высокий риск диабета — необходима консультация врача")
advice.append("Срочно обратиться к врачу и сдать анализы на сахар")
# ── Усталость ──────────────────────────────────────────────
fatigue = data.get("fatigue", "small")
fatigue_map = {
"small": ("ok", "Низкая усталость"),
"middle": ("warning", "Средняя усталость"),
"big": ("warning", "Высокая усталость"),
"over": ("danger", "Переутомление"),
}
f_status, f_comment = fatigue_map.get(fatigue, ("info", "Не определена"))
metrics.append({
"name": "Усталость",
"value": {"small": "Низкая", "middle": "Средняя", "big": "Высокая", "over": "Переутомление"}.get(fatigue, "Не определена"),
"status": f_status,
"comment": f_comment,
})
if f_status == "warning":
warnings.append("Повышенная усталость — рекомендуется полноценный отдых")
advice.append("Нормализовать сон (7–8 часов), снизить нагрузку")
elif f_status == "danger":
warnings.append("Переутомление — необходима консультация врача")
advice.append("Обратиться к врачу, исключить перегрузки")
# ── Пульс ──────────────────────────────────────────────────
hr = data.get("hr", 0)
if hr > 0:
if hr < 60:
hr_status, hr_comment = "warning", "Замедленный пульс (брадикардия)"
elif hr <= 100:
hr_status, hr_comment = "ok", "Нормальный пульс"
else:
hr_status, hr_comment = "warning", "Учащённый пульс (тахикардия)"
metrics.append({
"name": "Пульс (ЧСС)",
"value": f"{hr} уд/мин",
"status": hr_status,
"comment": hr_comment,
})
if hr_status == "warning":
warnings.append(f"Пульс {hr} уд/мин — вне нормы (60–100)")
advice.append("Проконсультироваться с врачом по поводу пульса")
# ── Давление ───────────────────────────────────────────────
upper_ap = data.get("upper_ap", 0)
lower_ap = data.get("lower_ap", 0)
if upper_ap > 0 and lower_ap > 0:
if upper_ap < 120 and lower_ap < 80:
ap_status, ap_comment = "ok", "Оптимальное давление"
elif upper_ap < 130 and lower_ap < 85:
ap_status, ap_comment = "ok", "Нормальное давление"
elif upper_ap < 140 or lower_ap < 90:
ap_status, ap_comment = "warning", "Повышенное давление"
else:
ap_status, ap_comment = "danger", "Высокое давление"
metrics.append({
"name": "Артериальное давление",
"value": f"{upper_ap}/{lower_ap} мм рт.ст.",
"status": ap_status,
"comment": ap_comment,
})
if ap_status == "warning":
warnings.append(f"Повышенное давление {upper_ap}/{lower_ap} — рекомендуется контроль")
advice.append("Контролировать давление, ограничить соль, кофеин, алкоголь")
elif ap_status == "danger":
warnings.append(f"Высокое давление {upper_ap}/{lower_ap} — необходима консультация врача")
advice.append("Обратиться к врачу для назначения лечения")
# ── Гликированный гемоглобин ───────────────────────────────
gh = data.get("glycated_hemoglobin", 0)
if gh > 0:
if gh < 5.7:
gh_status, gh_comment = "ok", "Норма (без диабета)"
elif gh < 6.5:
gh_status, gh_comment = "warning", "Преддиабет"
else:
gh_status, gh_comment = "danger", "Диабет"
metrics.append({
"name": "Гликированный гемоглобин",
"value": f"{gh:.1f}%",
"status": gh_status,
"comment": gh_comment,
})
if gh_status == "warning":
warnings.append(f"Гликированный гемоглобин {gh:.1f}% — преддиабет")
advice.append("Снизить потребление сахара, проверить сахар крови")
elif gh_status == "danger":
warnings.append(f"Гликированный гемоглобин {gh:.1f}% — возможен диабет")
advice.append("Срочно обратиться к врачу")
# ── Холестерин ─────────────────────────────────────────────
chol = data.get("cholesterol", 0)
if chol > 0:
if chol < 5.2:
chol_status, chol_comment = "ok", "Норма"
elif chol < 6.2:
chol_status, chol_comment = "warning", "Повышенный"
else:
chol_status, chol_comment = "danger", "Высокий"
metrics.append({
"name": "Общий холестерин",
"value": f"{chol:.1f} ммоль/л",
"status": chol_status,
"comment": chol_comment,
})
if chol_status == "warning":
warnings.append(f"Холестерин {chol:.1f} ммоль/л — выше нормы (до 5.2)")
advice.append("Ограничить жирную пищу, добавить овощи и физическую активность")
chol_ld = data.get("cholesterol_ld", 0)
if chol_ld > 0:
if chol_ld < 3.0:
ld_status, ld_comment = "ok", "Норма"
elif chol_ld < 4.1:
ld_status, ld_comment = "warning", "Повышенный"
else:
ld_status, ld_comment = "danger", "Высокий"
metrics.append({
"name": "Холестерин ЛПНП («плохой»)",
"value": f"{chol_ld:.1f} ммоль/л",
"status": ld_status,
"comment": ld_comment,
})
chol_hd = data.get("cholesterol_hd", 0)
if chol_hd > 0:
if chol_hd >= 1.0:
hd_status, hd_comment = "ok", "Норма (защищает сердце)"
else:
hd_status, hd_comment = "warning", "Пониженный"
metrics.append({
"name": "Холестерин ЛПВП («хороший»)",
"value": f"{chol_hd:.1f} ммоль/л",
"status": hd_status,
"comment": hd_comment,
})
trig = data.get("triglycerides", 0)
if trig > 0:
if trig < 1.7:
trig_status, trig_comment = "ok", "Норма"
elif trig < 2.3:
trig_status, trig_comment = "warning", "Повышенные"
else:
trig_status, trig_comment = "danger", "Высокие"
metrics.append({
"name": "Триглицериды",
"value": f"{trig:.1f} ммоль/л",
"status": trig_status,
"comment": trig_comment,
})
# ── Lp(a) ──────────────────────────────────────────────────
lpa50 = data.get("lpa50", False)
if lpa50:
warnings.append("Повышенный уровень липопротеина (а) — фактор риска сердечно-сосудистых заболеваний")
advice.append("Проконсультироваться с кардиологом")
# ── Дыхание ────────────────────────────────────────────────
resp = data.get("respiratory", 0)
if resp > 0:
if 12 <= resp <= 20:
resp_status, resp_comment = "ok", "Норма"
else:
resp_status, resp_comment = "warning", "Вне нормы"
metrics.append({
"name": "Частота дыхания",
"value": f"{resp:.1f} вдохов/мин",
"status": resp_status,
"comment": resp_comment,
})
# ── Стресс ─────────────────────────────────────────────────
stress = data.get("stress", 0)
if stress > 0:
if stress < 50:
stress_status, stress_comment = "ok", "Низкий уровень стресса"
elif stress < 100:
stress_status, stress_comment = "ok", "Умеренный уровень стресса"
elif stress < 150:
stress_status, stress_comment = "warning", "Повышенный уровень стресса"
else:
stress_status, stress_comment = "danger", "Высокий уровень стресса"
metrics.append({
"name": "Уровень стресса",
"value": f"{stress}",
"status": stress_status,
"comment": stress_comment,
})
if stress_status == "warning":
warnings.append("Повышенный уровень стресса")
advice.append("Практиковать релаксацию, дыхательные упражнения, прогулки")
elif stress_status == "danger":
warnings.append("Высокий уровень стресса — рекомендуется консультация")
advice.append("Обратиться к психологу или психотерапевту")
# ── Эластичность сосудов ───────────────────────────────────
rigidity = data.get("rigidity", 0)
if rigidity > 0:
if rigidity < 9:
rig_status, rig_comment = "ok", "Сосуды эластичные"
elif rigidity < 12:
rig_status, rig_comment = "warning", "Сниженная эластичность"
else:
rig_status, rig_comment = "danger", "Жёсткие сосуды"
metrics.append({
"name": "Эластичность сосудов",
"value": f"{rigidity:.1f} м/с",
"status": rig_status,
"comment": rig_comment,
})
# ── Моргания ───────────────────────────────────────────────
blinks = data.get("blinks", 0)
metrics.append({
"name": "Моргания",
"value": f"{blinks}",
"status": "info",
"comment": "За время сканирования",
})
# ── Сводка ─────────────────────────────────────────────────
summary = f"{gender.capitalize()}, {age} лет, {ethnicity}. Эмоция: {emotion}."
# Считаем проблемы
problems = [m for m in metrics if m["status"] in ("warning", "danger")]
if not problems:
summary += " Все основные показатели в норме."
else:
danger = [m for m in metrics if m["status"] == "danger"]
warning = [m for m in metrics if m["status"] == "warning"]
if danger:
summary += f" Обнаружены серьёзные отклонения: {', '.join(m['name'] for m in danger)}."
if warning:
summary += f" Требуют внимания: {', '.join(m['name'] for m in warning)}."
if not advice:
advice.append("Продолжайте вести здоровый образ жизни и проходите профилактические осмотры")
return {
"summary": summary,
"metrics": metrics,
"warnings": warnings,
"advice": advice,
}