Ali2206 commited on
Commit
203ffde
·
verified ·
1 Parent(s): 345e909

Update api/routes.py

Browse files
Files changed (1) hide show
  1. api/routes.py +114 -63
api/routes.py CHANGED
@@ -1,16 +1,15 @@
1
- from fastapi import APIRouter, HTTPException, Depends
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
5
  from core.security import hash_password, verify_password, create_access_token, get_current_user
6
  from datetime import datetime
7
  from bson import ObjectId
8
- from typing import Optional
9
- from pymongo import UpdateOne
10
- import json
11
- from pathlib import Path
12
- import traceback
13
  import httpx
 
14
 
15
  router = APIRouter()
16
 
@@ -56,6 +55,22 @@ async def create_doctor(data: DoctorCreate):
56
  await users_collection.insert_one(user_doc)
57
  return {"success": True, "message": "Doctor account created"}
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  # --- LOGIN ---
60
  @router.post("/login", response_model=TokenResponse)
61
  async def login(form_data: OAuth2PasswordRequestForm = Depends()):
@@ -83,59 +98,101 @@ async def get_me(current_user: dict = Depends(get_current_user)):
83
  "created_at": user.get("created_at", "")
84
  }
85
 
86
- # --- IMPORT MOCKED FHIR PATIENTS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  @router.post("/ehr/fetch-from-api")
88
  async def fetch_and_store_patients_from_fhir():
89
- base_url = "https://launch.smarthealthit.org/v/r4/fhir"
90
- patient_url = f"{base_url}/Patient?_count=50"
91
- valid_patients = []
92
 
93
  try:
94
- async with httpx.AsyncClient(timeout=30.0) as client:
95
- patient_response = await client.get(patient_url)
96
-
97
- if patient_response.status_code != 200:
98
- raise HTTPException(status_code=502, detail="Failed to fetch patients")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
- patients_data = patient_response.json().get("entry", [])
 
 
 
101
 
102
- for p_entry in patients_data:
103
- resource = p_entry.get("resource", {})
 
104
  fhir_id = resource.get("id")
105
- name_data = resource.get("name", [{}])[0]
106
- full_name = f"{name_data.get('given', [''])[0]} {name_data.get('family', '')}".strip()
 
 
 
 
 
107
  gender = resource.get("gender")
108
  birth_date = resource.get("birthDate")
 
 
 
 
109
 
110
- address_data = resource.get("address", [{}])[0]
111
- address = address_data.get("line", [""])[0] if "line" in address_data else ""
112
- city = address_data.get("city", "")
113
- state = address_data.get("state", "")
114
- zip_code = address_data.get("postalCode", "")
115
-
116
- # Ensure required fields are present
117
  if not all([fhir_id, full_name, gender, birth_date, address, city, state, zip_code]):
118
  continue
119
 
120
- # Fetch encounters with notes
121
- try:
122
- async with httpx.AsyncClient(timeout=20.0) as client:
123
- enc_response = await client.get(f"{base_url}/Encounter?subject=Patient/{fhir_id}")
124
 
125
- encounter_entries = enc_response.json().get("entry", []) if enc_response.status_code == 200 else []
126
- notes = []
127
-
128
- for enc in encounter_entries:
129
- enc_res = enc.get("resource", {})
130
- for note in enc_res.get("note", []):
131
- text = note.get("text")
132
- if text:
133
- notes.append(text)
134
-
135
- if not notes:
136
- continue # Skip if no notes
137
-
138
- valid_patients.append({
139
  "fhir_id": fhir_id,
140
  "full_name": full_name,
141
  "gender": gender,
@@ -146,27 +203,22 @@ async def fetch_and_store_patients_from_fhir():
146
  "zip": zip_code,
147
  "notes": notes,
148
  "created_at": datetime.utcnow()
149
- })
 
 
150
 
151
- except Exception as enc_err:
152
- print(f"Skipping encounter fetch error for {fhir_id}: {enc_err}")
153
- continue
 
 
 
 
154
 
