| from fastapi import APIRouter, Depends, Response |
| from utils.auth_utils import get_current_user |
| from db import db |
| from datetime import datetime, timedelta |
| import io |
| import pandas as pd |
| import matplotlib.pyplot as plt |
| from reportlab.lib.pagesizes import letter |
| from reportlab.pdfgen import canvas |
| from reportlab.lib.utils import ImageReader |
|
|
| router = APIRouter(prefix="/analysis", tags=["analysis"]) |
|
|
|
|
| from fastapi import Query |
|
|
| @router.get("/medicine-summary") |
| async def medicine_summary( |
| current_user: dict = Depends(get_current_user), |
| days: int = Query(30, description="Number of days to include in analytics (default 30)") |
| ): |
| from datetime import datetime, timedelta, timezone |
| now = datetime.now(timezone.utc) |
| since = now - timedelta(days=days) |
| medicines = await db.medicines.find({"user_id": str(current_user["_id"])}).to_list(length=100) |
| |
| medicine_adherence = [] |
| total_taken = 0 |
| total_missed = 0 |
| time_analysis_map = {} |
| adherence_trend_map = {} |
| for med in medicines: |
| taken = 0 |
| missed = 0 |
| for h in med.get("history", []): |
| try: |
| hist_dt = datetime.fromisoformat(h["time"]) |
| |
| if hist_dt.tzinfo is not None: |
| hist_dt = hist_dt.astimezone(timezone.utc) |
| else: |
| hist_dt = hist_dt.replace(tzinfo=timezone.utc) |
| except Exception: |
| continue |
| if hist_dt < since: |
| continue |
| status = h["status"] |
| |
| if status == "absent": |
| missed += 1 |
| total_missed += 1 |
| elif status == "taken": |
| taken += 1 |
| total_taken += 1 |
| else: |
| continue |
| |
| day = h["time"][:10] |
| if day not in adherence_trend_map: |
| adherence_trend_map[day] = {"taken": 0, "missed": 0} |
| if status == "taken": |
| adherence_trend_map[day]["taken"] += 1 |
| elif status == "absent": |
| adherence_trend_map[day]["missed"] += 1 |
| |
| time_str = hist_dt.strftime("%H:%M") |
| if time_str not in time_analysis_map: |
| time_analysis_map[time_str] = {"taken": 0, "missed": 0} |
| if status == "taken": |
| time_analysis_map[time_str]["taken"] += 1 |
| elif status == "absent": |
| time_analysis_map[time_str]["missed"] += 1 |
| medicine_adherence.append({ |
| "name": med.get("name", ""), |
| "taken": taken, |
| "missed": missed |
| }) |
| |
| adherence_trend = [] |
| for day, stats in sorted(adherence_trend_map.items()): |
| total = stats["taken"] + stats["missed"] |
| rate = round((stats["taken"] / total) * 100, 2) if total > 0 else 0 |
| adherence_trend.append({"date": day, "rate": rate}) |
| |
| time_analysis = [] |
| for t, stats in sorted(time_analysis_map.items()): |
| total = stats["taken"] + stats["missed"] |
| rate = round((stats["taken"] / total) * 100, 2) if total > 0 else 0 |
| time_analysis.append({"time": t, "rate": rate}) |
| total_medicines = len(medicines) |
| adherence_rate = round((total_taken / (total_taken + total_missed)) * 100, 2) if (total_taken + total_missed) > 0 else 0 |
| return { |
| "total_medicines": total_medicines, |
| "doses_taken": total_taken, |
| "doses_missed": total_missed, |
| "adherence_rate": adherence_rate, |
| "medicine_adherence": medicine_adherence, |
| "adherence_trend": adherence_trend, |
| "time_analysis": time_analysis |
| } |
|
|
| @router.get("/report/csv") |
| async def download_csv_report(current_user: dict = Depends(get_current_user)): |
| medicines = await db.medicines.find({"user_id": str(current_user["_id"])}).to_list(length=100) |
| rows = [] |
| for med in medicines: |
| for h in med.get("history", []): |
| status = h["status"] |
| |
| if status == "absent": |
| status = "missed" |
| elif status != "taken": |
| continue |
| |
| |
| hist_dt = None |
| try: |
| hist_dt = datetime.fromisoformat(h["time"]) |
| except Exception: |
| continue |
| |
| hist_time_str = hist_dt.strftime("%I:%M %p") |
| |
| if hist_time_str in med.get("times", []): |
| rows.append({ |
| "Medicine Name": med["name"], |
| "Dosage": med["dosage"], |
| "Time": hist_time_str, |
| "Taken/Missed": status, |
| "History Time": h["time"] |
| }) |
| df = pd.DataFrame(rows) |
| output = io.StringIO() |
| df.to_csv(output, index=False) |
| return Response(content=output.getvalue(), media_type="text/csv", headers={"Content-Disposition": "attachment; filename=medical_report.csv"}) |
|
|
| @router.get("/report/pdf") |
| async def download_pdf_report(current_user: dict = Depends(get_current_user)): |
| medicines = await db.medicines.find({"user_id": str(current_user["_id"])}).to_list(length=100) |
| rows = [] |
| for med in medicines: |
| for h in med.get("history", []): |
| status = h["status"] |
| |
| if status == "absent": |
| status = "missed" |
| elif status != "taken": |
| continue |
| |
| hist_dt = None |
| try: |
| hist_dt = datetime.fromisoformat(h["time"]) |
| except Exception: |
| continue |
| hist_time_str = hist_dt.strftime("%I:%M %p") |
| if hist_time_str in med.get("times", []): |
| rows.append({ |
| "Medicine Name": med["name"], |
| "Dosage": med["dosage"], |
| "Time": hist_time_str, |
| "Taken/Missed": status, |
| "History Time": h["time"] |
| }) |
| df = pd.DataFrame(rows) |
| |
| taken_count = df[df["Taken/Missed"] == "taken"].shape[0] |
| missed_count = df[df["Taken/Missed"] == "missed"].shape[0] |
| plt.figure(figsize=(4, 3)) |
| plt.bar(["Taken", "Missed"], [taken_count, missed_count], color=["green", "red"]) |
| plt.title("Medicine Taken vs Missed") |
| plt.tight_layout() |
| img_buf = io.BytesIO() |
| plt.savefig(img_buf, format="png") |
| img_buf.seek(0) |
| |
| pdf_buf = io.BytesIO() |
| c = canvas.Canvas(pdf_buf, pagesize=letter) |
| c.setFont("Helvetica", 12) |
| c.drawString(30, 750, "Medical Report") |
| c.drawString(30, 735, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}") |
| c.drawString(30, 715, f"User: {current_user['full_name']} ({current_user['email']})") |
| c.drawString(30, 695, f"Total Medicines: {len(medicines)}") |
| c.drawString(30, 675, f"Taken: {taken_count} Missed: {missed_count}") |
| |
| c.drawImage(ImageReader(img_buf), 30, 500, width=200, height=150) |
| |
| c.drawString(30, 470, "Name Dosage Time Status History Time") |
| y = 455 |
| for _, row in df.iterrows(): |
| c.drawString(30, y, f"{row['Medicine Name']} {row['Dosage']} {row['Time']} {row['Taken/Missed']} {row['History Time']}") |
| y -= 15 |
| if y < 50: |
| c.showPage() |
| y = 750 |
| c.save() |
| pdf_buf.seek(0) |
| return Response(content=pdf_buf.getvalue(), media_type="application/pdf", headers={"Content-Disposition": "attachment; filename=medical_report.pdf"}) |
|
|