""" med_tracker.py — Medication CRUD with persistent JSON storage. Each record is scoped to a care profile via profile_id. """ from __future__ import annotations import json import os import uuid from datetime import datetime from typing import List, Dict, Any, Optional from config import MEDICATIONS_JSON def _load() -> List[Dict[str, Any]]: if MEDICATIONS_JSON.exists(): try: with open(MEDICATIONS_JSON, "r", encoding="utf-8") as f: return json.load(f) except Exception: return [] return [] def _save(meds: List[Dict[str, Any]]): MEDICATIONS_JSON.parent.mkdir(parents=True, exist_ok=True) with open(MEDICATIONS_JSON, "w", encoding="utf-8") as f: json.dump(meds, f, indent=2, ensure_ascii=False) f.flush() os.fsync(f.fileno()) def _new_id() -> str: return str(uuid.uuid4()) def _now() -> str: return datetime.now().isoformat(timespec="seconds") def _for_profile(meds: List[Dict[str, Any]], profile_id: Optional[str]) -> List[Dict[str, Any]]: if not profile_id: return [] return [m for m in meds if m.get("profile_id") == profile_id] def add_medication( name: str, dosage: str = "", frequency: str = "", status: str = "current", side_effects: str = "", personal_notes: str = "", category: str = "", profile_id: str = "", ) -> Dict[str, Any]: """Add a new medication for profile_id. status: current | past.""" if not profile_id: raise ValueError("No active care profile.") if not name.strip(): raise ValueError("Medication name is required.") status = status if status in ("current", "past") else "current" med = { "id": _new_id(), "profile_id": profile_id, "name": name.strip(), "dosage": dosage.strip(), "frequency": frequency.strip(), "status": status, "side_effects": side_effects.strip(), "personal_notes": personal_notes.strip(), "category": category.strip(), "created_at": _now(), "updated_at": _now(), } meds = _load() meds.append(med) _save(meds) return med def edit_medication( med_id: str, name: Optional[str] = None, dosage: Optional[str] = None, frequency: Optional[str] = None, status: Optional[str] = None, side_effects: Optional[str] = None, personal_notes: Optional[str] = None, category: Optional[str] = None, ) -> Optional[Dict[str, Any]]: meds = _load() for med in meds: if med["id"] == med_id: if name is not None: med["name"] = name.strip() if dosage is not None: med["dosage"] = dosage.strip() if frequency is not None: med["frequency"] = frequency.strip() if status is not None and status in ("current", "past"): med["status"] = status if side_effects is not None: med["side_effects"] = side_effects.strip() if personal_notes is not None: med["personal_notes"] = personal_notes.strip() if category is not None: med["category"] = category.strip() med["updated_at"] = _now() _save(meds) return med return None def delete_medication(med_id: str) -> bool: meds = _load() new_meds = [m for m in meds if m["id"] != med_id] if len(new_meds) == len(meds): return False _save(new_meds) return True def get_medications(filter: str = "all", profile_id: Optional[str] = None) -> List[Dict[str, Any]]: meds = _for_profile(_load(), profile_id) if filter in ("current", "past"): meds = [m for m in meds if m.get("status") == filter] return sorted(meds, key=lambda m: m.get("name", "").lower()) def get_medication_by_id(med_id: str) -> Optional[Dict[str, Any]]: for m in _load(): if m["id"] == med_id: return m return None def delete_for_profile(profile_id: str) -> int: meds = _load() kept = [m for m in meds if m.get("profile_id") != profile_id] deleted = len(meds) - len(kept) if deleted: _save(kept) return deleted