Medisync / routers /analysis.py
anique-1's picture
Deploy MediSync AI Backend with APK to Hugging Face
903c699
Raw
History Blame Contribute Delete
8.22 kB
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)
# Per-medicine stats
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"])
# Convert to UTC if offset-aware, else assume UTC
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"]
# Treat "absent" as "missed"
if status == "absent":
missed += 1
total_missed += 1
elif status == "taken":
taken += 1
total_taken += 1
else:
continue
# For adherence_trend (by day)
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
# For time_analysis (by time of day)
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 (list of {date, rate})
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 (list of {time, 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"]
# Convert "absent" to "missed" for reporting
if status == "absent":
status = "missed"
elif status != "taken":
continue # Ignore unknown statuses
# Extract scheduled time from history time
# h["time"] is ISO string, e.g., "2025-10-25T08:00:00+05:00"
hist_dt = None
try:
hist_dt = datetime.fromisoformat(h["time"])
except Exception:
continue
# Format to match scheduled time string (e.g., "08:00 AM")
hist_time_str = hist_dt.strftime("%I:%M %p")
# Only include if this time is in the medicine's scheduled times
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"]
# Convert "absent" to "missed" for reporting
if status == "absent":
status = "missed"
elif status != "taken":
continue # Ignore unknown statuses
# Extract scheduled time from history time
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)
# Create a bar graph of taken vs missed
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)
# Create PDF
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}")
# Draw graph
c.drawImage(ImageReader(img_buf), 30, 500, width=200, height=150)
# Table header
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"})