""" Modular section renderers for dynamic report generation. Each function renders one type of report section as HTML. """ from typing import Dict, Any, Optional from schemas.analysis_intent import AnalysisIntent from schemas.decision_result import RegulatoryDecisionResult def render_executive_summary( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render executive summary with LLM-generated narrative.""" summary_text = explanations.get("executive_summary", "分析结果待生成") focus = config.get("focus", "general") # Add focus-specific highlights highlights = "" if focus == "top_batch_recommendation" and result and result.batch_ranking: top_batch = result.batch_ranking[0] highlights = f"""
🏆 推荐批次: {top_batch.batch_name}
评分: {top_batch.score}
理由: {top_batch.reason}
""" return f"""
核心结论

{summary_text}

{highlights}
""" def render_ranking_table( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render batch ranking table.""" if not result or not result.batch_ranking: return "" show_scores = config.get("show_scores", True) rows = [] for item in result.batch_ranking: rank_badge = f'推荐' if item.rank == 1 else "" score_cell = f"{item.score}" if show_scores else "" rows.append(f""" {item.rank} {rank_badge} {item.batch_name} {score_cell} {item.reason} """) score_header = "评分" if show_scores else "" return f"""
批次排名
{score_header} {''.join(rows)}
排名 批次名称评价理由
""" def render_prediction_table( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render shelf-life predictions table.""" if not result or not result.predictions: return "" # Only show valid predictions valid_preds = { tp: pred for tp, pred in result.predictions.items() if pred.is_valid } if not valid_preds: return render_data_quality_warning( intent, result, explanations, {"message": "由于数据不足或外推限制,无法生成有效的货架期预测。"} ) rows = [] for timepoint, pred in valid_preds.items(): status = "合规" if pred.is_compliant() else "超标" status_class = "badge-success" if pred.is_compliant() else "badge-warning" rows.append(f""" {timepoint} {pred.point_estimate:.2f}% {pred.CI_lower:.2f}% - {pred.CI_upper:.2f}% {status} """) return f"""
货架期预测
{''.join(rows)}
时间点 点预测 95% 置信区间 状态
""" def render_kinetic_modeling( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render kinetic modeling results.""" if not result or not result.kinetic_fits: return "" # Only show fits with meaningful data (not None) rows = [] for condition, fit in result.kinetic_fits.items(): # Skip if no data if fit.k is None and fit.R2 is None: continue k_str = f"{fit.k:.4f}" if fit.k is not None else "N/A" r2_str = f"{fit.R2:.4f}" if fit.R2 is not None else "N/A" se_str = f"{fit.SE_k:.3f}" if fit.SE_k is not None else "N/A" # Confidence based on R² if fit.R2 is not None: if fit.R2 >= 0.95: confidence = "高" elif fit.R2 >= 0.8: confidence = "中" else: confidence = "低" else: confidence = "低" rows.append(f""" {condition} {fit.model_type} {k_str} {r2_str} {se_str} {confidence} """) if not rows: return "" return f"""
动力学建模结果
模型: 零级降解动力学 y(t) = y₀ + k·t
{''.join(rows)}
条件 模型 k (%/月) SE(k) 置信度
""" def render_trend_visualization( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render trend comparison chart (placeholder - reuse existing SVG logic).""" # This would integrate with existing chart generation # For now, return a placeholder return f"""
趋势对比可视化
图表生成中...(整合现有 SVG 绘图逻辑)
""" def render_data_quality_warning( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render data quality warning.""" message = config.get("message", "数据质量存在限制,请谨慎使用分析结果。") return f"""
⚠️ 数据质量提示:
{message}
""" def render_recommendations( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render actionable recommendations.""" recs_text = explanations.get("recommendations", "建议进行进一步的稳定性研究。") return f"""
行动建议
{recs_text}
""" def render_regulatory_compliance( intent: Optional[AnalysisIntent], result: Optional[RegulatoryDecisionResult], explanations: Dict[str, str], config: Dict[str, Any] ) -> str: """Render regulatory compliance statement.""" return f"""
法规合规性
ICH Q1E: 本报告遵循 ICH Q1E 稳定性数据评价指导原则。

本分析符合以下法规要求:

""" # Registry of all section renderers SECTION_RENDERERS = { "executive_summary": render_executive_summary, "ranking_table": render_ranking_table, "prediction_table": render_prediction_table, "kinetic_modeling": render_kinetic_modeling, "trend_visualization": render_trend_visualization, "data_quality_warning": render_data_quality_warning, "recommendations": render_recommendations, "regulatory_compliance": render_regulatory_compliance, }