Files
vector/backend/routers/admin.py
T
Sadmin a7f8879034 Импорт .docx удалён: не использовался (0 кейсов), парсер не совместим с русскими спецдонесениями, LLM-распознавание вырезано
- admin.py: /parse-doc + _extract_docx_text/_extract_kv_pairs/_coerce_preview/
  _parse_bool удалены; импорты почищены.
- schemas.py: ParseDocResponse удалена.
- AdminDashboard: секция «Импорт отчёта .docx» + все upload/parse handlers
  и state удалены (компонент стал на ~160 строк короче).
2026-09-09 23:00:13 +03:00

72 lines
3.1 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
import logging
from fastapi import APIRouter, Depends, HTTPException, Query
from backend.database import db
from backend.routers.auth import require_roles
from backend.schemas import CaseListResponse, CaseResponse, CaseUpdate, DashboardResponse
log = logging.getLogger('vector.admin')
router = APIRouter(prefix='/api/v1/admin', tags=['admin'], dependencies=[Depends(require_roles(['admin']))])
@router.get('/cases', response_model=CaseListResponse)
def admin_cases(
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=100),
status: str | None = None,
age_min: int | None = Query(default=None, ge=0),
age_max: int | None = Query(default=None, ge=0),
) -> dict:
cases = db.list_cases()
if status:
cases = [c for c in cases if c.status == status]
if age_min is not None:
cases = [c for c in cases if c.age is not None and c.age >= age_min]
if age_max is not None:
cases = [c for c in cases if c.age is not None and c.age <= age_max]
start = (page - 1) * page_size
end = start + page_size
return {'items': [c.to_detail() for c in cases[start:end]], 'total': len(cases), 'page': page, 'page_size': page_size}
@router.patch('/cases/{case_id}', response_model=CaseResponse)
def admin_update_case(case_id: str, payload: CaseUpdate) -> dict:
data = payload.model_dump(exclude_none=True)
# default_factory-списки ([]) при exclude_none всё равно попадают в data и
# ЗАТИРАЮТ реальные 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')
# B21-синхронизация: кейс закрыт вручную → его операция завершается автоматически
if data.get('status') == 'closed':
import uuid as _uuid
from backend.models import SearchOperation as _Op
from backend.database import SessionLocal as _SL
case_uuid = _uuid.UUID(str(case.id)) if isinstance(case.id, str) else case.id
with _SL() as session:
ops = session.query(_Op).filter(_Op.case_id == case_uuid,
_Op.status.in_(['planned', 'active', 'paused'])).all()
for op in ops:
op.status = 'completed'
op.closed_at = datetime.now(timezone.utc)
session.commit()
closed_ops = len(ops)
if closed_ops:
log.info('admin closed case %s → %d операция(ий) завершены автоматически', case_id, closed_ops)
return case.to_detail()
@router.get('/dashboard', response_model=DashboardResponse)
def admin_dashboard() -> dict:
return db.stats()