160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
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']
|
|
)
|