Spaces:
Sleeping
Sleeping
device token
Browse files- api/routes/patients.py +9 -35
- api/services/synthea_integration.py +110 -0
api/routes/patients.py
CHANGED
|
@@ -1678,42 +1678,16 @@ async def generate_and_import_synthea_patients(
|
|
| 1678 |
"errors": []
|
| 1679 |
}
|
| 1680 |
|
| 1681 |
-
#
|
| 1682 |
-
|
| 1683 |
-
skipped_count = 0
|
| 1684 |
-
errors = []
|
| 1685 |
-
|
| 1686 |
-
for patient_data in generation_result['patients']:
|
| 1687 |
-
try:
|
| 1688 |
-
# Check for existing patient by FHIR ID
|
| 1689 |
-
existing_patient = await patients_collection.find_one({
|
| 1690 |
-
"fhir_id": patient_data['fhir_id']
|
| 1691 |
-
})
|
| 1692 |
-
|
| 1693 |
-
if existing_patient:
|
| 1694 |
-
skipped_count += 1
|
| 1695 |
-
continue
|
| 1696 |
-
|
| 1697 |
-
# Insert patient
|
| 1698 |
-
result = await patients_collection.insert_one(patient_data)
|
| 1699 |
-
imported_count += 1
|
| 1700 |
-
|
| 1701 |
-
logger.info(f"Imported Synthea patient {patient_data['full_name']} with FHIR ID {patient_data['fhir_id']}")
|
| 1702 |
-
|
| 1703 |
-
except Exception as e:
|
| 1704 |
-
error_msg = f"Error importing patient {patient_data.get('full_name', 'Unknown')}: {str(e)}"
|
| 1705 |
-
errors.append(error_msg)
|
| 1706 |
-
logger.error(error_msg)
|
| 1707 |
-
|
| 1708 |
return {
|
| 1709 |
-
"message":
|
| 1710 |
-
"generated_count": generation_result
|
| 1711 |
-
"
|
| 1712 |
-
"
|
| 1713 |
-
"
|
| 1714 |
-
"
|
| 1715 |
-
"config": generation_result
|
| 1716 |
-
"source": generation_result.get('source', 'synthea')
|
| 1717 |
}
|
| 1718 |
|
| 1719 |
except Exception as e:
|
|
|
|
| 1678 |
"errors": []
|
| 1679 |
}
|
| 1680 |
|
| 1681 |
+
# The patients are already saved to database by the service
|
| 1682 |
+
# Just return the results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1683 |
return {
|
| 1684 |
+
"message": "Synthea patients generated and saved to database successfully",
|
| 1685 |
+
"generated_count": generation_result.get('generated_patients', 0),
|
| 1686 |
+
"saved_to_database": generation_result.get('saved_to_database', 0),
|
| 1687 |
+
"failed_to_save": generation_result.get('failed_to_save', 0),
|
| 1688 |
+
"database_errors": generation_result.get('database_errors', []),
|
| 1689 |
+
"source": generation_result.get('source', 'unknown'),
|
| 1690 |
+
"config": generation_result.get('config', {})
|
|
|
|
| 1691 |
}
|
| 1692 |
|
| 1693 |
except Exception as e:
|
api/services/synthea_integration.py
CHANGED
|
@@ -10,6 +10,8 @@ from typing import Dict, List, Optional, Any
|
|
| 10 |
import aiofiles
|
| 11 |
import aiohttp
|
| 12 |
from fastapi import HTTPException, status
|
|
|
|
|
|
|
| 13 |
|
| 14 |
logger = logging.getLogger(__name__)
|
| 15 |
|
|
@@ -208,6 +210,28 @@ class SyntheaIntegrationService:
|
|
| 208 |
]
|
| 209 |
patient_files.extend(subdir_files)
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
logger.info(f"📁 Found {len(patient_files)} patient files")
|
| 212 |
logger.info(f"📁 Patient file names: {[f.name for f in patient_files]}")
|
| 213 |
|
|
@@ -419,6 +443,72 @@ class SyntheaIntegrationService:
|
|
| 419 |
except:
|
| 420 |
return []
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
async def generate_and_import_patients(
|
| 423 |
self,
|
| 424 |
population: int = 10,
|
|
@@ -468,9 +558,19 @@ class SyntheaIntegrationService:
|
|
| 468 |
# Process output
|
| 469 |
patients = await self.process_synthea_output()
|
| 470 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
return {
|
| 472 |
"status": "success",
|
| 473 |
"generated_patients": len(patients),
|
|
|
|
|
|
|
|
|
|
| 474 |
"patients": patients,
|
| 475 |
"config": config_overrides,
|
| 476 |
"output_directory": str(self.output_dir),
|
|
@@ -559,9 +659,19 @@ class SyntheaIntegrationService:
|
|
| 559 |
|
| 560 |
patients.append(patient_data)
|
| 561 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 562 |
return {
|
| 563 |
"status": "success",
|
| 564 |
"generated_patients": len(patients),
|
|
|
|
|
|
|
|
|
|
| 565 |
"patients": patients,
|
| 566 |
"config": {
|
| 567 |
"population": population,
|
|
|
|
| 10 |
import aiofiles
|
| 11 |
import aiohttp
|
| 12 |
from fastapi import HTTPException, status
|
| 13 |
+
from bson import ObjectId
|
| 14 |
+
from db.mongo import patients_collection
|
| 15 |
|
| 16 |
logger = logging.getLogger(__name__)
|
| 17 |
|
|
|
|
| 210 |
]
|
| 211 |
patient_files.extend(subdir_files)
|
| 212 |
|
| 213 |
+
# If no files found, try alternative locations
|
| 214 |
+
if not patient_files:
|
| 215 |
+
logger.warning("⚠️ No patient files found in output directory, trying alternative locations...")
|
| 216 |
+
|
| 217 |
+
# Try parent directory
|
| 218 |
+
parent_dir = self.output_dir.parent
|
| 219 |
+
logger.info(f"🔍 Checking parent directory: {parent_dir}")
|
| 220 |
+
parent_files = [
|
| 221 |
+
f for f in parent_dir.glob("*.json")
|
| 222 |
+
if not any(x in f.name for x in ["hospitalInformation", "practitionerInformation"])
|
| 223 |
+
]
|
| 224 |
+
patient_files.extend(parent_files)
|
| 225 |
+
|
| 226 |
+
# Try current working directory
|
| 227 |
+
cwd = Path.cwd()
|
| 228 |
+
logger.info(f"🔍 Checking current working directory: {cwd}")
|
| 229 |
+
cwd_files = [
|
| 230 |
+
f for f in cwd.glob("*.json")
|
| 231 |
+
if not any(x in f.name for x in ["hospitalInformation", "practitionerInformation"])
|
| 232 |
+
]
|
| 233 |
+
patient_files.extend(cwd_files)
|
| 234 |
+
|
| 235 |
logger.info(f"📁 Found {len(patient_files)} patient files")
|
| 236 |
logger.info(f"📁 Patient file names: {[f.name for f in patient_files]}")
|
| 237 |
|
|
|
|
| 443 |
except:
|
| 444 |
return []
|
| 445 |
|
| 446 |
+
async def save_patients_to_database(self, patients: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 447 |
+
"""
|
| 448 |
+
Save generated patients directly to the database
|
| 449 |
+
"""
|
| 450 |
+
try:
|
| 451 |
+
saved_count = 0
|
| 452 |
+
failed_count = 0
|
| 453 |
+
errors = []
|
| 454 |
+
|
| 455 |
+
for patient in patients:
|
| 456 |
+
try:
|
| 457 |
+
# Prepare patient document for database
|
| 458 |
+
patient_doc = {
|
| 459 |
+
'_id': ObjectId(),
|
| 460 |
+
'fhir_id': patient.get('fhir_id', f"synthea-{ObjectId()}"),
|
| 461 |
+
'full_name': patient.get('full_name', ''),
|
| 462 |
+
'gender': patient.get('gender', 'unknown'),
|
| 463 |
+
'date_of_birth': patient.get('date_of_birth'),
|
| 464 |
+
'address': patient.get('address', ''),
|
| 465 |
+
'city': patient.get('city', ''),
|
| 466 |
+
'state': patient.get('state', ''),
|
| 467 |
+
'postal_code': patient.get('postal_code', ''),
|
| 468 |
+
'country': patient.get('country', 'US'),
|
| 469 |
+
'marital_status': patient.get('marital_status', ''),
|
| 470 |
+
'language': patient.get('language', 'English'),
|
| 471 |
+
'source': patient.get('source', 'synthea'),
|
| 472 |
+
'import_date': datetime.utcnow(),
|
| 473 |
+
'last_updated': datetime.utcnow(),
|
| 474 |
+
'conditions': patient.get('conditions', []),
|
| 475 |
+
'medications': patient.get('medications', []),
|
| 476 |
+
'encounters': patient.get('encounters', []),
|
| 477 |
+
'observations': patient.get('observations', []),
|
| 478 |
+
'procedures': patient.get('procedures', []),
|
| 479 |
+
'immunizations': patient.get('immunizations', []),
|
| 480 |
+
'allergies': patient.get('allergies', []),
|
| 481 |
+
'notes': []
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
# Insert into database
|
| 485 |
+
result = await patients_collection.insert_one(patient_doc)
|
| 486 |
+
saved_count += 1
|
| 487 |
+
logger.info(f"✅ Saved patient {patient.get('full_name', 'Unknown')} with ID: {result.inserted_id}")
|
| 488 |
+
|
| 489 |
+
except Exception as e:
|
| 490 |
+
failed_count += 1
|
| 491 |
+
error_msg = f"Failed to save patient {patient.get('full_name', 'Unknown')}: {str(e)}"
|
| 492 |
+
errors.append(error_msg)
|
| 493 |
+
logger.error(f"❌ {error_msg}")
|
| 494 |
+
continue
|
| 495 |
+
|
| 496 |
+
return {
|
| 497 |
+
"saved_count": saved_count,
|
| 498 |
+
"failed_count": failed_count,
|
| 499 |
+
"errors": errors,
|
| 500 |
+
"success": saved_count > 0
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
except Exception as e:
|
| 504 |
+
logger.error(f"❌ Error saving patients to database: {str(e)}")
|
| 505 |
+
return {
|
| 506 |
+
"saved_count": 0,
|
| 507 |
+
"failed_count": len(patients),
|
| 508 |
+
"errors": [f"Database error: {str(e)}"],
|
| 509 |
+
"success": False
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
async def generate_and_import_patients(
|
| 513 |
self,
|
| 514 |
population: int = 10,
|
|
|
|
| 558 |
# Process output
|
| 559 |
patients = await self.process_synthea_output()
|
| 560 |
|
| 561 |
+
# Save patients to database
|
| 562 |
+
if patients:
|
| 563 |
+
db_result = await self.save_patients_to_database(patients)
|
| 564 |
+
logger.info(f"💾 Database save result: {db_result}")
|
| 565 |
+
else:
|
| 566 |
+
db_result = {"saved_count": 0, "failed_count": 0, "errors": ["No patients to save"], "success": False}
|
| 567 |
+
|
| 568 |
return {
|
| 569 |
"status": "success",
|
| 570 |
"generated_patients": len(patients),
|
| 571 |
+
"saved_to_database": db_result["saved_count"],
|
| 572 |
+
"failed_to_save": db_result["failed_count"],
|
| 573 |
+
"database_errors": db_result["errors"],
|
| 574 |
"patients": patients,
|
| 575 |
"config": config_overrides,
|
| 576 |
"output_directory": str(self.output_dir),
|
|
|
|
| 659 |
|
| 660 |
patients.append(patient_data)
|
| 661 |
|
| 662 |
+
# Save mock patients to database
|
| 663 |
+
if patients:
|
| 664 |
+
db_result = await self.save_patients_to_database(patients)
|
| 665 |
+
logger.info(f"💾 Mock patients database save result: {db_result}")
|
| 666 |
+
else:
|
| 667 |
+
db_result = {"saved_count": 0, "failed_count": 0, "errors": ["No mock patients to save"], "success": False}
|
| 668 |
+
|
| 669 |
return {
|
| 670 |
"status": "success",
|
| 671 |
"generated_patients": len(patients),
|
| 672 |
+
"saved_to_database": db_result["saved_count"],
|
| 673 |
+
"failed_to_save": db_result["failed_count"],
|
| 674 |
+
"database_errors": db_result["errors"],
|
| 675 |
"patients": patients,
|
| 676 |
"config": {
|
| 677 |
"population": population,
|