Skip to content

Commit 56c7f3d

Browse files
committed
Basic FHIR export and release new ver
Final update for new minor version release. Basic FHIR-compatible format export functionality is implemented, as a stepping stone to more advanced FHIR export methods. Currently, the data in db is used to build FHIR r4 pharmacogenomic reporting IG style reports in JSON and XML format. These two new files can be found in the same reports dir. By default this functionality is enabled, but can be explicitly set with an env flag.
1 parent cf0a8af commit 56c7f3d

File tree

12 files changed

+1884
-11
lines changed

12 files changed

+1884
-11
lines changed

app/api/routes/fhir_export_router.py

Lines changed: 585 additions & 0 deletions
Large diffs are not rendered by default.

app/api/routes/pharmcat_router.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,5 +412,5 @@ async def health_check():
412412
return {
413413
"status": "healthy",
414414
"service": "pharmcat-api",
415-
"version": "0.2.2"
415+
"version": "0.2.3"
416416
}

app/main.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from app.api.routes.monitoring import router as monitoring_router
3838
from app.api.routes.workflow_router import router as workflow_router
3939
from app.api.routes.pharmcat_router import router as pharmcat_router
40+
from app.api.routes.fhir_export_router import router as fhir_export_router
4041
from app.pharmcat import pharmcat_client
4142
from app.pharmcat.pharmcat_client import call_pharmcat_service, normalize_pharmcat_results
4243
from app.reports.generator import create_interactive_html_report, generate_pdf_report, generate_report
@@ -99,6 +100,7 @@ def _env_flag(name: str, default: bool = False) -> bool:
99100
KROKI_ENABLED = _env_flag("KROKI_ENABLED", True)
100101
HAPI_FHIR_ENABLED = _env_flag("HAPI_FHIR_ENABLED", True)
101102
OUTSIDE_CALLS_OVERRIDE_ENABLED = _env_flag("OUTSIDECALLSOVERRIDE", False)
103+
FHIR_EXPORT_ENABLED = _env_flag("FHIR_EXPORT_ENABLED", True) # Enable FHIR export by default
102104
TEMP_DIR = Path("/tmp")
103105
DATA_DIR = Path("/data")
104106
REPORTS_DIR = Path(os.getenv("REPORT_DIR", "/data/reports"))
@@ -246,6 +248,13 @@ async def ensure_docs_built_on_start() -> None:
246248
app.include_router(workflow_router)
247249
app.include_router(pharmcat_router)
248250

251+
# Conditionally include FHIR export router (enabled by default)
252+
if FHIR_EXPORT_ENABLED:
253+
app.include_router(fhir_export_router)
254+
logger.info("FHIR export functionality enabled (endpoints at /fhir/*)")
255+
else:
256+
logger.info("FHIR export functionality disabled (set FHIR_EXPORT_ENABLED=true to enable)")
257+
249258
# Override and disable authentication in development mode
250259
if os.getenv("ZAROPGX_DEV_MODE", "true").lower() == "true":
251260
# Print warning about development mode
@@ -1129,7 +1138,12 @@ async def services_config():
11291138
"optitype": {"enabled": OPTITYPE_ENABLED},
11301139
"genome_downloader": {"enabled": GENOME_DOWNLOADER_ENABLED},
11311140
"kroki": {"enabled": KROKI_ENABLED},
1132-
"hapi_fhir": {"enabled": HAPI_FHIR_ENABLED}
1141+
"hapi_fhir": {"enabled": HAPI_FHIR_ENABLED},
1142+
"fhir_export": {
1143+
"enabled": FHIR_EXPORT_ENABLED,
1144+
"description": "FHIR R4 export for pharmacogenomic reports",
1145+
"endpoints": "/fhir/*" if FHIR_EXPORT_ENABLED else None
1146+
}
11331147
}
11341148
}
11351149

app/reports/generator.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
# Keep below import commented out; this prevents circular import
3030
# from app.reports.pdf_generators import generate_pdf_report_dual_lane
3131

32+
# FHIR Export - import lazily to avoid circular imports
33+
# from app.services.fhir_export_service import FHIRExportService, FHIR_EXPORT_ENABLED
34+
3235

3336
# Do not hardcode; derive from pyproject when available
3437
__version__ = "0.0.0"
@@ -269,9 +272,13 @@ def _env_flag(name: str, default: bool = False) -> bool:
269272
INCLUDE_PHARMCAT_TSV = _env_flag("INCLUDE_PHARMCAT_TSV", False)
270273
EXECSUM_USE_TSV = _env_flag("EXECSUM_USE_TSV", False)
271274

275+
# FHIR Export - automatically generate FHIR R4 exports during report generation
276+
FHIR_EXPORT_ENABLED = _env_flag("FHIR_EXPORT_ENABLED", True)
277+
272278
# Log the configuration for debugging
273279
logger.info(f"PharmCAT Report Configuration - HTML: {INCLUDE_PHARMCAT_HTML}, JSON: {INCLUDE_PHARMCAT_JSON}, TSV: {INCLUDE_PHARMCAT_TSV}")
274280
logger.info(f"Executive Summary Configuration - Use TSV: {EXECSUM_USE_TSV}")
281+
logger.info(f"FHIR Export Configuration - Enabled: {FHIR_EXPORT_ENABLED}")
275282

