222 lines
6.2 KiB
Markdown
222 lines
6.2 KiB
Markdown
# Heatmap API - Тепловые карты с кэшированием
|
||
|
||
## Endpoint
|
||
|
||
### GET /api/v1/stats/heatmap
|
||
|
||
Получить данные для тепловой карты находок с 4 типами визуализации и кэшированием на 1 час.
|
||
|
||
## Типы карт (map_type)
|
||
|
||
### 1. `all` - Все точки
|
||
Все точки с одинаковой интенсивностью (1.0).
|
||
|
||
**Использование:** Общий обзор всех находок.
|
||
|
||
**Пример:**
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=all"
|
||
```
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"points": [
|
||
{
|
||
"lat": 53.92,
|
||
"lon": 27.58,
|
||
"intensity": 1.0,
|
||
"case_id": "uuid",
|
||
"metadata": {
|
||
"age": 10,
|
||
"season": "лето",
|
||
"outcome": "alive"
|
||
}
|
||
}
|
||
],
|
||
"total": 1,
|
||
"filters_applied": {"map_type": "all"}
|
||
}
|
||
```
|
||
|
||
### 2. `age` - По возрасту
|
||
Интенсивность зависит от возраста: младше = выше интенсивность (более критично).
|
||
|
||
**Формула:** `intensity = max(0.3, 1.0 - (age / 18.0))`
|
||
|
||
**Использование:** Приоритизация поиска младших детей.
|
||
|
||
**Пример:**
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=age&age_group=8-11"
|
||
```
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"points": [
|
||
{
|
||
"lat": 53.92,
|
||
"lon": 27.58,
|
||
"intensity": 0.44,
|
||
"case_id": "uuid",
|
||
"metadata": {
|
||
"age": 10,
|
||
"age_group": "8-11"
|
||
}
|
||
}
|
||
],
|
||
"total": 1,
|
||
"filters_applied": {
|
||
"map_type": "age",
|
||
"age_group": "8-11"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. `season` - По сезону
|
||
Интенсивность зависит от сезона (зима = критичнее).
|
||
|
||
**Интенсивности:**
|
||
- Зима: 1.0 (красный)
|
||
- Осень: 0.8 (оранжевый)
|
||
- Весна: 0.6 (желтый)
|
||
- Лето: 0.4 (зеленый)
|
||
|
||
**Использование:** Анализ сезонных паттернов.
|
||
|
||
**Пример:**
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=season&season=зима"
|
||
```
|
||
|
||
### 4. `outcome` - По исходу
|
||
Интенсивность зависит от исхода поиска.
|
||
|
||
**Интенсивности:**
|
||
- Погиб: 1.0 (красный)
|
||
- Выжил: 0.5 (зеленый)
|
||
- Неизвестно: 0.3 (серый)
|
||
|
||
**Использование:** Анализ опасных зон.
|
||
|
||
**Пример:**
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=outcome&outcome=alive"
|
||
```
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"points": [
|
||
{
|
||
"lat": 53.92,
|
||
"lon": 27.58,
|
||
"intensity": 0.5,
|
||
"case_id": "uuid",
|
||
"metadata": {
|
||
"outcome": "alive",
|
||
"distance_km": 2.5
|
||
}
|
||
}
|
||
],
|
||
"total": 1,
|
||
"filters_applied": {
|
||
"map_type": "outcome",
|
||
"outcome": "alive"
|
||
}
|
||
}
|
||
```
|
||
|
||
## Query Parameters (фильтры)
|
||
|
||
| Параметр | Тип | Описание | Пример |
|
||
|----------|-----|----------|--------|
|
||
| `map_type` | string | Тип карты: all, age, season, outcome | `map_type=age` |
|
||
| `age_group` | string | Возрастная группа: 0-3, 4-7, 8-11, 12-14, 15-17, 18+ | `age_group=8-11` |
|
||
| `season` | string | Сезон: зима, весна, лето, осень | `season=лето` |
|
||
| `year_from` | int | Год начала периода | `year_from=2020` |
|
||
| `year_to` | int | Год окончания периода | `year_to=2026` |
|
||
| `outcome` | string | Исход: alive, deceased | `outcome=alive` |
|
||
|
||
## Кэширование
|
||
|
||
**Механизм:** `functools.lru_cache` с автоматической инвалидацией каждый час.
|
||
|
||
**Параметры кэша:**
|
||
- Размер: 128 комбинаций параметров
|
||
- TTL: 1 час (автоматически через timestamp_hour)
|
||
- Ключ: `{map_type}_{age_group}_{season}_{year_from}_{year_to}_{outcome}_{hour}`
|
||
|
||
**Преимущества:**
|
||
- Быстрый отклик для повторных запросов
|
||
- Снижение нагрузки на БД
|
||
- Автоматическая очистка устаревших данных
|
||
|
||
## Примеры использования
|
||
|
||
### 1. Все находки за 2025 год
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=all&year_from=2025&year_to=2025"
|
||
```
|
||
|
||
### 2. Младшие дети (0-7 лет) зимой
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=age&age_group=0-3&season=зима"
|
||
```
|
||
|
||
### 3. Опасные зоны (погибшие)
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=outcome&outcome=deceased"
|
||
```
|
||
|
||
### 4. Подростки летом (последние 3 года)
|
||
```bash
|
||
curl "http://localhost:8000/api/v1/stats/heatmap?map_type=age&age_group=12-14&season=лето&year_from=2023&year_to=2026"
|
||
```
|
||
|
||
## Интеграция с фронтендом
|
||
|
||
### Leaflet.js пример
|
||
```javascript
|
||
// Получить данные
|
||
const response = await fetch('/api/v1/stats/heatmap?map_type=age');
|
||
const data = await response.json();
|
||
|
||
// Создать heatmap слой
|
||
const heatPoints = data.points.map(p => [p.lat, p.lon, p.intensity]);
|
||
const heat = L.heatLayer(heatPoints, {
|
||
radius: 25,
|
||
blur: 15,
|
||
maxZoom: 17,
|
||
gradient: {
|
||
0.0: 'green',
|
||
0.5: 'yellow',
|
||
1.0: 'red'
|
||
}
|
||
}).addTo(map);
|
||
```
|
||
|
||
## Протестировано (2026-05-05)
|
||
|
||
✅ `map_type=all` - работает (intensity: 1.0)
|
||
✅ `map_type=age` - работает (intensity: 0.44 для 10 лет)
|
||
✅ `map_type=season` - работает
|
||
✅ `map_type=outcome` - работает (intensity: 0.5 для alive)
|
||
✅ Фильтры: age_group, season, outcome - работают
|
||
✅ Кэширование: lru_cache с TTL 1 час
|
||
|
||
## Технические детали
|
||
|
||
**Файлы:**
|
||
- `backend/services/stats_service.py` - функции `get_heatmap_with_cache()`, `get_heatmap_data_cached()`
|
||
- `backend/api/v1/stats.py` - endpoint `/heatmap`
|
||
|
||
**Зависимости:**
|
||
- `functools.lru_cache` - встроенный кэш Python
|
||
- SQLAlchemy - фильтрация по БД
|
||
- Pydantic - валидация типов
|
||
|
||
## Дата создания
|
||
2026-05-05
|