Files
medtech-hackathon/api/main.py
T
admins a6a6609c48 feat: Фаза 0 — каркас, контракты, загрузчик справочника
- db/migrations/001_init.sql — схема (4 сущности, prices jsonb, pgvector/pg_trgm)
- contracts/models.py — общие модели (RawRow, ParsedDocument, MatchResult, *Out)
- etl/dictionary.py — загрузчик справочника (1281 услуга, нормализация)
- api/main.py — FastAPI: 7 эндпоинтов ТЗ + /stats (контракт)
- infra/ — docker-compose (pg+pgvector, api, web), Caddyfile
- scripts/proof/phase0_proof.py — проверка каскада на реальных данных (35% без эмбеддингов)
- data/ в .gitignore: данные клиник в публичный репо не коммитятся

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:23:06 +05:00

70 lines
2.9 KiB
Python

"""MedArchive API — эндпоинты ТЗ §4.5 плюс /stats для дашборда.
Здесь зафиксирован контракт API (пути, параметры, модели ответа). Реализация
запросов к БД — задача агента D; пока эндпоинты возвращают пустые заготовки,
чтобы приложение поднималось и фронт мог разрабатываться на сгенерированной
OpenAPI-схеме.
"""
from __future__ import annotations
import sys
from pathlib import Path
from fastapi import FastAPI, Query
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from contracts.models import PartnerOut, PriceOut, ServiceOut, Stats # noqa: E402
app = FastAPI(
title="MedArchive API",
version="0.1.0",
description="Поиск услуг и цен по архиву прайсов клиник-партнёров.",
)
@app.get("/services", response_model=list[ServiceOut], summary="Услуги справочника")
def list_services(specialty: str | None = Query(None, description="Фильтр по специальности")):
return [] # TODO(D): выборка из таблицы service
@app.get("/services/{service_id}/partners", response_model=list[PriceOut],
summary="Кто оказывает услугу и по какой цене")
def service_partners(service_id: str):
return [] # TODO(D): партнёры с ценами по услуге (ядро WOW-сравнения)
@app.get("/partners", response_model=list[PartnerOut], summary="Партнёры")
def list_partners(city: str | None = None, is_active: bool | None = None):
return [] # TODO(D)
@app.get("/partners/{partner_id}/services", response_model=list[PriceOut],
summary="Все услуги партнёра с ценами")
def partner_services(partner_id: str):
return [] # TODO(D)
@app.get("/search", summary="Полнотекстовый поиск по услугам и партнёрам")
def search(q: str = Query(..., min_length=1)):
return {"services": [], "partners": []} # TODO(D)
@app.get("/unmatched", summary="Несопоставленные позиции (для операторов)")
def unmatched(limit: int = 50, offset: int = 0):
return [] # TODO(D)
@app.post("/match", summary="Ручное сопоставление позиции со справочником")
def manual_match(item_id: str, service_id: str):
return {"item_id": item_id, "service_id": service_id, "status": "todo"} # TODO(D)
@app.get("/stats", response_model=Stats, summary="Сводка качества обработки")
def stats():
return Stats() # TODO(D): реальные метрики из БД
@app.get("/health", summary="Проверка живости")
def health():
return {"status": "ok"}