Spaces:
Sleeping
Sleeping
teest
Browse files- api/routes/patients.py +108 -112
api/routes/patients.py
CHANGED
|
@@ -130,14 +130,15 @@ async def create_patient(
|
|
| 130 |
# Add system-generated fields
|
| 131 |
patient_doc.update({
|
| 132 |
"fhir_id": str(uuid.uuid4()),
|
| 133 |
-
"import_date": now,
|
| 134 |
-
"last_updated": now,
|
| 135 |
"source": "manual",
|
| 136 |
-
"
|
|
|
|
|
|
|
|
|
|
| 137 |
})
|
| 138 |
|
| 139 |
# Ensure arrays exist even if empty
|
| 140 |
-
for field in ['
|
| 141 |
if field not in patient_doc:
|
| 142 |
patient_doc[field] = []
|
| 143 |
|
|
@@ -257,21 +258,21 @@ async def process_synthea_patient(bundle: dict, file_path: str) -> Optional[dict
|
|
| 257 |
raw_full_name = f"{' '.join(name.get('given', ['']))} {name.get('family', '')}".strip()
|
| 258 |
clean_full_name = re.sub(r'\d+', '', raw_full_name).strip()
|
| 259 |
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
|
| 276 |
elif resource_type == 'Encounter':
|
| 277 |
encounter = {
|
|
@@ -317,18 +318,25 @@ async def process_synthea_patient(bundle: dict, file_path: str) -> Optional[dict
|
|
| 317 |
logger.error(f"Error processing {resource_type} in {file_path}: {str(e)}")
|
| 318 |
continue
|
| 319 |
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
|
| 333 |
@router.post("/import", status_code=status.HTTP_201_CREATED)
|
| 334 |
async def import_patients(
|
|
@@ -894,34 +902,32 @@ async def get_patient(patient_id: str):
|
|
| 894 |
)
|
| 895 |
|
| 896 |
response = {
|
| 897 |
-
"
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
|
| 911 |
-
|
| 912 |
-
|
| 913 |
-
|
| 914 |
-
"
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
"
|
| 921 |
-
|
| 922 |
-
|
| 923 |
-
"last_updated": patient.get("last_updated")
|
| 924 |
-
}
|
| 925 |
}
|
| 926 |
|
| 927 |
logger.info(f"Successfully retrieved patient: {patient_id}")
|
|
@@ -991,62 +997,44 @@ async def update_patient(
|
|
| 991 |
"state": update_data.state,
|
| 992 |
"postal_code": update_data.postal_code,
|
| 993 |
"country": update_data.country,
|
| 994 |
-
"
|
| 995 |
-
"
|
| 996 |
}
|
| 997 |
for key, value in demographics.items():
|
| 998 |
if value is not None:
|
| 999 |
update_ops["$set"][key] = value
|
| 1000 |
|
| 1001 |
-
# Handle
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1002 |
array_fields = {
|
| 1003 |
-
"
|
| 1004 |
-
"
|
| 1005 |
-
"
|
| 1006 |
-
"notes": update_data.notes
|
| 1007 |
}
|
| 1008 |
|
| 1009 |
for field, items in array_fields.items():
|
| 1010 |
if items is not None:
|
| 1011 |
-
|
| 1012 |
-
existing_items = patient.get(field, [])
|
| 1013 |
-
updated_items = []
|
| 1014 |
-
|
| 1015 |
-
for item in items:
|
| 1016 |
-
item_dict = item.dict(exclude_unset=True)
|
| 1017 |
-
if not item_dict:
|
| 1018 |
-
continue
|
| 1019 |
-
|
| 1020 |
-
# Generate ID for new items
|
| 1021 |
-
if not item_dict.get("id"):
|
| 1022 |
-
item_dict["id"] = str(uuid.uuid4())
|
| 1023 |
-
|
| 1024 |
-
# Validate required fields
|
| 1025 |
-
if field == "conditions" and not item_dict.get("code"):
|
| 1026 |
-
raise HTTPException(
|
| 1027 |
-
status_code=status.HTTP_400_BAD_REQUEST,
|
| 1028 |
-
detail=f"Condition code is required for {field}"
|
| 1029 |
-
)
|
| 1030 |
-
if field == "medications" and not item_dict.get("name"):
|
| 1031 |
-
raise HTTPException(
|
| 1032 |
-
status_code=status.HTTP_400_BAD_REQUEST,
|
| 1033 |
-
detail=f"Medication name is required for {field}"
|
| 1034 |
-
)
|
| 1035 |
-
if field == "encounters" and not item_dict.get("type"):
|
| 1036 |
-
raise HTTPException(
|
| 1037 |
-
status_code=status.HTTP_400_BAD_REQUEST,
|
| 1038 |
-
detail=f"Encounter type is required for {field}"
|
| 1039 |
-
)
|
| 1040 |
-
if field == "notes" and not item_dict.get("content"):
|
| 1041 |
-
raise HTTPException(
|
| 1042 |
-
status_code=status.HTTP_400_BAD_REQUEST,
|
| 1043 |
-
detail=f"Note content is required for {field}"
|
| 1044 |
-
)
|
| 1045 |
-
|
| 1046 |
-
updated_items.append(item_dict)
|
| 1047 |
-
|
| 1048 |
-
# Replace the entire array
|
| 1049 |
-
update_ops["$set"][field] = updated_items
|
| 1050 |
|
| 1051 |
# Perform the update
|
| 1052 |
result = await patients_collection.update_one(query, update_ops)
|
|
@@ -1078,15 +1066,23 @@ async def update_patient(
|
|
| 1078 |
"state": updated_patient.get("state"),
|
| 1079 |
"postal_code": updated_patient.get("postal_code"),
|
| 1080 |
"country": updated_patient.get("country"),
|
| 1081 |
-
"
|
| 1082 |
-
"
|
| 1083 |
-
"
|
|
|
|
| 1084 |
"medications": updated_patient.get("medications", []),
|
| 1085 |
-
"
|
| 1086 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1087 |
"source": updated_patient.get("source"),
|
| 1088 |
-
"
|
| 1089 |
-
"
|
|
|
|
|
|
|
| 1090 |
}
|
| 1091 |
|
| 1092 |
logger.info(f"Successfully updated patient {patient_id}")
|
|
|
|
| 130 |
# Add system-generated fields
|
| 131 |
patient_doc.update({
|
| 132 |
"fhir_id": str(uuid.uuid4()),
|
|
|
|
|
|
|
| 133 |
"source": "manual",
|
| 134 |
+
"status": "active",
|
| 135 |
+
"created_by": current_user.get('email'),
|
| 136 |
+
"created_at": datetime.utcnow(),
|
| 137 |
+
"updated_at": datetime.utcnow()
|
| 138 |
})
|
| 139 |
|
| 140 |
# Ensure arrays exist even if empty
|
| 141 |
+
for field in ['allergies', 'chronic_conditions', 'medications']:
|
| 142 |
if field not in patient_doc:
|
| 143 |
patient_doc[field] = []
|
| 144 |
|
|
|
|
| 258 |
raw_full_name = f"{' '.join(name.get('given', ['']))} {name.get('family', '')}".strip()
|
| 259 |
clean_full_name = re.sub(r'\d+', '', raw_full_name).strip()
|
| 260 |
|
| 261 |
+
patient_data = {
|
| 262 |
+
'fhir_id': resource.get('id'),
|
| 263 |
+
'full_name': clean_full_name,
|
| 264 |
+
'gender': resource.get('gender', 'unknown'),
|
| 265 |
+
'date_of_birth': resource.get('birthDate', ''),
|
| 266 |
+
'address': ' '.join(address.get('line', [''])),
|
| 267 |
+
'city': address.get('city', ''),
|
| 268 |
+
'state': address.get('state', ''),
|
| 269 |
+
'postal_code': address.get('postalCode', ''),
|
| 270 |
+
'country': address.get('country', ''),
|
| 271 |
+
'source': 'synthea',
|
| 272 |
+
'status': 'active',
|
| 273 |
+
'created_at': datetime.utcnow(),
|
| 274 |
+
'updated_at': datetime.utcnow()
|
| 275 |
+
}
|
| 276 |
|
| 277 |
elif resource_type == 'Encounter':
|
| 278 |
encounter = {
|
|
|
|
| 318 |
logger.error(f"Error processing {resource_type} in {file_path}: {str(e)}")
|
| 319 |
continue
|
| 320 |
|
| 321 |
+
if patient_data:
|
| 322 |
+
patient_data.update({
|
| 323 |
+
'allergies': [], # Convert conditions to allergies if needed
|
| 324 |
+
'chronic_conditions': conditions,
|
| 325 |
+
'medications': medications,
|
| 326 |
+
'symptoms': [],
|
| 327 |
+
'vital_signs': {},
|
| 328 |
+
'medical_notes': '',
|
| 329 |
+
'emergency_contact_name': '',
|
| 330 |
+
'emergency_contact_phone': '',
|
| 331 |
+
'insurance_provider': '',
|
| 332 |
+
'insurance_policy_number': '',
|
| 333 |
+
'national_id': '',
|
| 334 |
+
'blood_type': ''
|
| 335 |
+
})
|
| 336 |
+
logger.info(f"Successfully processed patient {patient_data.get('fhir_id')} from {file_path}")
|
| 337 |
+
return patient_data
|
| 338 |
+
logger.warning(f"No valid patient data found in {file_path}")
|
| 339 |
+
return None
|
| 340 |
|
| 341 |
@router.post("/import", status_code=status.HTTP_201_CREATED)
|
| 342 |
async def import_patients(
|
|
|
|
| 902 |
)
|
| 903 |
|
| 904 |
response = {
|
| 905 |
+
"id": str(patient["_id"]),
|
| 906 |
+
"full_name": patient.get("full_name"),
|
| 907 |
+
"gender": patient.get("gender"),
|
| 908 |
+
"date_of_birth": patient.get("date_of_birth"),
|
| 909 |
+
"address": patient.get("address"),
|
| 910 |
+
"city": patient.get("city"),
|
| 911 |
+
"state": patient.get("state"),
|
| 912 |
+
"postal_code": patient.get("postal_code"),
|
| 913 |
+
"country": patient.get("country"),
|
| 914 |
+
"national_id": patient.get("national_id"),
|
| 915 |
+
"blood_type": patient.get("blood_type"),
|
| 916 |
+
"allergies": patient.get("allergies", []),
|
| 917 |
+
"chronic_conditions": patient.get("chronic_conditions", []),
|
| 918 |
+
"medications": patient.get("medications", []),
|
| 919 |
+
"emergency_contact_name": patient.get("emergency_contact_name"),
|
| 920 |
+
"emergency_contact_phone": patient.get("emergency_contact_phone"),
|
| 921 |
+
"insurance_provider": patient.get("insurance_provider"),
|
| 922 |
+
"insurance_policy_number": patient.get("insurance_policy_number"),
|
| 923 |
+
"medical_notes": patient.get("medical_notes"),
|
| 924 |
+
"symptoms": patient.get("symptoms", []),
|
| 925 |
+
"vital_signs": patient.get("vital_signs", {}),
|
| 926 |
+
"source": patient.get("source"),
|
| 927 |
+
"status": patient.get("status"),
|
| 928 |
+
"assigned_doctor_id": patient.get("assigned_doctor_id"),
|
| 929 |
+
"created_at": patient.get("created_at"),
|
| 930 |
+
"updated_at": patient.get("updated_at")
|
|
|
|
|
|
|
| 931 |
}
|
| 932 |
|
| 933 |
logger.info(f"Successfully retrieved patient: {patient_id}")
|
|
|
|
| 997 |
"state": update_data.state,
|
| 998 |
"postal_code": update_data.postal_code,
|
| 999 |
"country": update_data.country,
|
| 1000 |
+
"national_id": update_data.national_id,
|
| 1001 |
+
"blood_type": update_data.blood_type
|
| 1002 |
}
|
| 1003 |
for key, value in demographics.items():
|
| 1004 |
if value is not None:
|
| 1005 |
update_ops["$set"][key] = value
|
| 1006 |
|
| 1007 |
+
# Handle contact and insurance updates
|
| 1008 |
+
contact_fields = {
|
| 1009 |
+
"emergency_contact_name": update_data.emergency_contact_name,
|
| 1010 |
+
"emergency_contact_phone": update_data.emergency_contact_phone,
|
| 1011 |
+
"insurance_provider": update_data.insurance_provider,
|
| 1012 |
+
"insurance_policy_number": update_data.insurance_policy_number
|
| 1013 |
+
}
|
| 1014 |
+
for key, value in contact_fields.items():
|
| 1015 |
+
if value is not None:
|
| 1016 |
+
update_ops["$set"][key] = value
|
| 1017 |
+
|
| 1018 |
+
# Handle medical information updates
|
| 1019 |
+
medical_fields = {
|
| 1020 |
+
"medical_notes": update_data.medical_notes,
|
| 1021 |
+
"symptoms": update_data.symptoms,
|
| 1022 |
+
"vital_signs": update_data.vital_signs
|
| 1023 |
+
}
|
| 1024 |
+
for key, value in medical_fields.items():
|
| 1025 |
+
if value is not None:
|
| 1026 |
+
update_ops["$set"][key] = value
|
| 1027 |
+
|
| 1028 |
+
# Handle array updates (simple string arrays)
|
| 1029 |
array_fields = {
|
| 1030 |
+
"allergies": update_data.allergies,
|
| 1031 |
+
"chronic_conditions": update_data.chronic_conditions,
|
| 1032 |
+
"medications": update_data.medications
|
|
|
|
| 1033 |
}
|
| 1034 |
|
| 1035 |
for field, items in array_fields.items():
|
| 1036 |
if items is not None:
|
| 1037 |
+
update_ops["$set"][field] = items
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1038 |
|
| 1039 |
# Perform the update
|
| 1040 |
result = await patients_collection.update_one(query, update_ops)
|
|
|
|
| 1066 |
"state": updated_patient.get("state"),
|
| 1067 |
"postal_code": updated_patient.get("postal_code"),
|
| 1068 |
"country": updated_patient.get("country"),
|
| 1069 |
+
"national_id": updated_patient.get("national_id"),
|
| 1070 |
+
"blood_type": updated_patient.get("blood_type"),
|
| 1071 |
+
"allergies": updated_patient.get("allergies", []),
|
| 1072 |
+
"chronic_conditions": updated_patient.get("chronic_conditions", []),
|
| 1073 |
"medications": updated_patient.get("medications", []),
|
| 1074 |
+
"emergency_contact_name": updated_patient.get("emergency_contact_name"),
|
| 1075 |
+
"emergency_contact_phone": updated_patient.get("emergency_contact_phone"),
|
| 1076 |
+
"insurance_provider": updated_patient.get("insurance_provider"),
|
| 1077 |
+
"insurance_policy_number": updated_patient.get("insurance_policy_number"),
|
| 1078 |
+
"medical_notes": updated_patient.get("medical_notes"),
|
| 1079 |
+
"symptoms": updated_patient.get("symptoms", []),
|
| 1080 |
+
"vital_signs": updated_patient.get("vital_signs", {}),
|
| 1081 |
"source": updated_patient.get("source"),
|
| 1082 |
+
"status": updated_patient.get("status"),
|
| 1083 |
+
"assigned_doctor_id": updated_patient.get("assigned_doctor_id"),
|
| 1084 |
+
"created_at": updated_patient.get("created_at"),
|
| 1085 |
+
"updated_at": updated_patient.get("updated_at")
|
| 1086 |
}
|
| 1087 |
|
| 1088 |
logger.info(f"Successfully updated patient {patient_id}")
|