Files
vector/backend/routers/cases.py
T
Sadmin a1d33a2b28 Реорганизация /admin: операции, честные метрики, синхронизация закрытия
- Вкладка «Операции»: список search_operations (статус/подразделение/обновлена,
  фильтр, переход в анализ кейса), unit_name в DTO.
- Метрики: + активные/завершённые операции (stats из search_operations);
  «Активность 7 дней» теперь честная (created_at >= now-7d, была = total).
- Синхронизация: закрытие кейса через /admin PATCH → активная операция
  автоматически completed + closed_at (аудит в логе).
- ФИКС legacy-бага PATCH /cases: default_factory-списки затирали
  terrain/diagnosis_type пустыми массивами — выкидываются пустые списки.
- Редактор исхода: «Кто нашёл» → select WHO_FOUND_TYPES.
- Кнопка «Пользователи · Аудит» → /users.
- Тесты: автозакрытие операции, метрики (232 passed).
2026-09-09 22:41:14 +03:00

65 lines
2.4 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Query
from backend.database import db
from backend.routers.auth import require_roles
from backend.schemas import CaseCreate, CaseListResponse, CaseResponse, CaseUpdate
router = APIRouter(prefix='/api/v1/cases', tags=['cases'], dependencies=[Depends(require_roles(['operator', 'field', 'admin']))])
@router.post('', response_model=CaseResponse)
def create_case(payload: CaseCreate) -> dict:
data = payload.model_dump(exclude_none=True)
gps = data.pop('gps', None) or {}
note = data.pop('note', None)
if note is not None and 'notes' not in data:
data['notes'] = note
if gps:
data['gps_lat'] = gps.get('lat')
data['gps_lon'] = gps.get('lon')
case = db.create_case(**data)
if data.get('status'):
db.update_case(case.id, status=data['status'])
return case.to_detail()
@router.get('', response_model=CaseListResponse)
def list_cases(page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=100)) -> dict:
cases = db.list_cases()
start = (page - 1) * page_size
end = start + page_size
items = [c.to_detail() for c in cases[start:end]]
return {'items': items, 'total': len(cases), 'page': page, 'page_size': page_size}
@router.get('/{case_id}', response_model=CaseResponse)
def get_case(case_id: str) -> dict:
case = db.get_case(case_id)
if not case:
raise HTTPException(status_code=404, detail='Case not found')
return case.to_detail()
@router.patch('/{case_id}', response_model=CaseResponse)
def patch_case(case_id: str, payload: CaseUpdate) -> dict:
data = payload.model_dump(exclude_none=True)
# default_factory-списки затирали terrain/diagnosis_type пустыми массивами
data = {k: v for k, v in data.items() if not (isinstance(v, list) and not v)}
note = data.pop('note', None)
if note is not None and 'notes' not in data:
data['notes'] = note
case = db.update_case(case_id, **data)
if not case:
raise HTTPException(status_code=404, detail='Case not found')
return case.to_detail()
@router.delete('/{case_id}')
def delete_case(case_id: str) -> dict[str, bool]:
deleted = db.delete_case(case_id)
if not deleted:
raise HTTPException(status_code=404, detail='Case not found')
return {'deleted': True}