155
- if not valid_patients:
156
- return {"message": "No valid patients found with complete data and notes."}
157
-
158
- result = await patients_collection.insert_many(valid_patients)
159
- return {
160
- "message": "Imported valid patients with notes",
161
- "count": len(result.inserted_ids)
162
- }
163
 
164
  except Exception as e:
165
- print("❌ Server-side error during FHIR import:", e)
166
- raise HTTPException(status_code=500, detail="Server error during patient import.")
167
-
168
-
169
- # --- GET FHIR PATIENTS ---
170
  @router.get("/ehr/fhir-patients")
171
  async def list_fhir_patients():
172
  cursor = patients_collection.find({"fhir_id": {"$exists": True}})
@@ -177,6 +229,5 @@ async def list_fhir_patients():
177
  "full_name": p.get("full_name"),
178
  "gender": p.get("gender"),
179
  "date_of_birth": p.get("date_of_birth"),
180
- "notes": p.get("notes", [])
181
  })
182
  return patients
 
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
5
  from core.security import hash_password, verify_password, create_access_token, get_current_user
6
  from datetime import datetime
7
  from bson import ObjectId
8
+ from bson.errors import InvalidId
9
+ from typing import Optional, List
10
+ from pydantic import BaseModel
 
 
11
  import httpx
12
+ from pymongo import UpdateOne
13
 
14
  router = APIRouter()
15
 
 
55
  await users_collection.insert_one(user_doc)
56
  return {"success": True, "message": "Doctor account created"}
57
 
58
+ # --- GET ALL DOCTORS ---
59
+ @router.get("/doctors")
60
+ async def list_doctors():
61
+ cursor = users_collection.find({"role": "doctor"})
62
+ doctors = []
63
+ async for doc in cursor:
64
+ doctors.append({
65
+ "id": str(doc["_id"]),
66
+ "full_name": doc.get("full_name", ""),
67
+ "email": doc.get("email", ""),
68
+ "matricule": doc.get("matricule", ""),
69
+ "specialty": doc.get("specialty", ""),
70
+ "created_at": doc.get("created_at"),
71
+ })
72
+ return doctors
73
+
74
  # --- LOGIN ---
75
  @router.post("/login", response_model=TokenResponse)
76
  async def login(form_data: OAuth2PasswordRequestForm = Depends()):
 
98
  "created_at": user.get("created_at", "")
99
  }
100
 
101
+ # --- ADD NEW PATIENT ---
102
+ @router.post("/patients")
103
+ async def add_patient(data: PatientCreate, current_user: dict = Depends(get_current_user)):
104
+ if current_user.get("role") != "doctor":
105
+ raise HTTPException(status_code=403, detail="Only doctors can add patients")
106
+
107
+ patient_doc = {
108
+ **data.dict(),
109
+ "date_of_birth": datetime.combine(data.date_of_birth, datetime.min.time()),
110
+ "contact": data.contact.dict() if data.contact else {},
111
+ "created_by": current_user["email"],
112
+ "created_at": datetime.utcnow()
113
+ }
114
+ result = await patients_collection.insert_one(patient_doc)
115
+ return {"id": str(result.inserted_id), "message": "Patient created successfully"}
116
+
117
+ # --- GET ALL PATIENTS ---
118
+ @router.get("/patients")
119
+ async def list_patients(current_user: dict = Depends(get_current_user)):
120
+ if current_user.get("role") != "doctor":
121
+ raise HTTPException(status_code=403, detail="Only doctors can view patients")
122
+
123
+ cursor = patients_collection.find({"created_by": current_user["email"]})
124
+ patients = []
125
+ async for p in cursor:
126
+ patients.append({
127
+ "id": str(p["_id"]),
128
+ "full_name": p.get("full_name", ""),
129
+ "date_of_birth": p.get("date_of_birth"),
130
+ "gender": p.get("gender", ""),
131
+ "notes": p.get("notes", "")
132
+ })
133
+ return patients
134
+
135
+ # --- IMPORT FROM PUBLIC FHIR API ---
136
+
137
  @router.post("/ehr/fetch-from-api")
