Spaces:
Runtime error
Runtime error
J.B-Lin commited on
Commit ·
694082c
1
Parent(s): f21da80
feat: 实现 render_three_day_deficit_section HTML渲染函数,与营养报告统一UI风格
Browse files
utils.py
CHANGED
|
@@ -102,6 +102,20 @@ ZH = {
|
|
| 102 |
"report_diversity": "饮食多样性",
|
| 103 |
"report_suggestions": "饮食建议",
|
| 104 |
"report_days": "近{days}天",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
}
|
| 106 |
|
| 107 |
EN = {
|
|
@@ -154,6 +168,20 @@ EN = {
|
|
| 154 |
"report_diversity": "Diet Diversity",
|
| 155 |
"report_suggestions": "Dietary Suggestions",
|
| 156 |
"report_days": "Last {days} days",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
}
|
| 158 |
|
| 159 |
|
|
@@ -429,6 +457,196 @@ def render_nutrition_report_html(analysis: dict, days: int, lang="zh") -> str:
|
|
| 429 |
</div>"""
|
| 430 |
|
| 431 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
# ============================================================
|
| 433 |
# 自定义 CSS 样式
|
| 434 |
# ============================================================
|
|
|
|
| 102 |
"report_diversity": "饮食多样性",
|
| 103 |
"report_suggestions": "饮食建议",
|
| 104 |
"report_days": "近{days}天",
|
| 105 |
+
"three_day_title": "📊 三天深度营养分析",
|
| 106 |
+
"three_day_subtitle": "基于近三日饮食趋势的持续缺失追踪",
|
| 107 |
+
"three_day_days": "分析了 {days} 天数据",
|
| 108 |
+
"three_day_duration": "持续不足的营养素",
|
| 109 |
+
"three_day_deficit": "缺失风险",
|
| 110 |
+
"three_day_attention": "需关注",
|
| 111 |
+
"three_day_good": "摄入达标",
|
| 112 |
+
"three_day_suggestions": "🍽️ 食材补充建议",
|
| 113 |
+
"three_day_summary": "📋 总结",
|
| 114 |
+
"three_day_no_data": "近三天无饮食记录,跳过深度分析",
|
| 115 |
+
"three_day_all_good": "✅ 各营养素摄入基本达标,继续保持!",
|
| 116 |
+
"three_day_def_critical": "❌",
|
| 117 |
+
"three_day_def_warning": "⚠️",
|
| 118 |
+
"three_day_def_ok": "✅",
|
| 119 |
}
|
| 120 |
|
| 121 |
EN = {
|
|
|
|
| 168 |
"report_diversity": "Diet Diversity",
|
| 169 |
"report_suggestions": "Dietary Suggestions",
|
| 170 |
"report_days": "Last {days} days",
|
| 171 |
+
"three_day_title": "📊 3-Day Deep Analysis",
|
| 172 |
+
"three_day_subtitle": "Continuous deficiency tracking based on recent diet trends",
|
| 173 |
+
"three_day_days": "Analyzed {days} days of data",
|
| 174 |
+
"three_day_duration": "Persistent Deficits",
|
| 175 |
+
"three_day_deficit": "Deficit Risk",
|
| 176 |
+
"three_day_attention": "Needs Attention",
|
| 177 |
+
"three_day_good": "Adequate Intake",
|
| 178 |
+
"three_day_suggestions": "🍽️ Food Suggestions",
|
| 179 |
+
"three_day_summary": "📋 Summary",
|
| 180 |
+
"three_day_no_data": "No diet records in the last 3 days, skipping deep analysis",
|
| 181 |
+
"three_day_all_good": "✅ All nutrients adequate, keep it up!",
|
| 182 |
+
"three_day_def_critical": "❌",
|
| 183 |
+
"three_day_def_warning": "⚠️",
|
| 184 |
+
"three_day_def_ok": "✅",
|
| 185 |
}
|
| 186 |
|
| 187 |
|
|
|
|
| 457 |
</div>"""
|
| 458 |
|
| 459 |
|
| 460 |
+
# ============================================================
|
| 461 |
+
# 三天缺失深度分析 HTML(与营养报告统一视觉风格)
|
| 462 |
+
# ============================================================
|
| 463 |
+
|
| 464 |
+
def render_three_day_deficit_section(deficit_data: dict, lang="zh") -> str:
|
| 465 |
+
"""
|
| 466 |
+
渲染三天缺失深度分析板块,与营养报告使用同一套现代UI视觉语言。
|
| 467 |
+
|
| 468 |
+
Args:
|
| 469 |
+
deficit_data: 来自 ThreeDaySummaryPlugin 的数据字典,包含:
|
| 470 |
+
- "days_analyzed": int
|
| 471 |
+
- "persistent_deficits": list[str]
|
| 472 |
+
- "report": str (纯文本报告)
|
| 473 |
+
lang: 语言标识 "zh" / "en"
|
| 474 |
+
|
| 475 |
+
Returns:
|
| 476 |
+
str: 纯 HTML 字符串,可直接拼接到营养报告之后
|
| 477 |
+
"""
|
| 478 |
+
if not deficit_data:
|
| 479 |
+
return ""
|
| 480 |
+
|
| 481 |
+
days_analyzed = deficit_data.get("days_analyzed", 0)
|
| 482 |
+
persistent_deficits = deficit_data.get("persistent_deficits", [])
|
| 483 |
+
report_text = deficit_data.get("report", "")
|
| 484 |
+
|
| 485 |
+
# 如果没有数据则显示提示
|
| 486 |
+
if deficit_data.get("status") == "no_data" or days_analyzed == 0:
|
| 487 |
+
no_data_msg = t("three_day_no_data", lang)
|
| 488 |
+
return f"""
|
| 489 |
+
<div style="margin-top:20px;padding:20px;text-align:center;color:#aaa;font-size:15px;
|
| 490 |
+
background:#fff;border-radius:14px;border:1px solid #f0f0f0;
|
| 491 |
+
box-shadow:0 1px 6px rgba(0,0,0,0.04);">
|
| 492 |
+
{no_data_msg}
|
| 493 |
+
</div>"""
|
| 494 |
+
|
| 495 |
+
# ——— 构建缺失营养素的卡片 ———
|
| 496 |
+
deficit_cards = ""
|
| 497 |
+
for nutrient in persistent_deficits:
|
| 498 |
+
deficit_cards += f"""
|
| 499 |
+
<div style="background:#fff;border-radius:14px;padding:14px 16px;
|
| 500 |
+
box-shadow:0 1px 6px rgba(0,0,0,0.04);border:1px solid #FFCDD2;
|
| 501 |
+
transition:all 0.2s;display:flex;align-items:center;gap:12px;">
|
| 502 |
+
<div style="font-size:24px;min-width:36px;text-align:center;">⚠️</div>
|
| 503 |
+
<div>
|
| 504 |
+
<div style="font-weight:600;font-size:15px;color:#C62828;">{nutrient}</div>
|
| 505 |
+
<div style="font-size:12px;color:#888;margin-top:2px;">{t("three_day_deficit", lang)}</div>
|
| 506 |
+
</div>
|
| 507 |
+
</div>"""
|
| 508 |
+
|
| 509 |
+
# 如果没有任何缺失,显示全部达标
|
| 510 |
+
if not deficit_cards:
|
| 511 |
+
all_good = t("three_day_all_good", lang)
|
| 512 |
+
deficit_cards = f"""
|
| 513 |
+
<div style="background:#fff;border-radius:14px;padding:14px 16px;
|
| 514 |
+
box-shadow:0 1px 6px rgba(0,0,0,0.04);border:1px solid #C8E6C9;
|
| 515 |
+
display:flex;align-items:center;gap:12px;">
|
| 516 |
+
<div style="font-size:24px;">🎉</div>
|
| 517 |
+
<div style="font-weight:500;font-size:15px;color:#2E7D32;">{all_good}</div>
|
| 518 |
+
</div>"""
|
| 519 |
+
|
| 520 |
+
# ——— 构建食材建议卡片 ———
|
| 521 |
+
from modules.nutrition_standards import DRIsParser
|
| 522 |
+
suggestion_items = ""
|
| 523 |
+
for nutrient in persistent_deficits[:3]:
|
| 524 |
+
foods = DRIsParser.RICH_FOODS.get(nutrient, [])
|
| 525 |
+
if foods:
|
| 526 |
+
food_tags = "".join(
|
| 527 |
+
f'<span style="background:#E8F5E9;color:#2E7D32;padding:3px 12px;'
|
| 528 |
+
f'border-radius:99px;font-size:13px;white-space:nowrap;">{f}</span>'
|
| 529 |
+
for f in foods[:4]
|
| 530 |
+
)
|
| 531 |
+
suggestion_items += f"""
|
| 532 |
+
<div style="margin-bottom:12px;">
|
| 533 |
+
<div style="font-weight:600;font-size:14px;color:#E91E63;margin-bottom:6px;">
|
| 534 |
+
💪 {nutrient}
|
| 535 |
+
</div>
|
| 536 |
+
<div style="display:flex;flex-wrap:wrap;gap:6px;">
|
| 537 |
+
{food_tags}
|
| 538 |
+
</div>
|
| 539 |
+
</div>"""
|
| 540 |
+
|
| 541 |
+
# 如果 persistent_deficits 为空,显示鼓励信息
|
| 542 |
+
if not suggestion_items:
|
| 543 |
+
suggestion_items = f"""
|
| 544 |
+
<div style="font-size:14px;color:#888;text-align:center;padding:8px 0;">
|
| 545 |
+
🎉 {t("three_day_all_good", lang)}
|
| 546 |
+
</div>"""
|
| 547 |
+
|
| 548 |
+
# ——— 构建纯文本报告(如果存在) ———
|
| 549 |
+
text_report_html = ""
|
| 550 |
+
if report_text:
|
| 551 |
+
# 将纯文本按行转换为 HTML(保留颜色标识)
|
| 552 |
+
lines = report_text.split("\n")
|
| 553 |
+
formatted_lines = []
|
| 554 |
+
for line in lines:
|
| 555 |
+
line = line.strip()
|
| 556 |
+
if not line:
|
| 557 |
+
formatted_lines.append('<div style="height:4px;"></div>')
|
| 558 |
+
elif line.startswith("📊") or line.startswith("="):
|
| 559 |
+
# 标题行 + 分隔线
|
| 560 |
+
continue
|
| 561 |
+
elif line.startswith("❌"):
|
| 562 |
+
formatted_lines.append(
|
| 563 |
+
f'<div style="font-size:13px;color:#C62828;padding:2px 0;">{line}</div>'
|
| 564 |
+
)
|
| 565 |
+
elif line.startswith("⚠️"):
|
| 566 |
+
formatted_lines.append(
|
| 567 |
+
f'<div style="font-size:13px;color:#E65100;padding:2px 0;">{line}</div>'
|
| 568 |
+
)
|
| 569 |
+
elif line.startswith("✅"):
|
| 570 |
+
formatted_lines.append(
|
| 571 |
+
f'<div style="font-size:13px;color:#2E7D32;padding:2px 0;">{line}</div>'
|
| 572 |
+
)
|
| 573 |
+
elif line.startswith("💡") or line.startswith("-"):
|
| 574 |
+
continue # 跳过建议部分(我们已有更好的UI展示)
|
| 575 |
+
else:
|
| 576 |
+
formatted_lines.append(
|
| 577 |
+
f'<div style="font-size:13px;color:#555;padding:2px 0;">{line}</div>'
|
| 578 |
+
)
|
| 579 |
+
if formatted_lines:
|
| 580 |
+
text_report_html = """
|
| 581 |
+
<div style="background:#FAFAFA;border-radius:12px;padding:12px 16px;
|
| 582 |
+
border:1px solid #f0f0f0;max-height:200px;overflow-y:auto;
|
| 583 |
+
font-family:'Courier New',monospace;">
|
| 584 |
+
""" + "".join(formatted_lines) + "</div>"
|
| 585 |
+
|
| 586 |
+
days_label = t("three_day_days", lang).format(days=days_analyzed)
|
| 587 |
+
|
| 588 |
+
# ——— 拼装最终 HTML ———
|
| 589 |
+
return f"""
|
| 590 |
+
<div style="margin-top:20px;font-family:system-ui,-apple-system,sans-serif;max-width:100%;">
|
| 591 |
+
|
| 592 |
+
<!-- ★ Header: 与营养报告统一的渐变风格 -->
|
| 593 |
+
<div style="background:linear-gradient(135deg,#E8F5E9,#F1F8E9);border-radius:18px;padding:20px 24px;margin-bottom:16px;">
|
| 594 |
+
<div style="display:flex;justify-content:space-between;align-items:center;">
|
| 595 |
+
<div>
|
| 596 |
+
<div style="font-size:20px;font-weight:700;color:#1B5E20;">{t("three_day_title", lang)}</div>
|
| 597 |
+
<div style="color:#888;font-size:13px;margin-top:4px;">{t("three_day_subtitle", lang)}</div>
|
| 598 |
+
</div>
|
| 599 |
+
<div style="text-align:right;">
|
| 600 |
+
<div style="font-size:32px;font-weight:800;color:#4CAF50;">{days_analyzed}</div>
|
| 601 |
+
<div style="font-size:12px;color:#888;">{days_label.split(" ")[-1] if " " in days_label else ""}</div>
|
| 602 |
+
</div>
|
| 603 |
+
</div>
|
| 604 |
+
<div style="margin-top:8px;font-size:13px;color:#555;">{days_label}</div>
|
| 605 |
+
</div>
|
| 606 |
+
|
| 607 |
+
<!-- 双列布局:左侧缺失营养素 | 右侧食材建议 -->
|
| 608 |
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;">
|
| 609 |
+
|
| 610 |
+
<!-- 左侧:持续不足的营养素 -->
|
| 611 |
+
<div style="background:#fff;border-radius:14px;padding:14px 16px;
|
| 612 |
+
box-shadow:0 1px 6px rgba(0,0,0,0.04);border:1px solid #f0f0f0;">
|
| 613 |
+
<div style="font-weight:600;font-size:14px;margin-bottom:10px;color:#333;">
|
| 614 |
+
⚠️ {t("three_day_duration", lang)}
|
| 615 |
+
</div>
|
| 616 |
+
<div style="display:flex;flex-direction:column;gap:8px;">
|
| 617 |
+
{deficit_cards}
|
| 618 |
+
</div>
|
| 619 |
+
</div>
|
| 620 |
+
|
| 621 |
+
<!-- 右侧:食材补充建议 -->
|
| 622 |
+
<div style="background:linear-gradient(135deg,#FFF8E1,#FFFDE7);border-radius:14px;padding:14px 16px;
|
| 623 |
+
border:1px solid #FFE082;">
|
| 624 |
+
<div style="font-weight:600;font-size:14px;margin-bottom:10px;color:#E65100;">
|
| 625 |
+
{t("three_day_suggestions", lang)}
|
| 626 |
+
</div>
|
| 627 |
+
{suggestion_items}
|
| 628 |
+
</div>
|
| 629 |
+
|
| 630 |
+
</div>
|
| 631 |
+
|
| 632 |
+
<!-- 详细数据(如果存在) -->
|
| 633 |
+
{f'''
|
| 634 |
+
<div style="margin-bottom:12px;">
|
| 635 |
+
<details style="background:#fff;border-radius:14px;padding:14px 16px;
|
| 636 |
+
box-shadow:0 1px 6px rgba(0,0,0,0.04);border:1px solid #f0f0f0;">
|
| 637 |
+
<summary style="font-weight:600;font-size:14px;color:#666;cursor:pointer;">
|
| 638 |
+
📋 {t("three_day_summary", lang)}
|
| 639 |
+
</summary>
|
| 640 |
+
<div style="margin-top:10px;">
|
| 641 |
+
{text_report_html}
|
| 642 |
+
</div>
|
| 643 |
+
</details>
|
| 644 |
+
</div>
|
| 645 |
+
''' if text_report_html else ''}
|
| 646 |
+
|
| 647 |
+
</div>"""
|
| 648 |
+
|
| 649 |
+
|
| 650 |
# ============================================================
|
| 651 |
# 自定义 CSS 样式
|
| 652 |
# ============================================================
|