148 lines
4.8 KiB
Python
148 lines
4.8 KiB
Python
import logging
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from app.middleware.auth import get_current_user
|
|
from app.models.analysis import (
|
|
Analysis,
|
|
CategoryInterpretation,
|
|
IndicatorInterpretation,
|
|
ActionItem,
|
|
)
|
|
from app.models.report import Report
|
|
from app.models.user import User
|
|
from app.services.ai_service import analyze_report
|
|
from app.routers.calendar import link_report_to_checkup
|
|
from app.utils.helpers import parse_object_id
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/{report_id}/generate")
|
|
async def generate_analysis(
|
|
report_id: str, user: User = Depends(get_current_user)
|
|
):
|
|
"""生成 AI 解读"""
|
|
oid = parse_object_id(report_id)
|
|
report = await Report.get(oid)
|
|
if not report or report.user_id != user.id:
|
|
raise HTTPException(status_code=404, detail="报告不存在")
|
|
|
|
# Check if analysis already exists
|
|
existing = await Analysis.find_one(Analysis.report_id == report.id)
|
|
if existing:
|
|
return _analysis_to_dict(existing)
|
|
|
|
# Call Claude API
|
|
try:
|
|
result = await analyze_report(report, user)
|
|
except Exception:
|
|
logger.exception("AI 解读失败 report_id=%s", report_id)
|
|
raise HTTPException(status_code=502, detail="AI 解读服务暂时不可用,请稍后重试")
|
|
|
|
# Save analysis
|
|
categories = []
|
|
for cat in result.get("categories", []):
|
|
indicators = [
|
|
IndicatorInterpretation(**ind) for ind in cat.get("indicators", [])
|
|
]
|
|
categories.append(
|
|
CategoryInterpretation(
|
|
category=cat.get("category", ""),
|
|
status=cat.get("status", "normal"),
|
|
indicators=indicators,
|
|
)
|
|
)
|
|
|
|
action_items = [ActionItem(**a) for a in result.get("action_items", [])]
|
|
|
|
analysis = Analysis(
|
|
report_id=report.id,
|
|
user_id=user.id,
|
|
overall_status=result.get("overall_status", "normal"),
|
|
risk_level=result.get("risk_level", "low"),
|
|
overall_summary=result.get("overall_summary", ""),
|
|
emotional_summary=result.get("emotional_summary", ""),
|
|
categories=categories,
|
|
action_items=action_items,
|
|
tips=result.get("tips", []),
|
|
)
|
|
await analysis.insert()
|
|
|
|
# Update report status
|
|
report.status = "analyzed"
|
|
report.overall_status = analysis.overall_status
|
|
report.updated_at = datetime.now(timezone.utc)
|
|
await report.save()
|
|
|
|
# Auto-link report to the nearest calendar checkup
|
|
if report.pregnancy_week:
|
|
try:
|
|
await link_report_to_checkup(user.id, str(report.id), report.pregnancy_week)
|
|
except Exception:
|
|
logger.warning("自动关联日历失败 report_id=%s", report_id)
|
|
|
|
return _analysis_to_dict(analysis)
|
|
|
|
|
|
@router.get("/{report_id}")
|
|
async def get_analysis(report_id: str, user: User = Depends(get_current_user)):
|
|
"""获取报告的 AI 解读结果"""
|
|
oid = parse_object_id(report_id)
|
|
report = await Report.get(oid)
|
|
if not report or report.user_id != user.id:
|
|
raise HTTPException(status_code=404, detail="报告不存在")
|
|
|
|
analysis = await Analysis.find_one(Analysis.report_id == report.id)
|
|
if not analysis:
|
|
raise HTTPException(status_code=404, detail="暂无解读结果,请先生成")
|
|
|
|
return _analysis_to_dict(analysis)
|
|
|
|
|
|
def _analysis_to_dict(analysis: Analysis) -> dict:
|
|
return {
|
|
"id": str(analysis.id),
|
|
"report_id": str(analysis.report_id),
|
|
"overall_status": analysis.overall_status,
|
|
"risk_level": analysis.risk_level,
|
|
"overall_summary": analysis.overall_summary,
|
|
"emotional_summary": analysis.emotional_summary,
|
|
"categories": [
|
|
{
|
|
"category": cat.category,
|
|
"status": cat.status,
|
|
"indicators": [
|
|
{
|
|
"name": ind.name,
|
|
"nickname": ind.nickname,
|
|
"value": ind.value,
|
|
"unit": "",
|
|
"reference_range": "",
|
|
"status": ind.status,
|
|
"what_is_it": ind.what_is_it,
|
|
"baby_impact": ind.impact,
|
|
"suggestion": ind.suggestion,
|
|
"follow_up": ind.recheck_reminder or "",
|
|
}
|
|
for ind in cat.indicators
|
|
],
|
|
}
|
|
for cat in analysis.categories
|
|
],
|
|
"action_items": [
|
|
{
|
|
"priority": a.priority,
|
|
"title": a.content,
|
|
"description": a.category,
|
|
"timeframe": "",
|
|
}
|
|
for a in analysis.action_items
|
|
],
|
|
"tips": analysis.tips,
|
|
"created_at": analysis.created_at.isoformat(),
|
|
}
|