468804494d
plan ds-processing-ui-6an (시안 choice 채택: 안2 1차 + 안5/6 지원): - GET /api/queue/overview — 머신(GPU/맥미니/맥북) 귀속 라이브 집계 5쿼리, 마이그레이션 0. summarize 풀 완료 실적은 documents.ai_model_version 조인으로 맥북/맥미니 분리, 보류(deferred_until)=맥북 카드 귀속, state=active/deferred/idle. raw 모델명 비노출 - 홈: 처리 머신 보드(3열 카드 + 지금 처리 중 제목) + ETA 라인(유입 우세 시 null 명시), 기존 stage 테이블은 details 접힘으로 강등 (구조 개편) - 전 페이지: 상태 스트립(처리중·대기·실패·맥북 칩) + 우측 드로어(QueueDrawer, dialog a11y) — 공유 60s 폴링 store, 경량 fetch(401 강제 logout 부수효과 회피) - tests: 판정부 30건 (귀속/풀 분리/state 9케이스/ETA 경계/trend 버킷/계약 shape) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
"""처리 머신 보드 API — GET /api/queue/overview (plan ds-processing-ui-6an).
|
|
|
|
홈 stage 평면 테이블을 "머신 관점 보드(누가 일하나)"로 — 집계 로직은
|
|
services/queue_overview.py (순수 판정부 분리). 응답 스키마는 FE 와 계약 고정.
|
|
응답에 raw 모델명 노출 금지 — 머신 label 만.
|
|
"""
|
|
|
|
from typing import Annotated, Literal
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from core.auth import get_current_user
|
|
from core.database import get_session
|
|
from models.user import User
|
|
from services.queue_overview import build_overview
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class CurrentItem(BaseModel):
|
|
"""머신이 지금 처리 중인 문서 (최대 2건)."""
|
|
document_id: int
|
|
title: str
|
|
stage: str
|
|
|
|
|
|
class MachineCard(BaseModel):
|
|
"""머신 카드 — stage 귀속 합산 + 완료 실적(summarize 는 풀 분리) + state."""
|
|
key: Literal["gpu", "macmini", "macbook"]
|
|
label: str
|
|
state: Literal["active", "deferred", "idle"]
|
|
stages: list[str]
|
|
pending: int
|
|
processing: int
|
|
failed: int
|
|
done_1h: int
|
|
done_today: int
|
|
deferred_pending: int
|
|
current: list[CurrentItem]
|
|
|
|
|
|
class SummarizeEta(BaseModel):
|
|
"""summarize 풀 ETA — done > inflow 일 때만 eta_minutes 산출."""
|
|
pending: int
|
|
done_rate_1h: int
|
|
inflow_rate_1h: int
|
|
eta_minutes: int | None
|
|
|
|
|
|
class TrendBucket(BaseModel):
|
|
"""summarize 24h 추이 버킷 — hour 는 KST "HH:00" 라벨."""
|
|
hour: str
|
|
inflow: int
|
|
done: int
|
|
|
|
|
|
class Totals(BaseModel):
|
|
"""전 stage 합계."""
|
|
pending: int
|
|
processing: int
|
|
failed: int
|
|
|
|
|
|
class QueueOverviewResponse(BaseModel):
|
|
machines: list[MachineCard]
|
|
summarize_eta: SummarizeEta
|
|
trend_24h: list[TrendBucket]
|
|
totals: Totals
|
|
|
|
|
|
@router.get("/overview", response_model=QueueOverviewResponse)
|
|
async def get_queue_overview(
|
|
user: Annotated[User, Depends(get_current_user)],
|
|
session: Annotated[AsyncSession, Depends(get_session)],
|
|
):
|
|
"""머신 관점 처리 보드 + summarize ETA 집계 (라이브 계산, 신규 테이블 0)"""
|
|
return QueueOverviewResponse.model_validate(await build_overview(session))
|