# MedArchive — обработка архива прайсов клиник-партнёров

Terricon Valley · Medtech Hackathon (26–28.06.2026).

**Живой демо: https://med.secondbrain.tools**

## 🎬 Демо-трейлер

[![Демо-трейлер MedArchive](pitch/landing/trailer_poster.png)](pitch/landing/trailer.mp4)

▶️ **[Смотреть трейлер (40 сек)](pitch/landing/trailer.mp4)** · вживую: [med.secondbrain.tools/pitch](https://med.secondbrain.tools/pitch)

Система автоматически разбирает архив разнородных прайс-листов клиник (PDF, сканы, DOCX,
XLSX/XLS), извлекает услуги и цены, нормализует их к справочнику из 1287 услуг и даёт поиск
и сравнение «кто оказывает услугу и по какой цене».

## Результат на тестовом архиве (8 клиник, 10 файлов)

- **16 140 позиций** извлечено: нативными парсерами + трудные PDF (битый текстовый слой,
  нестандартная вёрстка) — через Gemini Vision; обработаны все 8 клиник.
- **73,7% позиций нормализованы автоматически** (цель ТЗ — не ниже 70%).
- Остальное уходит в очередь ручной разметки, как и предполагает ТЗ.

Полный отчёт о качестве — `docs/quality_report.json`.

## Производительность (ТЗ §5)

| Тип документа | Замер | Норматив ТЗ §5 |
|---|---|---|
| Текст (XLSX, 1724 строки) | **0,2 с** | ≤ 60 с ✅ |
| Скан/битый PDF через Gemini Vision | ~14,5 с/страница; 21-страничный документ — 5,2 мин | ≤ 3 мин/документ |

Текстовые форматы (PDF-текст, DOCX, XLSX/XLS) укладываются в норматив с большим запасом.
Скан-добор упирается в **квоту запросов free-tier Gemini Vision** (~14,5 с/страница — это
троттлинг RPM, а не латентность кода): небольшие и средние сканы проходят за < 3 мин, крупный
21-страничный документ — за 5,2 мин. Узкое место — лимит API, не алгоритм: платный тариф Gemini
или параллельные постраничные вызовы снимают ограничение без изменения логики извлечения.

## Стек и решения

| Слой | Решение |
|---|---|
| Извлечение | Python: pdfplumber + PyMuPDF, openpyxl/xlrd, python-docx. Таблицы из PDF без линий восстанавливаются по координатам слов |
| Vision-добор | Gemini (`gemini-2.5-flash`) — для сканов и битого текстового слоя |
| Нормализация | Каскад: код тарификатора → точное совпадение → эмбеддинги Gemini → нечёткое сравнение → очередь `unmatched` |
| Валидация | Правила ТЗ §4.4: положительность цены, резидент ≥ нерезидент, аномалии, версионирование |
| Хранилище | SQLite (MVP, разворачивается без Docker). Боевая схема PostgreSQL + pgvector — в `db/migrations` |
| API | FastAPI: эндпоинты ТЗ + `/stats`, загрузка архива, авто-загрузка из папки, алерты об изменении цен, экспорт в Excel; OpenAPI на `/docs` |
| Фронт | Одностраничный (Tailwind): поиск и сравнение цен, экран верификации оператора, дашборд обработки |
| Деплой | Контейнер API за Caddy на Selectel KZ |

Подробно об устройстве системы — **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** (поток данных, карта модулей, модель данных, обоснование решений).

## Запуск локально

```bash
python -m venv .venv && .venv/bin/pip install -e .
# архив прайсов → data/raw/, справочник → data/reference/dictionary.xlsx
GEMINI_API_KEY=... python scripts/run_pipeline.py        # → data/medarchive.db + отчёт
uvicorn api.main:app                                     # → http://localhost:8000/docs
# открыть web/index.html?api=http://localhost:8000
```

Эмбеддинги и Vision вызываются через Gemini API, поэтому локальная модель не нужна и контейнер
остаётся лёгким. Поиск и сравнение в готовой базе работают без обращений к API.

## Структура

```
contracts/  — общие модели данных (контракт между модулями)
db/         — схема PostgreSQL + pgvector
etl/        — извлечение, нормализация, валидация, конвейер
api/        — REST API поиска и сравнения
web/        — одностраничный фронт
infra/      — деплой (compose, Caddyfile)
docs/       — отчёт о качестве, план
pitch/      — презентация и демо-сценарий
scripts/    — запуск конвейера и проверки
```
