Spaces:
Sleeping
Sleeping
Update api/routes.py
Browse files- api/routes.py +28 -166
api/routes.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Depends, Body
|
| 2 |
from fastapi.security import OAuth2PasswordRequestForm
|
| 3 |
from models.schemas import SignupForm, TokenResponse, PatientCreate, DoctorCreate, AppointmentCreate
|
| 4 |
from db.mongo import users_collection, patients_collection, appointments_collection
|
|
@@ -8,10 +8,7 @@ from bson import ObjectId
|
|
| 8 |
from bson.errors import InvalidId
|
| 9 |
from typing import Optional, List
|
| 10 |
from pydantic import BaseModel
|
| 11 |
-
|
| 12 |
-
import csv
|
| 13 |
-
import io
|
| 14 |
-
import json
|
| 15 |
|
| 16 |
router = APIRouter()
|
| 17 |
|
|
@@ -134,176 +131,41 @@ async def list_patients(current_user: dict = Depends(get_current_user)):
|
|
| 134 |
})
|
| 135 |
return patients
|
| 136 |
|
| 137 |
-
# ---
|
| 138 |
-
@router.
|
| 139 |
-
async def
|
| 140 |
-
|
| 141 |
-
raise HTTPException(status_code=403, detail="Only doctors can count patients")
|
| 142 |
-
count = await patients_collection.count_documents({"created_by": current_user["email"]})
|
| 143 |
-
return {"count": count}
|
| 144 |
-
|
| 145 |
-
# --- CREATE APPOINTMENT ---
|
| 146 |
-
@router.post("/appointments")
|
| 147 |
-
async def create_appointment(data: AppointmentCreate, current_user: dict = Depends(get_current_user)):
|
| 148 |
-
if current_user.get("role") != "patient":
|
| 149 |
-
raise HTTPException(status_code=403, detail="Only patients can book appointments")
|
| 150 |
-
|
| 151 |
-
patient_user = await users_collection.find_one({"email": current_user["email"]})
|
| 152 |
-
if not patient_user:
|
| 153 |
-
raise HTTPException(status_code=404, detail="Patient user not found")
|
| 154 |
-
|
| 155 |
-
appointment_doc = {
|
| 156 |
-
"patient_id": patient_user["_id"],
|
| 157 |
-
"doctor_id": ObjectId(data.doctor_id),
|
| 158 |
-
"date": datetime.combine(data.date, datetime.min.time()),
|
| 159 |
-
"time": data.time.strftime("%H:%M:%S"),
|
| 160 |
-
"reason": data.reason,
|
| 161 |
-
"created_by": current_user["email"],
|
| 162 |
-
"created_at": datetime.utcnow()
|
| 163 |
-
}
|
| 164 |
-
await appointments_collection.insert_one(appointment_doc)
|
| 165 |
-
|
| 166 |
-
return {"message": "Appointment booked successfully"}
|
| 167 |
-
|
| 168 |
-
# --- LIST DOCTOR'S APPOINTMENTS ---
|
| 169 |
-
@router.get("/appointments/doctor")
|
| 170 |
-
async def list_doctor_appointments(current_user: dict = Depends(get_current_user)):
|
| 171 |
-
if current_user.get("role") != "doctor":
|
| 172 |
-
raise HTTPException(status_code=403, detail="Only doctors can view this")
|
| 173 |
-
|
| 174 |
-
cursor = appointments_collection.find({"doctor_id": ObjectId(current_user["_id"])})
|
| 175 |
-
appointments = []
|
| 176 |
-
async for a in cursor:
|
| 177 |
-
try:
|
| 178 |
-
patient_id = a.get("patient_id")
|
| 179 |
-
if not isinstance(patient_id, ObjectId):
|
| 180 |
-
patient_id = ObjectId(patient_id)
|
| 181 |
-
patient = await users_collection.find_one({"_id": patient_id})
|
| 182 |
-
except Exception:
|
| 183 |
-
patient = None
|
| 184 |
-
|
| 185 |
-
appointments.append({
|
| 186 |
-
"_id": str(a["_id"]),
|
| 187 |
-
"doctor_id": str(a["doctor_id"]),
|
| 188 |
-
"patient": {
|
| 189 |
-
"full_name": patient.get("full_name", "Unknown") if patient else "Unknown",
|
| 190 |
-
"email": patient.get("email", "") if patient else "",
|
| 191 |
-
},
|
| 192 |
-
"date": a.get("date").strftime("%Y-%m-%d") if isinstance(a.get("date"), datetime) else a.get("date"),
|
| 193 |
-
"time": a.get("time", ""),
|
| 194 |
-
"reason": a.get("reason", "")
|
| 195 |
-
})
|
| 196 |
-
|
| 197 |
-
return appointments
|
| 198 |
-
|
| 199 |
-
# --- LIST PATIENT'S APPOINTMENTS ---
|
| 200 |
-
@router.get("/appointments/patient")
|
| 201 |
-
async def list_my_appointments(current_user: dict = Depends(get_current_user)):
|
| 202 |
-
if current_user.get("role") != "patient":
|
| 203 |
-
raise HTTPException(status_code=403, detail="Only patients can view their appointments")
|
| 204 |
-
|
| 205 |
-
user = await users_collection.find_one({"email": current_user["email"]})
|
| 206 |
-
if not user:
|
| 207 |
-
raise HTTPException(status_code=404, detail="User not found")
|
| 208 |
-
|
| 209 |
-
patient_id = user.get("_id")
|
| 210 |
-
cursor = appointments_collection.find({"patient_id": patient_id})
|
| 211 |
-
return [{**a, "_id": str(a["_id"]), "patient_id": str(a["patient_id"]), "doctor_id": str(a["doctor_id"])} async for a in cursor]
|
| 212 |
-
|
| 213 |
-
# === UTIL: Parse Synthea CSV ===
|
| 214 |
-
def parse_synthea_patient_csv(file: UploadFile) -> List[dict]:
|
| 215 |
-
decoded = file.file.read().decode("utf-8")
|
| 216 |
-
reader = csv.DictReader(io.StringIO(decoded))
|
| 217 |
-
patients = []
|
| 218 |
-
|
| 219 |
-
for row in reader:
|
| 220 |
-
patients.append({
|
| 221 |
-
"synthea_id": row.get("Id"),
|
| 222 |
-
"full_name": f"{row.get('FIRST', '')} {row.get('LAST', '')}".strip(),
|
| 223 |
-
"gender": row.get("GENDER"),
|
| 224 |
-
"date_of_birth": datetime.strptime(row.get("BIRTHDATE"), "%Y-%m-%dT%H:%M:%SZ"),
|
| 225 |
-
"address": row.get("ADDRESS"),
|
| 226 |
-
"city": row.get("CITY"),
|
| 227 |
-
"state": row.get("STATE"),
|
| 228 |
-
"zip": row.get("ZIP"),
|
| 229 |
-
"created_at": datetime.utcnow()
|
| 230 |
-
})
|
| 231 |
-
|
| 232 |
-
return patients
|
| 233 |
-
|
| 234 |
-
# === API: Upload and import patients.csv ===
|
| 235 |
-
@router.post("/ehr/import-patients")
|
| 236 |
-
async def import_synthea_patients(file: UploadFile = File(...)):
|
| 237 |
-
if not file.filename.endswith(".csv"):
|
| 238 |
-
raise HTTPException(status_code=400, detail="Only CSV files are supported")
|
| 239 |
|
| 240 |
try:
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
return {"inserted_count": len(result.inserted_ids)}
|
| 244 |
-
except Exception as e:
|
| 245 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 246 |
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
async def list_synthea_patients():
|
| 250 |
-
cursor = patients_collection.find({"synthea_id": {"$exists": True}})
|
| 251 |
-
patients = []
|
| 252 |
-
async for p in cursor:
|
| 253 |
-
patients.append({
|
| 254 |
-
"id": str(p["_id"]),
|
| 255 |
-
"synthea_id": p.get("synthea_id"),
|
| 256 |
-
"full_name": p.get("full_name"),
|
| 257 |
-
"gender": p.get("gender"),
|
| 258 |
-
"date_of_birth": p.get("date_of_birth"),
|
| 259 |
-
})
|
| 260 |
-
return patients
|
| 261 |
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
patient = await patients_collection.find_one({"_id": ObjectId(id)})
|
| 267 |
-
if not patient:
|
| 268 |
-
raise HTTPException(status_code=404, detail="Patient not found")
|
| 269 |
-
patient["id"] = str(patient["_id"])
|
| 270 |
-
del patient["_id"]
|
| 271 |
-
return patient
|
| 272 |
-
except Exception:
|
| 273 |
-
raise HTTPException(status_code=400, detail="Invalid patient ID")
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
@router.post("/ehr/auto-import")
|
| 278 |
-
async def auto_import_synthea_patients():
|
| 279 |
-
csv_path = Path("data/synthea/patients.csv")
|
| 280 |
|
| 281 |
-
|
| 282 |
-
raise HTTPException(status_code=404, detail="Synthea patients.csv not found.")
|
| 283 |
-
|
| 284 |
-
try:
|
| 285 |
-
with open(csv_path, "r", encoding="utf-8") as f:
|
| 286 |
-
reader = csv.DictReader(f)
|
| 287 |
-
patients = []
|
| 288 |
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
"gender": row.get("GENDER"),
|
| 294 |
-
"date_of_birth": datetime.strptime(row.get("BIRTHDATE"), "%Y-%m-%dT%H:%M:%SZ"),
|
| 295 |
-
"address": row.get("ADDRESS"),
|
| 296 |
-
"city": row.get("CITY"),
|
| 297 |
-
"state": row.get("STATE"),
|
| 298 |
-
"zip": row.get("ZIP"),
|
| 299 |
-
"created_at": datetime.utcnow()
|
| 300 |
-
})
|
| 301 |
|
| 302 |
-
|
| 303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
result = await patients_collection.insert_many(patients)
|
| 306 |
-
return {"message": "
|
| 307 |
|
| 308 |
except Exception as e:
|
| 309 |
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
| 1 |
+
from fastapi import APIRouter, HTTPException, Depends, Body
|
| 2 |
from fastapi.security import OAuth2PasswordRequestForm
|
| 3 |
from models.schemas import SignupForm, TokenResponse, PatientCreate, DoctorCreate, AppointmentCreate
|
| 4 |
from db.mongo import users_collection, patients_collection, appointments_collection
|
|
|
|
| 8 |
from bson.errors import InvalidId
|
| 9 |
from typing import Optional, List
|
| 10 |
from pydantic import BaseModel
|
| 11 |
+
import httpx
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
router = APIRouter()
|
| 14 |
|
|
|
|
| 131 |
})
|
| 132 |
return patients
|
| 133 |
|
| 134 |
+
# --- IMPORT FROM PUBLIC FHIR API ---
|
| 135 |
+
@router.post("/ehr/fetch-from-api")
|
| 136 |
+
async def fetch_and_store_patients_from_fhir():
|
| 137 |
+
fhir_api = "https://hapi.fhir.org/baseR4/Patient?_count=50"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
try:
|
| 140 |
+
async with httpx.AsyncClient() as client:
|
| 141 |
+
response = await client.get(fhir_api)
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
+
if response.status_code != 200:
|
| 144 |
+
raise HTTPException(status_code=502, detail="Failed to fetch from FHIR server.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
+
data = response.json()
|
| 147 |
+
entries = data.get("entry", [])
|
| 148 |
+
if not entries:
|
| 149 |
+
return {"message": "No patients found."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
+
patients = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
+
for entry in entries:
|
| 154 |
+
resource = entry.get("resource", {})
|
| 155 |
+
name = resource.get("name", [{}])[0]
|
| 156 |
+
full_name = f"{name.get('given', [''])[0]} {name.get('family', '')}".strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
+
patient = {
|
| 159 |
+
"fhir_id": resource.get("id"),
|
| 160 |
+
"full_name": full_name,
|
| 161 |
+
"gender": resource.get("gender"),
|
| 162 |
+
"date_of_birth": resource.get("birthDate"),
|
| 163 |
+
"created_at": datetime.utcnow()
|
| 164 |
+
}
|
| 165 |
+
patients.append(patient)
|
| 166 |
|
| 167 |
result = await patients_collection.insert_many(patients)
|
| 168 |
+
return {"message": "Fetched and stored successfully", "count": len(result.inserted_ids)}
|
| 169 |
|
| 170 |
except Exception as e:
|
| 171 |
raise HTTPException(status_code=500, detail=str(e))
|