276283
# Report configuration dictionary
277284
REPORT_CONFIG = {
@@ -290,6 +297,9 @@ def _env_flag(name: str, default: bool = False) -> bool:
290297
"show_pharmcat_html_report": INCLUDE_PHARMCAT_HTML, # Original HTML report from PharmCAT
291298
"show_pharmcat_json_report": INCLUDE_PHARMCAT_JSON, # Original JSON report from PharmCAT
292299
"show_pharmcat_tsv_report": INCLUDE_PHARMCAT_TSV, # Original TSV report from PharmCAT
300+
301+
# FHIR Export - generate FHIR R4 compliant exports
302+
"generate_fhir_export": FHIR_EXPORT_ENABLED, # FHIR JSON/XML exports
293303
}
294304

295305
# Configure WeasyPrint logging for debugging text rendering issues
@@ -2496,6 +2506,86 @@ def generate_report(pharmcat_results: Dict[str, Any], output_dir: str, patient_i
24962506
else:
24972507
logger.debug("PharmCAT TSV report processing disabled via INCLUDE_PHARMCAT_TSV environment variable")
24982508

2509+
# FHIR Export - Generate FHIR R4 compliant exports if enabled
2510+
if REPORT_CONFIG["generate_fhir_export"]:
2511+
logger.info("=== FHIR EXPORT GENERATION START ===")
2512+
try:
2513+
# Import lazily to avoid circular imports
2514+
from app.services.fhir_export_service import FHIRExportService
2515+
2516+
# We need a database session for the FHIR export service
2517+
if db_session:
2518+
fhir_service = FHIRExportService(db_session)
2519+
2520+
# Get the PharmCAT run_id - it might be in workflow metadata or we can use patient_id
2521+
pharmcat_run_id = None
2522+
if workflow_id:
2523+
try:
2524+
from app.api.db import Workflow
2525+
import uuid as uuid_module
2526+
workflow_uuid = uuid_module.UUID(str(workflow_id))
2527+
workflow_obj = db_session.query(Workflow).filter(Workflow.id == workflow_uuid).first()
2528+
if workflow_obj and workflow_obj.workflow_metadata:
2529+
pharmcat_run_id = workflow_obj.workflow_metadata.get('pharmcat_run_id')
2530+
logger.info(f"Found PharmCAT run_id in workflow metadata: {pharmcat_run_id}")
2531+
except Exception as e:
2532+
logger.warning(f"Could not get PharmCAT run_id from workflow: {e}")
2533+
2534+
if not pharmcat_run_id:
2535+
logger.info("No explicit PharmCAT run_id found; using workflow data for FHIR export")
2536+
2537+
# Prepare patient info for FHIR export
2538+
fhir_patient_info = None
2539+
if patient_info:
2540+
fhir_patient_info = {
2541+
"id": patient_info.get("id", patient_id),
2542+
"name": patient_info.get("name"),
2543+
"gender": patient_info.get("gender"),
2544+
"birthDate": patient_info.get("birthDate"),
2545+
}
2546+
2547+
# Generate both JSON and XML FHIR exports directly from normalized data
2548+
fhir_result = fhir_service.save_fhir_export(
2549+
run_id=pharmcat_run_id,
2550+
patient_id=patient_id,
2551+
patient_info=fhir_patient_info,
2552+
output_format="both", # Generate both JSON and XML
2553+
include_recommendations=True,
2554+
pharmcat_data=data,
2555+
workflow_id=workflow_id,
2556+
)
2557+
2558+
if fhir_result.get("success"):
2559+
files_saved = fhir_result.get("files_saved", [])
2560+
for file_info in files_saved:
2561+
fmt = file_info.get("format", "")
2562+
url = file_info.get("url", "")
2563+
if fmt == "json":
2564+
report_paths["fhir_json_path"] = url
2565+
data["fhir_json_url"] = url
2566+
logger.info(f"✓ FHIR JSON export generated: {url}")
2567+
elif fmt == "xml":
2568+
report_paths["fhir_xml_path"] = url
2569+
data["fhir_xml_url"] = url
2570+
logger.info(f"✓ FHIR XML export generated: {url}")
2571+
2572+
# Mark workflow as having FHIR export
2573+
data["exported_to_fhir"] = True
2574+
else:
2575+
logger.warning(f"FHIR export failed: {fhir_result.get('error', 'Unknown error')}")
2576+
else:
2577+
logger.warning("No database session available - skipping FHIR export")
2578+
2579+
except ImportError as e:
2580+
logger.warning(f"FHIR export service not available: {e}")
2581+
except Exception as e:
2582+
logger.error(f"FHIR export generation failed: {e}", exc_info=True)
2583+
# Don't fail the entire report generation if FHIR export fails
2584+
2585+
logger.info("=== FHIR EXPORT GENERATION END ===")
2586+
else:
2587+
logger.debug("FHIR export disabled via FHIR_EXPORT_ENABLED environment variable")
2588+
24992589
# Add the processed data to the report paths for reference
25002590
report_paths["processed_data"] = data
25012591

0 commit comments

Comments
 (0)