Веб-приложение диагностики по видео (ФПГ GigaDoc)

- Загрузка/сканирование видео, извлечение кадров, анализ через SDK ФПГ
- Понятный диагноз с сохранением в SQLite и share-ссылками
- Экспорт PDF/DOCX, скачивание записанного видео
- Камерное сканирование на мобильных (canvas + MediaRecorder)
- Секреты вынесены в переменные окружения
This commit is contained in:
apuc
2026-09-07 19:01:06 +03:00
commit 04a7b31a75
12 changed files with 4419 additions and 0 deletions
+366
View File
@@ -0,0 +1,366 @@
#!/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,
}