"""독립 지원 — 자율 주간 보고서 생성·발송.""" from __future__ import annotations import logging from datetime import datetime, timedelta from fastapi import APIRouter, BackgroundTasks, Depends from sqlalchemy import select, func as sqlfunc, desc from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role from database import get_db from models import User, SelfReport, HealthCheckResult, AutonomousAction, IndependenceScore logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/self-report", tags=["독립지원-보고서"]) async def _generate_report(period_label: str = None) -> dict: """주간 자립도 보고서 데이터 수집.""" since = datetime.utcnow() - timedelta(days=7) period = period_label or f"{since.strftime('%Y-%m-%d')}~{datetime.utcnow().strftime('%Y-%m-%d')}" from database import AsyncSessionLocal async with AsyncSessionLocal() as db: # SR 통계 try: from models import ServiceRequest sr_total = (await db.execute( select(sqlfunc.count()).select_from(ServiceRequest) .where(ServiceRequest.created_at >= since) )).scalar() or 0 sr_auto = (await db.execute( select(sqlfunc.count()).select_from(ServiceRequest) .where(ServiceRequest.created_at >= since, ServiceRequest.resolved_by_ai == True) )).scalar() or 0 except Exception: sr_total, sr_auto = 0, 0 # 건강검진 점수 (최근 7회 평균) hc_rows = await db.execute( select(HealthCheckResult).order_by(desc(HealthCheckResult.created_at)).limit(7) ) hc_list = hc_rows.scalars().all() health_score = (sum(h.passed / (h.total or 1) * 100 for h in hc_list) / len(hc_list)) if hc_list else 0.0 # 자동 수복 횟수 auto_heals = (await db.execute( select(sqlfunc.count()).select_from(AutonomousAction) .where(AutonomousAction.executed_at >= since, AutonomousAction.success == True) )).scalar() or 0 # 최신 자립도 score_row = await db.execute( select(IndependenceScore) .where(IndependenceScore.dimension == "overall") .order_by(desc(IndependenceScore.measured_at)).limit(1) ) latest_score = score_row.scalar_one_or_none() indep = latest_score.score if latest_score else 30.0 summary_lines = [ f"📊 주간 보고서 ({period})", f" SR 처리: {sr_total}건 (AI 자동처리: {sr_auto}건, {round(sr_auto/sr_total*100) if sr_total else 0}%)", f" 건강점수: {health_score:.1f}%", f" 자가수복: {auto_heals}회", f" 자립도: {indep:.1f}%", ] summary = "\n".join(summary_lines) report = SelfReport( period=period, sr_total=sr_total, sr_auto_handled=sr_auto, health_score=health_score, auto_heals=auto_heals, incidents=0, summary=summary, created_at=datetime.utcnow(), ) db.add(report) await db.commit() await db.refresh(report) return {"id": report.id, "period": period, "summary": summary, "sr_total": sr_total, "sr_auto": sr_auto, "health_score": health_score, "auto_heals": auto_heals, "independence": indep} async def _send_report(period_label: str = None): data = await _generate_report(period_label) try: import httpx async with httpx.AsyncClient(timeout=5) as c: await c.post("http://127.0.0.1:9001/api/messenger/webhook", json={ "event": "weekly_self_report", "room": "ops", "message": data["summary"], }) except Exception as e: logger.warning("보고서 발송 실패: %s", e) @router.post("/generate") async def generate_report( background_tasks: BackgroundTasks, user: User = Depends(require_admin_role), ): """주간 보고서 즉시 생성·발송.""" background_tasks.add_task(_send_report) return {"ok": True, "message": "보고서 생성 + 메신저 발송 시작됨 (백그라운드)"} @router.get("/list") async def list_reports( limit: int = 10, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): """보고서 이력.""" rows = await db.execute( select(SelfReport).order_by(desc(SelfReport.created_at)).limit(limit) ) return [{ "id": r.id, "period": r.period, "sr_total": r.sr_total, "sr_auto_handled": r.sr_auto_handled, "health_score": r.health_score, "auto_heals": r.auto_heals, "summary": r.summary, "created_at": r.created_at, } for r in rows.scalars().all()] @router.get("/latest") async def latest_report( db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): """최신 보고서.""" row = await db.execute( select(SelfReport).order_by(desc(SelfReport.created_at)).limit(1) ) r = row.scalar_one_or_none() if not r: return {"message": "보고서 없음. POST /api/self-report/generate 실행"} return {"id": r.id, "period": r.period, "summary": r.summary, "sr_total": r.sr_total, "sr_auto_handled": r.sr_auto_handled, "health_score": r.health_score, "auto_heals": r.auto_heals, "created_at": r.created_at}