J.B-Lin commited on
Commit
694082c
·
1 Parent(s): f21da80

feat: 实现 render_three_day_deficit_section HTML渲染函数,与营养报告统一UI风格

Browse files
Files changed (1) hide show
  1. utils.py +218 -0
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
  # ============================================================