Spaces:
Runtime error
Runtime error
| """ | |
| PregoPal - 饮食记录模块 | |
| ======================== | |
| 记录孕妇的饮食习惯,同时保存为 JSON(结构化)+ Markdown(可读)。 | |
| 数据流向: | |
| MiniCPM-o 回复 (含 [DIET_RECORD] 标记) → 正则解析 → JSON + Markdown | |
| 当前:手动表单输入 | |
| 后续:AI 对话中自动提取 [DIET_RECORD] 标记 | |
| """ | |
| import json | |
| import datetime | |
| import hashlib | |
| import re | |
| from pathlib import Path | |
| from config import DIET_LOG_FILE, LOGS_DIR, DIET_LOG_SCHEMA_VERSION | |
| class DietLogger: | |
| """记录孕妇的饮食习惯并保存为 Markdown""" | |
| def __init__(self): | |
| self.log_file = DIET_LOG_FILE | |
| self.logs = self._load_logs() | |
| def _load_logs(self): | |
| """加载饮食记录""" | |
| if self.log_file.exists(): | |
| with open(self.log_file, 'r', encoding='utf-8') as f: | |
| return json.load(f) | |
| return {"schema_version": DIET_LOG_SCHEMA_VERSION, "records": []} | |
| def _save_logs(self): | |
| """保存饮食记录""" | |
| with open(self.log_file, 'w', encoding='utf-8') as f: | |
| json.dump(self.logs, f, ensure_ascii=False, indent=2) | |
| def add_record(self, member_name: str, member_relation: str, date: str, | |
| meals: dict, mood: str = "", notes: str = ""): | |
| """添加一条饮食记录""" | |
| record = { | |
| "id": hashlib.md5(f"{date}_{member_name}_{datetime.datetime.now()}".encode()).hexdigest()[:8], | |
| "member_name": member_name, | |
| "member_relation": member_relation, | |
| "date": date, | |
| "meals": meals, | |
| "mood": mood, | |
| "notes": notes, | |
| "extensions": {}, # 预留扩展字段 | |
| "created_at": datetime.datetime.now().isoformat() | |
| } | |
| self.logs["records"].append(record) | |
| self._save_logs() | |
| # 同时生成 Markdown 文件 | |
| md_path = self._generate_markdown(record) | |
| return record, md_path | |
| def _generate_markdown(self, record: dict) -> Path: | |
| """生成 Markdown 格式的饮食日志""" | |
| date_str = record["date"] | |
| md_filename = LOGS_DIR / f"饮食日志_{date_str}.md" | |
| content = f"""# 🥗 孕期饮食日志 | |
| ## 📋 基本信息 | |
| - **日期**: {date_str} | |
| - **记录人**: {record['member_relation']} - {record['member_name']} | |
| - **记录时间**: {record['created_at'][:19]} | |
| ## 🍽️ 今日饮食记录 | |
| """ | |
| for meal_time, food in record["meals"].items(): | |
| content += f"### {meal_time}\n- {food}\n\n" | |
| if record["mood"]: | |
| content += f"## 😊 今日心情\n{record['mood']}\n\n" | |
| if record["notes"]: | |
| content += f"## 📝 备注\n{record['notes']}\n\n" | |
| content += """--- | |
| *由 PregoPal 自动生成* | |
| """ | |
| with open(md_filename, 'w', encoding='utf-8') as f: | |
| f.write(content) | |
| return md_filename | |
| def get_recent_records(self, days: int = 7) -> list: | |
| """获取最近几天的记录""" | |
| today = datetime.date.today() | |
| cutoff = today - datetime.timedelta(days=days) | |
| recent = [] | |
| for r in self.logs["records"]: | |
| try: | |
| record_date = datetime.date.fromisoformat(r["date"]) | |
| if record_date >= cutoff: | |
| recent.append(r) | |
| except ValueError: | |
| continue | |
| return sorted(recent, key=lambda x: x["date"], reverse=True) | |
| def get_all_markdown_files(self) -> list: | |
| """获取所有 Markdown 日志文件""" | |
| return sorted(LOGS_DIR.glob("*.md"), reverse=True) | |
| # ============================================================ | |
| # [DIET_RECORD] 标记解析(后续 AI 版本使用) | |
| # ============================================================ | |
| def parse_diet_record(text: str) -> dict | None: | |
| """ | |
| 从 AI 回复中解析 [DIET_RECORD] 标记 | |
| Args: | |
| text: AI 模型的回复文本 | |
| Returns: | |
| 解析出的饮食记录字典,或 None(未找到标记) | |
| 示例输入: | |
| [DIET_RECORD] | |
| 日期: 2026-06-07 | |
| 早餐: 全麦面包+鸡蛋+牛奶 | |
| 午餐: 清蒸鱼+糙米饭+炒时蔬 | |
| 晚餐: | |
| 加餐: 酸奶+坚果 | |
| 心情: 挺好 | |
| 备注: | |
| [/DIET_RECORD] | |
| """ | |
| pattern = r'\[DIET_RECORD\](.*?)\[/DIET_RECORD\]' | |
| match = re.search(pattern, text, re.DOTALL) | |
| if not match: | |
| return None | |
| content = match.group(1).strip() | |
| record = {"meals": {}, "mood": "", "notes": ""} | |
| for line in content.split('\n'): | |
| line = line.strip() | |
| if not line: | |
| continue | |
| if ':' in line: | |
| key, value = line.split(':', 1) | |
| key = key.strip() | |
| value = value.strip() | |
| if key == '日期': | |
| record['date'] = value | |
| elif key == '心情': | |
| record['mood'] = value | |
| elif key == '备注': | |
| record['notes'] = value | |
| elif key in ['早餐', '午餐', '晚餐', '加餐']: | |
| if value: # 只记录非空值 | |
| record['meals'][key] = value | |
| return record if record['meals'] else None | |