Решение по TD-STYLE-ANALYZERS: LF — инструменты проекта (Python/Node) пишут LF, CRLF-.sh не работают на Linux CI (sh scripts/ci.sh), большинство файлов уже были LF. Добавлен .gitattributes (* text=auto eol=lf, бинарные исключения), .editorconfig переведён на lf, 1029 файлов конвертированы, git add --renormalize. Из индекса убраны закравшиеся archive/**/__pycache__/*.pyc.
75 lines
2.9 KiB
Python
75 lines
2.9 KiB
Python
"""Мониторинг пайплайна — вкладка «Обработка» (очередь и отсев)."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from ..auth import current_login
|
|
from ..services import processing as processing_svc
|
|
|
|
router = APIRouter(prefix="/api/pipeline", tags=["processing"])
|
|
|
|
|
|
class ReturnBody(BaseModel):
|
|
reason: str = ""
|
|
|
|
|
|
@router.get("/stats")
|
|
def stats(_: str = Depends(current_login)) -> dict:
|
|
"""Сводка: размер очереди (new/ai) и число записей в отсеве."""
|
|
return processing_svc.stats()
|
|
|
|
|
|
@router.get("/queue")
|
|
def queue(limit: int = 100, _: str = Depends(current_login)) -> dict:
|
|
"""Сырые сообщения, ожидающие обработки (этап 1 или ИИ)."""
|
|
q = processing_svc.queue_counts()
|
|
return {
|
|
"items": processing_svc.list_queue(limit),
|
|
"counts": q,
|
|
"rejected": processing_svc.rejected_count(),
|
|
}
|
|
|
|
|
|
@router.get("/rejected")
|
|
def rejected(
|
|
q: str = "",
|
|
offset: int = 0,
|
|
limit: int = 100,
|
|
_: str = Depends(current_login),
|
|
) -> dict:
|
|
"""Отсев: что и почему не прошло пайплайн (полнотекстовый поиск по q)."""
|
|
return processing_svc.list_rejected(q, offset, limit)
|
|
|
|
|
|
@router.post("/rejected/clear")
|
|
def rejected_clear(_: str = Depends(current_login)) -> dict:
|
|
"""Ручная полная очистка отсева (безвозвратно)."""
|
|
cleared = processing_svc.clear_all()
|
|
return {"ok": True, "cleared": cleared}
|
|
|
|
|
|
@router.delete("/rejected/{rej_id}")
|
|
def rejected_delete(rej_id: str, _: str = Depends(current_login)) -> dict:
|
|
processing_svc.delete_one(rej_id)
|
|
return {"ok": True}
|
|
|
|
|
|
@router.post("/rejected/{rej_id}/return")
|
|
def rejected_return(rej_id: str, body: ReturnBody, _: str = Depends(current_login)) -> dict:
|
|
"""Вернуть отсеянное сообщение в обработку.
|
|
|
|
Для возвращённого сообщения причины отсева (стоп-лист, резюме, тип,
|
|
без суммы, устарело, ML/ИИ-спам) игнорируются: оно уходит на ML/ИИ
|
|
и создаёт карточку. ML обучается на действии (снятие «спама»), а решение
|
|
ИИ по возвращённому сообщению снова учит ML. Запись в отсеве помечается
|
|
«возвращено» с указанной пользователем причиной.
|
|
"""
|
|
try:
|
|
out = processing_svc.return_to_queue(rej_id, body.reason)
|
|
except KeyError as exc:
|
|
raise HTTPException(404, "Запись не найдена") from exc
|
|
except ValueError as exc:
|
|
raise HTTPException(400, str(exc)) from exc
|
|
return out
|