Spaces:
Sleeping
Sleeping
| """ | |
| export_tool.py β Assemble and export caregiver data summaries. | |
| Combines selected data (meds, appointments, food chart, and an optional | |
| document summary) into a formatted text block ready for copy, email, or print. | |
| """ | |
| from __future__ import annotations | |
| import textwrap | |
| from datetime import datetime | |
| from typing import Optional | |
| from config import load_patient_config | |
| from med_tracker import get_medications | |
| from appointment_tracker import get_upcoming | |
| from food_tracker import get_food | |
| from profiles import get_profile | |
| # ββ Core assembler ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def assemble_export( | |
| include_meds: bool = True, | |
| include_appointments: bool = True, | |
| include_food: bool = True, | |
| document_summary: Optional[str] = None, | |
| custom_note: Optional[str] = None, | |
| profile_id: Optional[str] = None, | |
| ) -> str: | |
| """ | |
| Assemble a formatted text export block. | |
| Returns a multi-line string suitable for copy-to-clipboard, email body, | |
| or print. | |
| """ | |
| cfg = load_patient_config() | |
| today = datetime.now().strftime("%B %d, %Y at %I:%M %p") | |
| profile = get_profile(profile_id) if profile_id else None | |
| patient_label = "" | |
| if profile: | |
| patient_label = profile.get("full_name") or profile.get("label") or "" | |
| lines = [] | |
| # ββ Header ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| lines.append("=" * 60) | |
| lines.append(" CAREGIVER SUMMARY REPORT") | |
| lines.append(f" Generated: {today}") | |
| if patient_label: | |
| lines.append(f" Patient: {patient_label}") | |
| elif cfg.get("patient_name"): | |
| lines.append(f" Patient: {cfg['patient_name']}") | |
| lines.append("=" * 60) | |
| lines.append("") | |
| # ββ Custom note ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if custom_note and custom_note.strip(): | |
| lines.append("NOTES") | |
| lines.append("-" * 40) | |
| lines.append(custom_note.strip()) | |
| lines.append("") | |
| # ββ Medications ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if include_meds: | |
| meds = get_medications(filter="current", profile_id=profile_id) | |
| lines.append("CURRENT MEDICATIONS") | |
| lines.append("-" * 40) | |
| if meds: | |
| for med in meds: | |
| parts = [med["name"]] | |
| if med.get("dosage"): | |
| parts.append(med["dosage"]) | |
| if med.get("frequency"): | |
| parts.append(f"({med['frequency']})") | |
| lines.append("β’ " + " ".join(parts)) | |
| if med.get("side_effects"): | |
| lines.append(f" β³ Side effects: {med['side_effects']}") | |
| if med.get("personal_notes"): | |
| lines.append(f" β³ Notes: {med['personal_notes']}") | |
| else: | |
| lines.append("No current medications on file.") | |
| lines.append("") | |
| # ββ Appointments βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if include_appointments: | |
| appts = get_upcoming(profile_id=profile_id) | |
| lines.append("UPCOMING APPOINTMENTS") | |
| lines.append("-" * 40) | |
| if appts: | |
| for appt in appts: | |
| date_str = appt.get("date", "") | |
| time_str = appt.get("time", "") | |
| when = date_str + (f" at {time_str}" if time_str else "") | |
| lines.append(f"β’ {appt['title']} β {when}") | |
| if appt.get("location"): | |
| lines.append(f" Location: {appt['location']}") | |
| if appt.get("notes"): | |
| lines.append(f" Notes: {appt['notes']}") | |
| else: | |
| lines.append("No upcoming appointments.") | |
| lines.append("") | |
| # ββ Food Chart βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if include_food: | |
| food_data = get_food(profile_id=profile_id) | |
| lines.append("FOOD CHART") | |
| lines.append("-" * 40) | |
| liked = food_data.get("liked_foods", []) | |
| disliked = food_data.get("disliked_foods", []) | |
| allergies = food_data.get("allergies", []) | |
| if allergies: | |
| lines.append("β Allergies:") | |
| for e in allergies: | |
| note_part = f" ({e['notes']})" if e.get("notes") else "" | |
| lines.append(f" β’ {e['name']}{note_part}") | |
| if liked: | |
| lines.append("β Liked Foods:") | |
| for e in liked: | |
| note_part = f" ({e['notes']})" if e.get("notes") else "" | |
| lines.append(f" β’ {e['name']}{note_part}") | |
| if disliked: | |
| lines.append("β Disliked Foods:") | |
| for e in disliked: | |
| note_part = f" ({e['notes']})" if e.get("notes") else "" | |
| lines.append(f" β’ {e['name']}{note_part}") | |
| if not allergies and not liked and not disliked: | |
| lines.append("No food data on file.") | |
| lines.append("") | |
| # ββ Document Summary βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if document_summary and document_summary.strip(): | |
| lines.append("DOCUMENT SUMMARY") | |
| lines.append("-" * 40) | |
| # Word-wrap long lines | |
| for para in document_summary.strip().split("\n"): | |
| wrapped = textwrap.fill(para, width=58) if len(para) > 58 else para | |
| lines.append(wrapped) | |
| lines.append("") | |
| # ββ Footer βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| lines.append("=" * 60) | |
| lines.append("Built for the Build Small Hackathon β Backyard AI Track") | |
| lines.append("All data is stored locally. Nothing leaves this device.") | |
| lines.append("=" * 60) | |
| return "\n".join(lines) | |
| def build_mailto_link( | |
| to: str = "", | |
| subject: str = "Caregiver Summary Report", | |
| body: str = "", | |
| ) -> str: | |
| """ | |
| Build a mailto: URL with pre-filled subject and body. | |
| The body is URL-encoded so it works in href attributes. | |
| """ | |
| import urllib.parse | |
| encoded_body = urllib.parse.quote(body) | |
| encoded_subject = urllib.parse.quote(subject) | |
| return f"mailto:{to}?subject={encoded_subject}&body={encoded_body}" | |