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

345 lines
14 KiB
HTML
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.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- SEO -->
<title>{% if result %}Результат диагностики #{{ result.share_id }} | med.my-dpr.ru{% else %}Результат не найден | med.my-dpr.ru{% endif %}</title>
<meta name="description" content="{% if result %}{{ result.summary }}{% else %}Результат анализа не найден.{% endif %}">
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#0014FC">
<!-- Open Graph -->
<meta property="og:type" content="article">
<meta property="og:site_name" content="Диагностика по видео">
<meta property="og:title" content="{% if result %}Результат диагностики #{{ result.share_id }} — оценка здоровья по лицу{% else %}Результат не найден{% endif %}">
<meta property="og:description" content="{% if result %}{{ result.summary }}{% else %}Результат анализа не найден.{% endif %}">
<meta property="og:url" content="https://med.my-dpr.ru/r/{{ result.share_id if result else '' }}">
<meta property="og:image" content="https://med.my-dpr.ru/static/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Результат диагностики по видео">
<meta property="og:locale" content="ru_RU">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{% if result %}Результат диагностики #{{ result.share_id }}{% else %}Результат не найден{% endif %}">
<meta name="twitter:description" content="{% if result %}{{ result.summary }}{% else %}Результат анализа не найден.{% endif %}">
<meta name="twitter:image" content="https://med.my-dpr.ru/static/og-image.png">
<!-- Favicon -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🩺</text></svg>">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #061A40 0%, #0B4F9C 50%, #2D9CDB 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container { max-width: 900px; margin: 0 auto; }
.card {
background: #fff;
border-radius: 16px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
margin-bottom: 20px;
}
h1 { color: #fff; text-align: center; margin-bottom: 20px; font-size: 28px; }
h2 { color: #333; margin-bottom: 15px; font-size: 20px; }
.subtitle { color: rgba(255,255,255,0.9); text-align: center; margin-bottom: 25px; font-size: 15px; }
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 10px;
}
.result-id {
background: linear-gradient(135deg, #061A40, #0B4F9C, #2D9CDB);
color: #fff;
padding: 6px 14px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
}
.result-date { color: #888; font-size: 13px; }
.download-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.btn-download {
flex: 1;
min-width: 140px;
text-align: center;
background: linear-gradient(135deg, #061A40, #0B4F9C, #2D9CDB);
color: #fff;
border: none;
padding: 12px 20px;
border-radius: 8px;
font-size: 15px;
cursor: pointer;
transition: background 0.3s;
text-decoration: none;
display: inline-block;
}
.btn-download:hover { filter: brightness(1.15); }
.btn-download:active { transform: scale(0.98); }
.summary-box {
background: #f0f4ff;
border-left: 4px solid #0014FC;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 16px;
line-height: 1.6;
}
.metrics-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.metrics-table th, .metrics-table td { padding: 10px 15px; text-align: left; border-bottom: 1px solid #eee; }
.metrics-table th { background: #f8f9fa; font-weight: 600; color: #555; }
.metrics-table tr:hover { background: #fafafa; }
.status-badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; }
.status-ok { background: #d4edda; color: #155724; }
.status-warning { background: #fff3cd; color: #856404; }
.status-danger { background: #f8d7da; color: #721c24; }
.status-info { background: #d1ecf1; color: #0c5460; }
.warnings-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.warnings-box h3 { color: #856404; margin-bottom: 10px; font-size: 16px; }
.warnings-box ul { padding-left: 20px; }
.warnings-box li { margin: 5px 0; color: #856404; }
.advice-box {
background: #d4edda;
border-left: 4px solid #28a745;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.advice-box h3 { color: #155724; margin-bottom: 10px; font-size: 16px; }
.advice-box ul { padding-left: 20px; }
.advice-box li { margin: 5px 0; color: #155724; }
.disclaimer {
background: #f8f9fa;
border-radius: 8px;
padding: 12px 15px;
font-size: 13px;
color: #888;
margin-top: 20px;
}
.error-box {
background: #f8d7da;
border-left: 4px solid #dc3545;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
color: #721c24;
}
.btn {
background: linear-gradient(135deg, #061A40, #0B4F9C, #2D9CDB);
color: #fff;
border: none;
padding: 12px 30px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
text-decoration: none;
display: inline-block;
}
.btn:hover { filter: brightness(1.15); }
.btn-secondary {
background: #eee;
color: #333;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
transition: background 0.3s;
}
.btn-secondary:hover { background: #ddd; }
.center { text-align: center; }
.share-box {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-top: 20px;
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.share-box input {
flex: 1;
min-width: 200px;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
color: #555;
}
.toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: #333;
color: #fff;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
opacity: 0;
transition: all 0.3s;
z-index: 1000;
}
.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }
/* ===== Мобильная версия ===== */
@media (max-width: 600px) {
body { padding: 12px; }
h1 { font-size: 22px; margin-bottom: 12px; }
.subtitle { font-size: 14px; margin-bottom: 18px; }
.card { padding: 18px; border-radius: 14px; margin-bottom: 14px; }
h2 { font-size: 18px; }
.result-header { flex-direction: column; align-items: flex-start; gap: 8px; }
.metrics-table thead { display: none; }
.metrics-table, .metrics-table tbody, .metrics-table tr, .metrics-table td { display: block; width: 100%; }
.metrics-table tr { border: 1px solid #eee; border-radius: 10px; margin-bottom: 10px; padding: 10px 12px; }
.metrics-table td { border: none; padding: 5px 0; }
.metrics-table td:first-child { font-weight: 600; color: #555; }
.summary-box, .warnings-box, .advice-box { padding: 12px 14px; font-size: 15px; }
.btn, .btn-secondary { width: 100%; padding: 14px 20px; font-size: 16px; min-height: 48px; }
.share-box { flex-direction: column; align-items: stretch; }
.share-box input { width: 100%; min-width: 0; }
.download-buttons { flex-direction: column; }
.btn-download { width: 100%; min-width: 0; padding: 14px 20px; font-size: 16px; min-height: 48px; }
}
</style>
</head>
<body>
<div class="container">
<h1>🩺 Диагностика по видео</h1>
<p class="subtitle">Оценка состояния здоровья по лицу</p>
{% if error %}
<div class="card">
<div class="error-box">
<strong>Ошибка:</strong> {{ error }}
</div>
<div class="center">
<a href="/" class="btn">🔍 Пройти диагностику</a>
</div>
</div>
{% else %}
<div class="card">
<div class="result-header">
<span class="result-id">Результат #{{ result.share_id }}</span>
<span class="result-date">📅 {{ result.created_at[:10] }} {{ result.created_at[11:16] }}</span>
</div>
<div class="download-buttons">
<a class="btn-download" href="/download/{{ result.share_id }}/pdf">📄 Скачать PDF</a>
<a class="btn-download" href="/download/{{ result.share_id }}/docx">📝 Скачать DOCX</a>
{% if video_exists %}
<a class="btn-download" href="/download/{{ result.share_id }}/video">🎬 Скачать видео</a>
{% endif %}
<a class="btn-download" href="https://max.ru/miac_dnr_bot" target="_blank" rel="noopener">🏥 Запись на диспанцеризацию</a>
</div>
<div class="summary-box">{{ result.summary }}</div>
{% if result.warnings %}
<div class="warnings-box">
<h3>⚠️ Требует внимания</h3>
<ul>
{% for w in result.warnings %}
<li>{{ w }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<h2>Показатели</h2>
<table class="metrics-table">
<thead>
<tr><th>Показатель</th><th>Значение</th><th>Оценка</th></tr>
</thead>
<tbody>
{% for m in result.metrics %}
<tr>
<td>{{ m.name }}</td>
<td>
<strong>{{ m.value }}</strong>
{% if m.comment %}<br><small style="color:#888">{{ m.comment }}</small>{% endif %}
</td>
<td>
<span class="status-badge status-{{ m.status }}">
{% if m.status == 'ok' %}В норме
{% elif m.status == 'warning' %}Обратить внимание
{% elif m.status == 'danger' %}Отклонение
{% else %}Информация{% endif %}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if result.advice %}
<div class="advice-box">
<h3>💡 Рекомендации</h3>
<ul>
{% for a in result.advice %}
<li>{{ a }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="share-box">
<input type="text" id="shareLink" value="https://med.my-dpr.ru/r/{{ result.share_id }}" readonly>
<button class="btn-secondary" onclick="copyLink()">📋 Копировать ссылку</button>
</div>
<div class="disclaimer">⚠️ Результат получен автоматически по анализу видео и не является медицинским диагнозом. Для точной оценки обратитесь к врачу.</div>
<div class="center" style="margin-top:20px">
<a href="/" class="btn">🔍 Пройти диагностику</a>
</div>
</div>
{% endif %}
</div>
<div class="toast" id="toast">Ссылка скопирована!</div>
<script>
function copyLink() {
const input = document.getElementById('shareLink');
input.select();
input.setSelectionRange(0, 99999);
navigator.clipboard.writeText(input.value).then(() => {
const toast = document.getElementById('toast');
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 2000);
});
}
</script>
</body>
</html>