from fastapi import APIRouter, Depends, Query from sqlalchemy.orm import Session from pydantic import BaseModel from typing import Dict, List, Optional, Literal from database import get_db from models import Case from services.stats_service import ( get_dashboard_stats, get_statistical_recommendation, get_heatmap_with_cache, DashboardStats, StatisticalRecommendation ) router = APIRouter() class HeatmapPoint(BaseModel): lat: float lon: float intensity: float case_id: str metadata: Dict class HeatmapResponse(BaseModel): points: List[HeatmapPoint] total: int filters_applied: Dict class StatisticalRecommendationRequest(BaseModel): age: int season: Optional[str] = None terrain_primary: Optional[str] = None @router.get("/summary", response_model=DashboardStats) async def get_summary(db: Session = Depends(get_db)): """ Получить агрегированную статистику для дашборда. Возвращает: - Общее количество случаев (всего, активных, закрытых) - Распределение по полу, возрасту, психотипу, диагнозам, сезонам - Средняя дистанция находки - Средняя длительность поиска - Процент выживаемости """ return get_dashboard_stats(db) @router.post("/recommendation", response_model=StatisticalRecommendation) async def get_recommendation( request: StatisticalRecommendationRequest, db: Session = Depends(get_db) ): """ Получить статистические рекомендации на основе похожих случаев. Фильтры (в порядке приоритета): 1. Возраст ±2 года + сезон + terrain 2. Возраст ±2 года + сезон (если < 5 случаев) 3. Возраст ±2 года (если < 5 случаев) 4. Все случаи (если < 5 случаев) Возвращает: - Медианное расстояние находки - Топ-3 направления с процентами - Топ-5 типов локаций - Процент выживаемости - Размер выборки - Использованные фильтры """ case_data = { 'age': request.age, 'season': request.season, 'terrain_primary': request.terrain_primary } return get_statistical_recommendation(case_data, db) @router.get("/heatmap", response_model=HeatmapResponse) async def get_heatmap( map_type: Literal['all', 'age', 'season', 'outcome'] = Query( 'all', description="Тип карты: all (все точки), age (по возрасту), season (по сезону), outcome (по исходу)" ), age_group: Optional[str] = Query( None, description="Возрастная группа: 0-3, 4-7, 8-11, 12-14, 15-17, 18+" ), season: Optional[str] = Query( None, description="Сезон: зима, весна, лето, осень" ), year_from: Optional[int] = Query( None, description="Год начала периода (например, 2020)" ), year_to: Optional[int] = Query( None, description="Год окончания периода (например, 2026)" ), outcome: Optional[str] = Query( None, description="Исход: alive (выжил), deceased (погиб)" ), db: Session = Depends(get_db) ): """ Получить данные для тепловой карты находок с кэшированием (1 час). **Типы карт:** - `all` - все точки с одинаковой интенсивностью - `age` - интенсивность зависит от возраста (младше = выше) - `season` - интенсивность зависит от сезона (зима = выше) - `outcome` - интенсивность зависит от исхода (погиб = выше) **Фильтры:** - `age_group` - возрастная группа (0-3, 4-7, 8-11, 12-14, 15-17, 18+) - `season` - сезон (зима, весна, лето, осень) - `year_from`, `year_to` - период по годам - `outcome` - исход (alive, deceased) **Кэширование:** Результаты кэшируются на 1 час для ускорения повторных запросов. **Возвращает:** - `points` - массив точек с координатами, интенсивностью и метаданными - `total` - общее количество точек - `filters_applied` - примененные фильтры """ result = get_heatmap_with_cache( db=db, map_type=map_type, age_group=age_group, season=season, year_from=year_from, year_to=year_to, outcome=outcome ) points = [ HeatmapPoint( lat=p['lat'], lon=p['lon'], intensity=p['intensity'], case_id=p['case_id'], metadata=p['metadata'] ) for p in result['points'] ] return HeatmapResponse( points=points, total=result['total'], filters_applied=result['filters_applied'] )