138
  async def fetch_and_store_patients_from_fhir():
139
+ fhir_patients_url = "https://hapi.fhir.org/baseR4/Patient?_count=50"
140
+ fhir_encounters_url = "https://hapi.fhir.org/baseR4/Encounter?_count=100"
 
141
 
142
  try:
143
+ async with httpx.AsyncClient() as client:
144
+ # Fetch patients
145
+ patient_res = await client.get(fhir_patients_url)
146
+ if patient_res.status_code != 200:
147
+ raise HTTPException(status_code=502, detail="Failed to fetch patients")
148
+ patient_entries = patient_res.json().get("entry", [])
149
+
150
+ # Fetch encounters
151
+ encounter_res = await client.get(fhir_encounters_url)
152
+ if encounter_res.status_code != 200:
153
+ raise HTTPException(status_code=502, detail="Failed to fetch encounters")
154
+ encounter_entries = encounter_res.json().get("entry", [])
155
+
156
+ # Parse encounter notes grouped by patient ID
157
+ patient_notes = {}
158
+ for entry in encounter_entries:
159
+ resource = entry.get("resource", {})
160
+ ref = resource.get("subject", {}).get("reference") # e.g., "Patient/abc123"
161
+ if not ref or "note" not in resource:
162
+ continue
163
 
164
+ patient_id = ref.split("/")[-1]
165
+ notes_texts = [n.get("text") for n in resource.get("note", []) if n.get("text")]
166
+ if notes_texts:
167
+ patient_notes.setdefault(patient_id, []).extend(notes_texts)
168
 
169
+ operations = []
170
+ for entry in patient_entries:
171
+ resource = entry.get("resource", {})
172
  fhir_id = resource.get("id")
173
+ name_info = resource.get("name", [{}])[0]
174
+ address_info = resource.get("address", [{}])[0]
175
+
176
+ given = name_info.get("given", [""])[0]
177
+ family = name_info.get("family", "")
178
+ full_name = f"{given} {family}".strip()
179
+
180
  gender = resource.get("gender")
181
  birth_date = resource.get("birthDate")
182
+ address = address_info.get("line", [""])[0]
183
+ city = address_info.get("city", "")
184
+ state = address_info.get("state", "")
185
+ zip_code = address_info.get("postalCode", "")
186
 
187
+ # Skip if any essential field is missing
 
 
 
 
 
 
188
  if not all([fhir_id, full_name, gender, birth_date, address, city, state, zip_code]):
189
  continue
190
 
191
+ notes = patient_notes.get(fhir_id, [])
 
 
 
192
 
193
+ operations.append(UpdateOne(
194
+ {"fhir_id": fhir_id},
195
+ {"$set": {
 
 
 
 
 
 
 
 
 
 
 
196
  "fhir_id": fhir_id,
197
  "full_name": full_name,
198
  "gender": gender,
 
203
  "zip": zip_code,
204
  "notes": notes,
205
  "created_at": datetime.utcnow()
206
+ }},
207
+ upsert=True
208
+ ))
209
 
210
+ if operations:
211
+ result = await patients_collection.bulk_write(operations)
212
+ return {
213
+ "message": "Fetched and stored patients with linked notes successfully",
214
+ "inserted": result.upserted_count,
215
+ "modified": result.modified_count
216
+ }
217
 
218
+ return {"message": "No valid patients to store"}
 
 
 
 
 
 
 
219
 
220
  except Exception as e:
221
+ raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
222
  @router.get("/ehr/fhir-patients")
223
  async def list_fhir_patients():
224
  cursor = patients_collection.find({"fhir_id": {"$exists": True}})
 
229
  "full_name": p.get("full_name"),
230
  "gender": p.get("gender"),
231
  "date_of_birth": p.get("date_of_birth"),
 
232
  })
233
  return patients