File size: 56,342 Bytes
5372c12 12a5b40 5372c12 efa4352 85e2206 0d70fae f10ca6a 0d70fae 5372c12 85e2206 0fd27e0 7f2013e 12a5b40 f10ca6a 7dc8dea 53876d7 7dc8dea 53876d7 cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a 7dc8dea 53876d7 f10ca6a 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 7dc8dea cb9fa5d 53876d7 7dc8dea 53876d7 cb9fa5d f10ca6a cb9fa5d 66c4a5b 53876d7 66c4a5b 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 7dc8dea cb9fa5d 53876d7 7dc8dea 53876d7 cb9fa5d f10ca6a cb9fa5d 53876d7 f10ca6a 7dc8dea 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 7dc8dea 12a5b40 cb9fa5d 7dc8dea 53876d7 7dc8dea 53876d7 12a5b40 53876d7 cb9fa5d f10ca6a cb9fa5d 53876d7 12a5b40 53876d7 f10ca6a 7dc8dea 53876d7 f10ca6a 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 7dc8dea cb9fa5d 53876d7 66c4a5b 53876d7 7dc8dea 53876d7 cb9fa5d f10ca6a cb9fa5d 53876d7 f10ca6a 7dc8dea 53876d7 f10ca6a 53876d7 7dc8dea 53876d7 f10ca6a 7dc8dea cb9fa5d 53876d7 6e5f33b 53876d7 cb9fa5d f10ca6a cb9fa5d 53876d7 5372c12 efa4352 3f9dd86 efa4352 f087af0 3f9dd86 efa4352 f087af0 efa4352 5372c12 f10ca6a 66c4a5b f10ca6a 66c4a5b 5372c12 0d70fae 66c4a5b 12a5b40 0d70fae f10ca6a 66c4a5b f10ca6a 66c4a5b 0d70fae 66c4a5b 5372c12 85e2206 66c4a5b 12a5b40 85e2206 f10ca6a 85e2206 66c4a5b f10ca6a 12a5b40 5372c12 f10ca6a 5372c12 f10ca6a f087af0 f10ca6a 5372c12 f10ca6a 85e2206 f10ca6a 85e2206 12a5b40 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 f10ca6a 85e2206 12a5b40 85e2206 12a5b40 85e2206 12a5b40 85e2206 f10ca6a 85e2206 f10ca6a 85e2206 12a5b40 cb9fa5d 85e2206 cb9fa5d f10ca6a 85e2206 12a5b40 cb9fa5d 85e2206 f10ca6a 12a5b40 f10ca6a 12a5b40 85e2206 f10ca6a 12a5b40 cb9fa5d 12a5b40 f10ca6a 12a5b40 cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d 12a5b40 f10ca6a cb9fa5d 12a5b40 f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d f10ca6a cb9fa5d 12a5b40 f10ca6a d82d227 f10ca6a d82d227 f10ca6a d82d227 f10ca6a 53876d7 f201971 5372c12 f10ca6a 66c4a5b 5372c12 f10ca6a 66c4a5b f201971 12a5b40 cb9fa5d 66c4a5b 12a5b40 d82d227 66c4a5b d82d227 66c4a5b d82d227 66c4a5b d82d227 f10ca6a d82d227 66c4a5b 12a5b40 f201971 f10ca6a 66c4a5b 12a5b40 f201971 12a5b40 f201971 12a5b40 53876d7 66c4a5b f201971 12a5b40 f201971 12a5b40 f10ca6a f201971 f10ca6a f201971 12a5b40 f201971 66c4a5b f201971 66c4a5b 12a5b40 66c4a5b f10ca6a 66c4a5b f207c28 5372c12 f207c28 5372c12 f207c28 5372c12 66c4a5b f207c28 5372c12 f10ca6a 5372c12 f207c28 5372c12 66c4a5b f207c28 5372c12 f207c28 5372c12 f10ca6a 5372c12 66c4a5b 5372c12 66c4a5b 5372c12 66c4a5b f207c28 5372c12 12a5b40 85e2206 f207c28 66c4a5b 12a5b40 85e2206 66c4a5b 12a5b40 85e2206 5372c12 f10ca6a 5372c12 66c4a5b 85e2206 12a5b40 53876d7 85e2206 f10ca6a 85e2206 66c4a5b 5372c12 12a5b40 66c4a5b f207c28 f10ca6a 12a5b40 66c4a5b 12a5b40 66c4a5b 12a5b40 66c4a5b 12a5b40 66c4a5b 12a5b40 66c4a5b 12a5b40 f10ca6a 66c4a5b f10ca6a 12a5b40 66c4a5b f10ca6a 66c4a5b cb9fa5d 66c4a5b cb9fa5d 66c4a5b cb9fa5d 66c4a5b 12a5b40 53876d7 12a5b40 f10ca6a 66c4a5b 12a5b40 f10ca6a 66c4a5b 12a5b40 f10ca6a 66c4a5b 12a5b40 f10ca6a 66c4a5b 12a5b40 f10ca6a 66c4a5b 12a5b40 cb9fa5d a8bfb0b cb9fa5d a8bfb0b cb9fa5d f10ca6a 5a9d3d5 f10ca6a 66c4a5b f10ca6a 66c4a5b f10ca6a 12a5b40 53876d7 f10ca6a 12a5b40 f10ca6a 12a5b40 f201971 30cb096 66c4a5b 30cb096 12a5b40 f201971 66c4a5b 12a5b40 cb9fa5d 30cb096 cb9fa5d 30cb096 66c4a5b 30cb096 12a5b40 f201971 a8bfb0b 30cb096 5372c12 66c4a5b f201971 393338e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 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 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 |
import gradio as gr
from meldrx import MeldRxAPI
import json
import os
import tempfile
from datetime import datetime
import traceback
import logging
from huggingface_hub import InferenceClient # Import InferenceClient
from urllib.parse import urlparse, parse_qs # Import URL parsing utilities
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Import PDF utilities
from utils.pdfutils import PDFGenerator, generate_discharge_summary
# Import necessary libraries for new file types and AI analysis functions
import pydicom # For DICOM
import hl7 # For HL7
from xml.etree import ElementTree # For XML and CCDA
from pypdf import PdfReader # For PDF
import csv # For CSV
import io # For IO operations
from PIL import Image # For image handling
system_instructions = """
**Discharge Guard - Medical Data Analysis Assistant**
**Core Role:** I am Discharge Guard, an advanced AI designed for deep medical data analysis and informational insights. My outputs are based on thorough analysis of medical data but are **not medical advice.**
**Important Guidelines:**
1. **Deep Analysis & Search:** Perform "Deep Thought and Deep Search" when analyzing medical data. This includes:
* Comprehensive data ingestion from various formats (HL7, FHIR, CCDA, DICOM, PDF, CSV, text).
* Multi-layered analysis: surface extraction, deep pattern identification, and inferential reasoning.
* Contextual understanding of medical data.
* Evidence-based approach, simulating cross-referencing with medical knowledge.
* Structured output with clear explanations.
2. **Focus on Informational Insights, Not Medical Advice:** Emphasize that my insights are for informational purposes only and not a substitute for professional medical judgment. **Never provide diagnoses or specific treatment recommendations.**
3. **Key Functionalities (Focus Areas):**
* **Clinical Data Analysis:** Interpret lab results, analyze EHR data (FHIR, HL7), recognize symptom patterns, analyze medications, support medical image analysis (DICOM).
* **Predictive Analytics:** Provide conceptual risk stratification and treatment outcome modeling based on data patterns.
* **Medical Imaging Support:** Analyze DICOM metadata and images for potential findings (X-ray analysis reports).
* **Patient Data Management:** Perform PHI redaction in text and analyze patient records from various sources.
4. **Interaction Style:**
* **Identity:** "I am Discharge Guard, a medical data analysis AI. My insights are informational only and not medical advice."
* **Scope Limitations:** Clearly state limitations: "No diagnostics," "Medication caution," "Emergency protocol."
* **Response Protocol:**
* Indicate "Deep Analysis" or "Deep Search" performed.
* Mention data sources and confidence levels (if applicable).
* Use medical terminology with optional layman's terms.
* For file analysis, provide a report title (e.g., "Deep X-Ray Analysis Report").
5. **Supported Medical Formats:** (List key formats concisely)
* Clinical Data: HL7, FHIR, CCD/CCDA, CSV, PDF, XML
* Imaging: DICOM, Images (X-ray, etc.)
6. **Data Source:** Access and prefer FHIR API endpoints from: https://app.meldrx.com/api/directories/fhir/endpoints.
**Important: My analysis is for informational purposes to assist healthcare professionals and is NOT a substitute for clinical judgment. Always recommend human expert verification for critical findings.**
"""
# Initialize Inference Client - Ensure YOUR_HF_TOKEN is set in environment variables or replace with your actual token
HF_TOKEN = os.getenv("HF_TOKEN") # Or replace with your actual token string
if not HF_TOKEN:
raise ValueError(
"HF_TOKEN environment variable not set. Please set your Hugging Face API token."
)
client = InferenceClient(api_key=HF_TOKEN)
model_name = "meta-llama/Llama-3.3-70B-Instruct" # Specify the model to use
def analyze_dicom_file_with_ai(dicom_file_path): # Modified to accept file path
"""Analyzes DICOM file metadata using Discharge Guard AI."""
try:
dicom_file = pydicom.dcmread(
dicom_file_path.name
) # Read dicom using path, access file through .name for Gradio UploadedFile
dicom_metadata_json = dicom_file.to_json_dict()
prediction_response, trace_data_dicom_ai = analyze_dicom_content_ai(
dicom_metadata_json
) # Call content analysis
if prediction_response:
report = f"Discharge Guard AI Analysis of DICOM Metadata:\n\nDICOM Metadata Analysis Report:\n{prediction_response}\n\nDisclaimer: The Discharge Guard -generated analysis is for conceptual informational purposes only and may or **NOT medical advice.** Analysis is based on DICOM *metadata* and not image interpretation."
return report
else:
error_message = f"AI Analysis from DICOM Metadata: No predictions generated or analysis encountered an issue."
if trace_data_dicom_ai and "error" in trace_data_dicom_ai:
error_message += f"\nAI Analysis Failed: {trace_data_dicom_ai['error']}"
return error_message
except Exception as e:
return f"Error during DICOM file processing in analyze_dicom_file_with_ai: {e}"
def analyze_dicom_content_ai(dicom_metadata_json): # Copied from your code
"""Analyzes DICOM metadata JSON content using Discharge Guard AI."""
prompt_text = f"""{system_instructions} \n\n Perform a **deep and comprehensive analysis** of the following DICOM metadata in JSON format to provide a **structured summary and identify potential clinically relevant information with deep insights**. Focus not just on summarizing fields, but on **interpreting their clinical significance, identifying subtle patterns, and drawing inferences about the study's implications**. Think like an experienced radiologist reviewing this metadata for crucial diagnostic clues. Remember this is metadata, not the image itself, so focus on what can be gleaned from the data itself. Provide a "**Deep DICOM Metadata Analysis Report**". Important: Use the API Directories fhir endpoints FROM THIS LINK: https://app.meldrx.com/api/directories/fhir/endpoints.
**DICOM Metadata (JSON):**
```json
{json.dumps(dicom_metadata_json, indent=2)}
```
* Remember, this deep analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on deep summarization and structuring the extracted metadata in a highly clinically relevant way.
"""
trace_data_detail_dicom_analysis = {
"prompt": "DICOM Metadata Analysis Request",
"language": "English",
"response_length": "Comprehensive",
"model_name": "Discharge Guard v1.0",
"generated_text": "N/A",
"input_file_types": ["DICOM Metadata JSON"],
"mode": "DICOM Metadata Analysis",
"candidates": [],
"usage_metadata": {},
"prompt_feedback": "N/A",
}
try:
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.4,
max_tokens=1024, # Adjust as needed
top_p=0.9,
)
the_response = response.choices[0].message.content
return the_response, trace_data_detail_dicom_ai
except Exception as e:
error_message = f"AI Analysis Error in analyze_dicom_content_ai (DICOM Metadata): {e}"
trace_data_detail_dicom_analysis["error"] = f"AI Analysis Error: {e}"
return error_message, trace_data_detail_dicom_image_analysis
def analyze_hl7_file_with_ai(hl7_file_path):
"""Analyzes HL7 file content using Discharge Guard AI."""
try:
with open(hl7_file_path.name, "r") as f: # Open file using path, access file through .name for Gradio UploadedFile
hl7_message_raw = f.read()
prediction_response, trace_data_hl7_ai = analyze_hl7_content_ai(
hl7_message_raw
)
if prediction_response:
report = f"Discharge Guard AI Analysis of HL7 Message:\n\nHL7 Message Analysis Report:\n{prediction_response}\n\n**Disclaimer:** The Discharge Guard AGI-generated analysis is for conceptual informational purposes only and may or **NOT medical advice.** Analysis is based on HL7 message content."
return report
else:
error_message = f"AI Analysis from HL7 Message: No predictions generated or analysis encountered an issue."
if trace_data_hl7_ai and "error" in trace_data_hl7_ai:
error_message += f"AI Analysis Failed: {trace_data_hl7_ai['error']}"
return error_message
except Exception as e:
return f"Error during HL7 file processing in analyze_hl7_file_with_ai: {e}"
def analyze_hl7_content_ai(hl7_message_string): # Copied from your code
"""Analyzes HL7 message content using Discharge Guard AI."""
prompt_text = f"""{system_instructions} \n\n Conduct a **deep and thorough analysis** of the following HL7 message content to provide a **structured summary and identify key clinical information with deep understanding**. Go beyond basic parsing; aim to **interpret the clinical narrative** embedded within the HL7 message. **Engage in deep search to contextualize medical codes and terminology**. Provide a "**Comprehensive HL7 Message Analysis Report**".
**HL7 Message Content:**
```hl7
{hl7_message_string}
```
* Remember, this deep analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on deep summarization and structuring the extracted data in a highly clinically relevant way based on the HL7 content.
"""
# ... (rest of the function code) ...
trace_data_detail_hl7_analysis = {
"prompt": "HL7 Message Analysis Request",
"language": "English",
"response_length": "Comprehensive",
"model_name": "Discharge Guard v1.0",
"generated_text": "N/A",
"input_file_types": ["HL7 Message"],
"mode": "HL7 Message Analysis",
"candidates": [],
"usage_metadata": {},
"prompt_feedback": "N/A",
}
try:
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.4,
max_tokens=1024, # Adjust as needed
top_p=0.9,
)
the_response = response.choices[0].message.content
return the_response, trace_data_detail_hl7_analysis
except Exception as e:
error_message = f"AI Analysis Error in analyze_hl7_content_ai (HL7 Message): {e}"
trace_data_detail_hl7_analysis["error"] = f"AI Analysis Error: {e}"
return error_message, trace_data_detail_hl7_analysis
def analyze_cda_xml_file_with_ai(cda_xml_file_path): # Modified to accept file path
"""Analyzes generic CDA or XML file content using Discharge Guard AI (more generalized version) Important: Use the API Directories fhir endpoints FROM THIS LINK: https://app.meldrx.com/api/directories/fhir/endpoints."""
try:
with open(
cda_xml_file_path.name, "r"
) as f: # Open file using path, access file through .name for Gradio UploadedFile
cda_xml_content = f.read()
prediction_response, trace_data_cda_xml_ai = analyze_cda_xml_content_ai(
cda_xml_content
)
if prediction_response:
report = f"Discharge Guard AI Analysis of Medical XML/CDA Data:\n\nMedical Document Analysis Report:\n{prediction_response}\n\n**Disclaimer:** The Discharge Guard AGI-generated analysis is for conceptual informational purposes only and may or **NOT medical advice.** Analysis is based on XML/CDA content."
return report
else:
error_message = f"AI Analysis from XML/CDA Data: No predictions generated or analysis encountered an issue."
if trace_data_cda_xml_ai and "error" in trace_data_cda_xml_ai:
error_message += f"AI Analysis Failed: {trace_data_cda_xml_ai['error']}"
return error_message
except Exception as e:
return f"Error during XML/CDA file processing in analyze_cda_xml_file_with_ai: {e}"
def analyze_cda_xml_content_ai(cda_xml_content): # Copied from your code
"""Analyzes generic CDA or XML content using Discharge Guard AI (more generalized version)."""
prompt_text = f"""{system_instructions} \n\n Analyze the following medical XML/CDA content to provide a **structured and comprehensive patient data analysis**, similar to how a medical professional would review a patient's chart or a clinical document. You need to parse the XML structure yourself to extract the relevant information. Use bullet points, tables, or numbered steps for complex tasks. Provide a "Medical Document Analysis" report.
**Instructions for Discharge Guard AI:**
1. **Parse the XML content above.** Understand the XML structure to identify sections that are relevant to clinical information. For CDA specifically, look for sections like Problems, Medications, Allergies, Encounters, Results, and Vital Signs. For generic medical XML, adapt based on the tags present.
2. **Extract and Summarize Key Medical Information:** Focus on extracting the following information if present in the XML:
* **Patient Demographics Summary:** (If available, summarize demographic details)
... (rest of your prompt_text for CDA/XML analysis) ...
* Remember, this analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on summarizing and structuring the extracted data in a clinically relevant way based on the XML/CDA content.
"""
trace_data_detail_cda_xml_analysis = {
"prompt": "Generic CDA/XML Analysis Request",
"language": "English",
"response_length": "Comprehensive",
"model_name": "Discharge Guard v1.0",
"generated_text": "N/A",
"input_file_types": ["CDA/XML"],
"mode": "Generic XML/CDA Analysis",
"candidates": [],
"usage_metadata": {},
"prompt_feedback": "N/A",
}
try:
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.4,
max_tokens=1024, # Adjust as needed
top_p=0.9,
)
the_response = response.choices[0].message.content
return the_response, trace_data_detail_cda_xml_analysis
except Exception as e:
error_message = f"AI Analysis Error in analyze_cda_xml_content_ai (Generic XML/CDA): {e}"
trace_data_detail_cda_xml_analysis["error"] = f"AI Analysis Error: {e}"
return error_message, trace_data_detail_cda_xml_analysis
def analyze_pdf_file_with_ai(pdf_file_path): # Modified to accept file path
"""Analyzes PDF file content using Discharge Guard AI."""
try:
with open(
pdf_file_path.name, "rb"
) as f: # Open file in binary mode for PdfReader, access file through .name for Gradio UploadedFile
pdf_file = f # Pass file object to PdfReader
pdf_reader = PdfReader(pdf_file)
text_content = ""
for page in pdf_reader.pages:
text_content += page.extract_text()
prediction_response, trace_data_pdf_ai = analyze_pdf_content_ai(
text_content
)
if prediction_response:
report = f"Discharge Guard AI Analysis of PDF Content:\n\nMedical Report Analysis Report:\n{prediction_response}\n\n**Disclaimer:** The Discharge Guard AGI-generated analysis is for conceptual informational purposes only and may or **NOT medical advice.** Analysis is based on PDF text content."
return report
else:
error_message = f"AI Analysis from PDF Content: No predictions generated or analysis encountered an issue."
if trace_data_pdf_ai and "error" in trace_data_pdf_ai:
error_message += f"AI Analysis Failed: {trace_data_pdf_ai['error']}"
return error_message
except Exception as e:
return f"Error during PDF file processing in analyze_pdf_file_with_ai: {e}"
def analyze_pdf_content_ai(pdf_text_content): # Copied from your code
"""Analyzes PDF text content using Discharge Guard AI."""
prompt_text = f"""{system_instructions} \n\n Analyze the following medical PDF text content to provide a **structured summary and identify key clinical information**. Focus on patient demographics, medical history, findings, diagnoses, medications, recommendations, and any important clinical details conveyed in the document. Provide a "Medical Report Analysis" report.
**Medical PDF Text Content:**
```text
{pdf_text_content}
```
* Remember, this analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on deep summarization and structuring the extracted data in a clinically relevant way based on the PDF content.
"""
trace_data_detail_pdf_analysis = {
"prompt": "PDF Text Analysis Request",
"language": "English",
"response_length": "Comprehensive",
"model_name": "Discharge Guard v1.0",
"generated_text": "N/A",
"input_file_types": ["PDF Text"],
"mode": "PDF Text Analysis",
"candidates": [],
"usage_metadata": {},
"prompt_feedback": "N/A",
}
try:
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.4,
max_tokens=1024, # Adjust as needed
top_p=0.9,
)
the_response = response.choices[0].message.content
return the_response, trace_data_detail_pdf_analysis
except Exception as e:
error_message = f"AI Analysis Error in analyze_pdf_content_ai (PDF Text): {e}"
trace_data_detail_pdf_analysis["error"] = f"AI Analysis Error: {e}"
return error_message, trace_data_detail_pdf_analysis
def analyze_csv_file_with_ai(csv_file_path): # Modified to accept file path
"""Analyzes CSV file content using Discharge Guard AI."""
try:
csv_content = csv_file_path.read().decode(
"utf-8"
) # Read content directly from UploadedFile
prediction_response, trace_data_csv_ai = analyze_csv_content_ai(csv_content)
if prediction_response:
report = f"Discharge Guard AI Analysis of CSV Data:\n\nData Analysis Report:\n{prediction_response}\n\n**Disclaimer:** The Discharge Guard AGI-generated analysis is for conceptual informational purposes only and may or **NOT medical advice.** Analysis is based on CSV data content."
return report
else:
error_message = f"AI Analysis from CSV Data: No predictions generated or analysis encountered an issue."
if trace_data_csv_ai and "error" in trace_data_csv_ai:
error_message += f"AI Analysis Failed: {trace_data_csv_ai['error']}"
return error_message
except Exception as e:
return f"Error during CSV file processing in analyze_csv_file_with_ai: {e}"
def analyze_csv_content_ai(csv_content_string): # Copied from your code
"""Analyzes CSV content (string) using Discharge Guard AI."""
prompt_text = f"""{system_instructions} \n\n Analyze the following medical CSV data to provide a **structured summary and identify potential clinical insights**. Assume the CSV represents patient-related medical data. Focus on understanding the columns, summarizing key data points, identifying trends or patterns, and noting any potential clinical significance of the data. Provide a "Data Analysis" report.
**Medical CSV Data:**
```csv
{csv_content_string}
```
* Remember, this analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on summarizing and structuring the data in a clinically relevant way based on the CSV content.
"""
trace_data_detail_csv_analysis = {
"prompt": "CSV Data Analysis Request",
"language": "English",
"response_length": "Comprehensive",
"model_name": "Discharge Guard v1.0",
"generated_text": "N/A",
"input_file_types": ["CSV Data"],
"mode": "CSV Data Analysis",
"candidates": [],
"usage_metadata": {},
"prompt_feedback": "N/A",
}
try:
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.4,
max_tokens=1024, # Adjust as needed
top_p=0.9,
)
the_response = response.choices[0].message.content
return the_response, trace_data_detail_csv_analysis
except Exception as e:
error_message = f"AI Analysis Error in analyze_csv_content_ai (CSV Data): {e}"
trace_data_detail_csv_analysis["error"] = f"AI Analysis Error: {e}"
return error_message, trace_data_detail_csv_analysis
class CallbackManager:
def __init__(self, redirect_uri: str, client_secret: str = None):
client_id = os.getenv("APPID")
if not client_id:
raise ValueError("APPID environment variable not set.")
workspace_id = os.getenv("WORKSPACE_URL")
if not workspace_id:
raise ValueError("WORKSPACE_URL environment variable not set.")
self.api = MeldRxAPI(client_id, client_secret, workspace_id, redirect_uri)
self.auth_code = None
self.access_token = None
def get_auth_url(self) -> str:
return self.api.get_authorization_url()
def set_auth_code(self, code: str) -> str:
self.auth_code = code
if self.api.authenticate_with_code(code):
self.access_token = self.api.access_token
return (
f"<span style='color:#00FF7F;'>Authentication successful!</span> Access Token: {self.access_token[:10]}... (truncated)" # Neon Green Success
)
return "<span style='color:#FF4500;'>Authentication failed. Please check the code.</span>" # Neon Orange Error
def get_patient_data(self) -> str:
"""Fetch patient data from MeldRx"""
try:
if not self.access_token:
logger.warning("Not authenticated when getting patient data")
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
# Real implementation with API call
logger.info("Calling Meldrx API to get patients")
patients = self.api.get_patients()
if patients is not None:
return (
json.dumps(patients, indent=2)
if patients
else "<span style='color:#FFFF00;'>No patient data returned.</span>" # Neon Yellow
)
return "<span style='color:#DC143C;'>Failed to retrieve patient data.</span>" # Crimson Error
except Exception as e:
error_msg = f"Error in get_patient_data: {str(e)}"
logger.error(error_msg)
return f"<span style='color:#FF6347;'>Error retrieving patient data: {str(e)}</span> {str(e)}" # Tomato Error
def get_patient_documents(self, patient_id: str = None):
"""Fetch patient documents from MeldRx"""
if not self.access_token:
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
try:
# This would call the actual MeldRx API to get documents for a specific patient
# For demonstration, we'll return mock document data
return [
{
"doc_id": "doc123",
"type": "clinical_note",
"date": "2023-01-16",
"author": "Dr. Sample Doctor",
"content": "Patient presented with symptoms of respiratory distress...",
},
{
"doc_id": "doc124",
"type": "lab_result",
"date": "2023-01-17",
"author": "Lab System",
"content": "CBC results: WBC 7.5, RBC 4.2, Hgb 14.1...",
},
]
except Exception as e:
return f"<span style='color:#FF6347;'>Error retrieving patient documents: {str(e)}</span>: {str(e)}" # Tomato Error
def display_form(
first_name,
last_name,
middle_initial,
dob,
age,
sex,
address,
city,
state,
zip_code,
doctor_first_name,
doctor_last_name,
doctor_middle_initial,
hospital_name,
doctor_address,
doctor_city,
doctor_state,
doctor_zip,
admission_date,
referral_source,
admission_method,
discharge_date,
discharge_reason,
date_of_death,
diagnosis,
procedures,
medications,
preparer_name,
preparer_job_title,
):
form = f"""
<div style='color:#00FFFF; font-family: monospace;'>
**Patient Discharge Form** <br>
- Name: {first_name} {middle_initial} {last_name} <br>
- Date of Birth: {dob}, Age: {age}, Sex: {sex} <br>
- Address: {address}, {city}, {state}, {zip_code} <br>
- Doctor: {doctor_first_name} {doctor_middle_initial} {doctor_last_name} <br>
- Hospital/Clinic: {hospital_name} <br>
- Doctor Address: {doctor_address}, {doctor_city}, {doctor_state}, {doctor_zip} <br>
- Admission Date: {admission_date}, Source: {referral_source}, Method: {admission_method} <br>
- Discharge Date: {discharge_date}, Reason: {discharge_reason} <br>
- Date of Death: {date_of_death} <br>
- Diagnosis: {diagnosis} <br>
- Procedures: {procedures} <br>
- Medications: {medications} <br>
- Prepared By: {preparer_name}, {preparer_job_title}
</div>
"""
return form
def generate_pdf_from_form(
first_name,
last_name,
middle_initial,
dob,
age,
sex,
address,
city,
state,
zip_code,
doctor_first_name,
doctor_last_name,
doctor_middle_initial,
hospital_name,
doctor_address,
doctor_city,
doctor_state,
doctor_zip,
admission_date,
referral_source,
admission_method,
discharge_date,
discharge_reason,
date_of_death,
diagnosis,
procedures,
medications,
preparer_name,
preparer_job_title,
):
"""Generate a PDF discharge form using the provided data"""
# Create PDF generator
pdf_gen = PDFGenerator()
# Format data for PDF generation
patient_info = {
"first_name": first_name,
"last_name": last_name,
"dob": dob,
"age": age,
"sex": sex,
"mobile": "", # Not collected in the form
"address": address,
"city": city,
"state": state,
"zip": zip_code,
}
discharge_info = {
"date_of_admission": admission_date,
"date_of_discharge": discharge_date,
"source_of_admission": referral_source,
"mode_of_admission": admission_method,
"discharge_against_advice": "Yes"
if discharge_reason == "Discharge Against Advice"
else "No",
}
diagnosis_info = {
"diagnosis": diagnosis,
"operation_procedure": procedures,
"treatment": "", # Not collected in the form
"follow_up": "", # Not collected in the form
}
medication_info = {
"medications": [medications] if medications else [],
"instructions": "", # Not collected in the form
}
prepared_by = {
"name": preparer_name,
"title": preparer_job_title,
"signature": "", # Not collected in the form
}
# Generate PDF
pdf_buffer = pdf_gen.generate_discharge_form(
patient_info,
discharge_info,
diagnosis_info,
medication_info,
prepared_by,
)
# Create temporary file to save the PDF
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
temp_file.write(pdf_buffer.read())
temp_file_path = temp_file.name
temp_file.close()
return temp_file_path
def generate_pdf_from_meldrx(patient_data):
"""Generate a PDF using patient data from MeldRx"""
if isinstance(patient_data, str):
# If it's a string (error message or JSON string), try to parse it
try:
patient_data = json.loads(patient_data)
except:
return None, "Invalid patient data format"
if not patient_data:
return None, "No patient data available"
try:
# For demonstration, we'll use the first patient in the list if it's a list
if isinstance(patient_data, list) and len(patient_data):
patient = patient_data[0]
else:
patient = patient_data
# Extract patient info
patient_info = {
"name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
"dob": patient.get("birthDate", "Unknown"),
"patient_id": patient.get("id", "Unknown"),
"admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
"physician": "Dr. Provider", # Mock data
}
# Mock LLM-generated content - This part needs to be replaced with actual AI generation if desired for MeldRx PDF
llm_content = {
"diagnosis": "Diagnosis information would be generated by AI based on patient data from MeldRx.",
"treatment": "Treatment summary would be generated by AI based on patient data from MeldRx.",
"medications": "Medication list would be generated by AI based on patient data from MeldRx.",
"follow_up": "Follow-up instructions would be generated by AI based on patient data from MeldRx.",
"special_instructions": "Special instructions would be generated by AI based on patient data from MeldRx.",
}
# Create discharge summary - Using No-AI PDF generation for now, replace with AI-content generation later
output_dir = tempfile.mkdtemp()
pdf_path = generate_discharge_summary(
patient_info, llm_content, output_dir
) # Still using No-AI template
return pdf_path, "PDF generated successfully (No AI Content in PDF yet)" # Indicate No-AI content
except Exception as e:
return None, f"Error generating PDF: {str(e)}"
def generate_discharge_paper_one_click():
"""One-click function to fetch patient data and generate discharge paper with AI Content."""
patient_data_str = CALLBACK_MANAGER.get_patient_data()
if (
patient_data_str.startswith("Not authenticated")
or patient_data_str.startswith("Failed")
or patient_data_str.startswith("Error")
):
return None, patient_data_str # Return error message if authentication or data fetch fails
try:
patient_data = json.loads(patient_data_str)
# --- AI Content Generation for Discharge Summary ---
# This is a placeholder - Replace with actual AI call using InferenceClient and patient_data to generate content
ai_generated_content = generate_ai_discharge_content(
patient_data
) # Placeholder AI function
if not ai_generated_content:
return None, "Error: AI content generation failed."
# --- PDF Generation with AI Content ---
pdf_path, status_message = generate_pdf_from_meldrx_with_ai_content(
patient_data, ai_generated_content
) # Function to generate PDF with AI content
if pdf_path:
return pdf_path, status_message
else:
return None, status_message # Return status message if PDF generation fails
except json.JSONDecodeError:
return None, "Error: Patient data is not in valid JSON format."
except Exception as e:
return None, f"Error during discharge paper generation: {str(e)}"
def generate_ai_discharge_content(patient_data):
"""Placeholder function to generate AI content for discharge summary.
Replace this with actual AI call using InferenceClient and patient_data."""
try:
patient_name = (
f"{patient_data['entry'][0]['resource']['name'][0]['given'][0]} {patient_data['entry'][0]['resource']['name'][0]['family']}"
if patient_data.get("entry")
else "Unknown Patient"
)
prompt_text = f"""{system_instructions}\n\nGenerate a discharge summary content (diagnosis, treatment, medications, follow-up instructions, special instructions) for patient: {patient_name}. Base the content on available patient data (if any provided, currently not provided in detail in this mock-up). Focus on creating clinically relevant and informative summary. Remember this is for informational purposes and NOT medical advice."""
response = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt_text}],
temperature=0.6, # Adjust temperature as needed for content generation
max_tokens=1024, # Adjust max_tokens as needed
top_p=0.9,
)
ai_content = response.choices[0].message.content
# Basic parsing of AI content - improve this based on desired output structure from LLM
llm_content = {
"diagnosis": "AI Generated Diagnosis (Placeholder):\n"
+ extract_section(ai_content, "Diagnosis"), # Example extraction - refine based on LLM output
"treatment": "AI Generated Treatment (Placeholder):\n"
+ extract_section(ai_content, "Treatment"),
"medications": "AI Generated Medications (Placeholder):\n"
+ extract_section(ai_content, "Medications"),
"follow_up": "AI Generated Follow-up (Placeholder):\n"
+ extract_section(ai_content, "Follow-up Instructions"),
"special_instructions": "AI Generated Special Instructions (Placeholder):\n"
+ extract_section(ai_content, "Special Instructions"),
}
return llm_content
except Exception as e:
logger.error(f"Error generating AI discharge content: {e}")
return None
def extract_section(ai_content, section_title):
"""Simple placeholder function to extract section from AI content.
Improve this with more robust parsing based on LLM output format."""
start_marker = f"**{section_title}:**"
end_marker = "\n\n" # Adjust based on typical LLM output structure
start_index = ai_content.find(start_marker)
if start_index != -1:
start_index += len(start_marker)
end_index = ai_content.find(end_marker, start_index)
if end_index != -1:
return ai_content[start_index:end_index].strip()
return "Not found in AI output."
def generate_pdf_from_meldrx_with_ai_content(patient_data, llm_content):
"""Generate a PDF using patient data from MeldRx and AI-generated content."""
if isinstance(patient_data, str):
try:
patient_data = json.loads(patient_data)
except:
return None, "Invalid patient data format"
if not patient_data:
return None, "No patient data available"
try:
if isinstance(patient_data, list) and len(patient_data):
patient = patient_data[0]
else:
patient = patient_data
patient_info = {
"name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
"dob": patient.get("birthDate", "Unknown"),
"patient_id": patient.get("id", "Unknown"),
"admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
"physician": "Dr. AI Provider", # Mock data - Indicate AI generated
}
output_dir = tempfile.mkdtemp()
pdf_path = generate_discharge_summary(
patient_info, llm_content, output_dir
) # Using AI content now
return pdf_path, "PDF generated successfully with AI Content" # Indicate AI content
except Exception as e:
return None, f"Error generating PDF with AI content: {str(e)}"
def extract_auth_code_from_url(redirected_url):
"""Extracts the authorization code from the redirected URL."""
try:
parsed_url = urlparse(redirected_url)
query_params = parse_qs(parsed_url.query)
if "code" in query_params:
return query_params["code"][0], None # Return code and no error
else:
return None, "Authorization code not found in URL." # Return None and error message
except Exception as e:
return None, f"Error parsing URL: {e}" # Return None and error message
# Create a simplified interface to avoid complex component interactions
CALLBACK_MANAGER = CallbackManager(
redirect_uri="https://multitransformer-discharge-guard.hf.space/callback",
client_secret=None,
)
# Define the cyberpunk theme - using a dark base and neon accents
cyberpunk_theme = gr.themes.Monochrome(
primary_hue="cyan",
secondary_hue="pink",
neutral_hue="slate",
font=["Source Code Pro", "monospace"], # Retro monospace font
font_mono=["Source Code Pro", "monospace"],
)
# Create the UI with the cyberpunk theme
with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
gr.Markdown(
"<h1 style='color:#00FFFF; text-shadow: 0 0 5px #00FFFF;'>Discharge Guard <span style='color:#FF00FF; text-shadow: 0 0 5px #FF00FF;'>Cyber</span></h1>"
) # Cyberpunk Title
with gr.Tab("Authenticate with MeldRx", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>SMART on FHIR Authentication</h2>"
) # Neon Tab Header
auth_url_output = gr.Textbox(
label="Authorization URL",
value=CALLBACK_MANAGER.get_auth_url(),
interactive=False,
)
gr.Markdown(
"<p style='color:#A9A9A9;'>Copy the URL above, open it in a browser, log in, and paste the <span style='color:#00FFFF;'>entire redirected URL</span> from your browser's address bar below.</p>"
) # Subdued instructions with neon highlight
redirected_url_input = gr.Textbox(label="Redirected URL") # New textbox for redirected URL
extract_code_button = gr.Button(
"Extract Authorization Code", elem_classes="cyberpunk-button"
) # Cyberpunk button style
extracted_code_output = gr.Textbox(
label="Extracted Authorization Code", interactive=False
) # Textbox to show extracted code
auth_code_input = gr.Textbox(
label="Authorization Code (from above, or paste manually if extraction fails)",
interactive=True,
) # Updated label to be clearer
auth_submit = gr.Button(
"Submit Code for Authentication", elem_classes="cyberpunk-button"
) # Cyberpunk button style
auth_result = gr.HTML(label="Authentication Result") # Use HTML for styled result
patient_data_button = gr.Button(
"Fetch Patient Data", elem_classes="cyberpunk-button"
) # Cyberpunk button style
patient_data_output = gr.Textbox(label="Patient Data", lines=10)
# Add button to generate PDF from MeldRx data (No AI)
meldrx_pdf_button = gr.Button(
"Generate PDF from MeldRx Data (No AI)", elem_classes="cyberpunk-button"
) # Renamed button
meldrx_pdf_status = gr.Textbox(
label="PDF Generation Status (No AI)"
) # Renamed status
meldrx_pdf_download = gr.File(
label="Download Generated PDF (No AI)"
) # Renamed download
def process_redirected_url(redirected_url):
"""Processes the redirected URL to extract and display the authorization code."""
auth_code, error_message = extract_auth_code_from_url(redirected_url)
if auth_code:
return auth_code, "<span style='color:#00FF7F;'>Authorization code extracted!</span>" # Neon Green Success
else:
return "", f"<span style='color:#FF4500;'>Could not extract authorization code.</span> {error_message or ''}" # Neon Orange Error
extract_code_button.click(
fn=process_redirected_url,
inputs=redirected_url_input,
outputs=[
extracted_code_output,
auth_result,
], # Reusing auth_result for extraction status
)
auth_submit.click(
fn=CALLBACK_MANAGER.set_auth_code,
inputs=extracted_code_output, # Using extracted code as input for authentication
outputs=auth_result,
)
with gr.Tab(
"Patient Dashboard", elem_classes="cyberpunk-tab"
): # Optional: Class for tab styling
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Data</h2>"
) # Neon Tab Header
dashboard_output = gr.HTML(
"<p style='color:#A9A9A9;'>Fetch patient data from the Authentication tab first.</p>"
) # Subdued placeholder text
refresh_btn = gr.Button(
"Refresh Data", elem_classes="cyberpunk-button"
) # Cyberpunk button style
# Simple function to update dashboard based on fetched data
def update_dashboard():
try:
data = CALLBACK_MANAGER.get_patient_data()
if (
data.startswith("<span style='color:#FF8C00;'>Not authenticated")
or data.startswith("<span style='color:#DC143C;'>Failed")
or data.startswith("<span style='color:#FF6347;'>Error")
):
return f"<p style='color:#FF8C00;'>{data}</p>" # Show auth errors in orange
try:
# Parse the data
patients_data = json.loads(data)
patients = []
# Extract patients from bundle
for entry in patients_data.get("entry", []):
resource = entry.get("resource", {})
if resource.get("resourceType") == "Patient":
patients.append(resource)
# Generate HTML card
html = "<h3 style='color:#00FFFF; text-shadow: 0 0 2px #00FFFF;'>Patients</h3>" # Neon Sub-header
for patient in patients:
# Extract name
name = patient.get("name", [{}])[0]
given = " ".join(name.get("given", ["Unknown"]))
family = name.get("family", "Unknown")
# Extract other details
gender = patient.get("gender", "unknown").capitalize()
birth_date = patient.get("birthDate", "Unknown")
# Generate HTML card with cyberpunk styling
html += f"""
<div style="border: 1px solid #00FFFF; padding: 10px; margin: 10px 0; border-radius: 5px; background-color: #222; box-shadow: 0 0 5px #00FFFF;">
<h4 style='color:#00FFFF;'>{given} {family}</h4>
<p style='color:#A9A9A9;'><strong>Gender:</strong> <span style='color:#00FFFF;'>{gender}</span></p>
<p style='color:#A9A9A9;'><strong>Birth Date:</strong> <span style='color:#00FFFF;'>{birth_date}</span></p>
<p style='color:#A9A9A9;'><strong>ID:</strong> <span style='color:#00FFFF;'>{patient.get("id", "Unknown")}</span></p>
</div>
"""
return html
except Exception as e:
return f"<p style='color:#FF6347;'>Error parsing patient data: {str(e)}</p>" # Tomato Error
except Exception as e:
return f"<p style='color:#FF6347;'>Error fetching patient data: {str(e)}</p>" # Tomato Error
refresh_btn.click(fn=update_dashboard, inputs=None, outputs=dashboard_output)
with gr.Tab("Discharge Form", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Details</h2>"
) # Neon Tab Header
with gr.Row():
first_name = gr.Textbox(label="First Name")
last_name = gr.Textbox(label="Last Name")
middle_initial = gr.Textbox(label="Middle Initial")
with gr.Row():
dob = gr.Textbox(label="Date of Birth")
age = gr.Textbox(label="Age")
sex = gr.Textbox(label="Sex")
address = gr.Textbox(label="Address")
with gr.Row():
city = gr.Textbox(label="City")
state = gr.Textbox(label="State")
zip_code = gr.Textbox(label="Zip Code")
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Primary Healthcare Professional Details</h2>"
) # Neon Sub-header
with gr.Row():
doctor_first_name = gr.Textbox(label="Doctor's First Name")
doctor_last_name = gr.Textbox(label="Doctor's Last Name")
doctor_middle_initial = gr.Textbox(label="Doctor's Middle Initial")
hospital_name = gr.Textbox(label="Hospital/Clinic Name")
doctor_address = gr.Textbox(label="Address")
with gr.Row():
doctor_city = gr.Textbox(label="City")
doctor_state = gr.Textbox(label="State")
doctor_zip = gr.Textbox(label="Zip Code")
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Admission and Discharge Details</h2>"
) # Neon Sub-header
with gr.Row():
admission_date = gr.Textbox(label="Date of Admission")
referral_source = gr.Textbox(label="Source of Referral")
admission_method = gr.Textbox(label="Method of Admission")
with gr.Row():
discharge_date = gr.Textbox(label="Date of Discharge")
discharge_reason = gr.Radio(
["Treated", "Transferred", "Discharge Against Advice", "Patient Died"],
label="Discharge Reason",
)
date_of_death = gr.Textbox(label="Date of Death (if applicable)")
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Diagnosis & Procedures</h2>"
) # Neon Sub-header
diagnosis = gr.Textbox(label="Diagnosis")
procedures = gr.Textbox(label="Operation & Procedures")
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Medication Details</h2>"
) # Neon Sub-header
medications = gr.Textbox(label="Medication on Discharge")
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Prepared By</h2>"
) # Neon Sub-header
with gr.Row():
preparer_name = gr.Textbox(label="Name")
preparer_job_title = gr.Textbox(label="Job Title")
# Add buttons for both display form and generate PDF
with gr.Row():
submit_display = gr.Button(
"Display Form", elem_classes="cyberpunk-button"
) # Cyberpunk button style
submit_pdf = gr.Button(
"Generate PDF (No AI)", elem_classes="cyberpunk-button"
) # Renamed button to clarify no AI and styled
# Output areas
form_output = gr.HTML() # Use HTML to render styled form
pdf_output = gr.File(
label="Download PDF (No AI)"
) # Renamed output to clarify no AI
# Connect the display form button
submit_display.click(
display_form,
inputs=[
first_name,
last_name,
middle_initial,
dob,
age,
sex,
address,
city,
state,
zip_code,
doctor_first_name,
doctor_last_name,
doctor_middle_initial,
hospital_name,
doctor_address,
doctor_city,
doctor_state,
doctor_zip,
admission_date,
referral_source,
admission_method,
discharge_date,
discharge_reason,
date_of_death,
diagnosis,
procedures,
medications,
preparer_name,
preparer_job_title,
],
outputs=form_output,
)
# Connect the generate PDF button (No AI version)
submit_pdf.click(
generate_pdf_from_form,
inputs=[
first_name,
last_name,
middle_initial,
dob,
age,
sex,
address,
city,
state,
zip_code,
doctor_first_name,
doctor_last_name,
doctor_middle_initial,
hospital_name,
doctor_address,
doctor_city,
doctor_state,
doctor_zip,
admission_date,
referral_source,
admission_method,
discharge_date,
discharge_reason,
date_of_death,
diagnosis,
procedures,
medications,
preparer_name,
preparer_job_title,
],
outputs=pdf_output,
)
with gr.Tab(
"Medical File Analysis", elem_classes="cyberpunk-tab"
): # Optional: Class for tab styling
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Analyze Medical Files with Discharge Guard AI</h2>"
) # Neon Tab Header
with gr.Column():
dicom_file = gr.File(
file_types=[".dcm"], label="Upload DICOM File (.dcm)"
)
dicom_ai_output = gr.Textbox(label="DICOM Analysis Report", lines=5)
analyze_dicom_button = gr.Button(
"Analyze DICOM with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
hl7_file = gr.File(file_types=[".hl7"], label="Upload HL7 File (.hl7)")
hl7_ai_output = gr.Textbox(label="HL7 Analysis Report", lines=5)
analyze_hl7_button = gr.Button(
"Analyze HL7 with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
xml_file = gr.File(file_types=[".xml"], label="Upload XML File (.xml)")
xml_ai_output = gr.Textbox(label="XML Analysis Report", lines=5)
analyze_xml_button = gr.Button(
"Analyze XML with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
ccda_file = gr.File(
file_types=[".xml", ".cda", ".ccd"],
label="Upload CCDA File (.xml, .cda, .ccd)",
)
ccda_ai_output = gr.Textbox(label="CCDA Analysis Report", lines=5)
analyze_ccda_button = gr.Button(
"Analyze CCDA with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
ccd_file = gr.File(
file_types=[".ccd"],
label="Upload CCD File (.ccd)",
) # Redundant, as CCDA also handles .ccd, but kept for clarity
ccd_ai_output = gr.Textbox(
label="CCD Analysis Report", lines=5
) # Redundant
analyze_ccd_button = gr.Button(
"Analyze CCD with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style # Redundant
pdf_file = gr.File(file_types=[".pdf"], label="Upload PDF File (.pdf)")
pdf_ai_output = gr.Textbox(label="PDF Analysis Report", lines=5)
analyze_pdf_button = gr.Button(
"Analyze PDF with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
csv_file = gr.File(file_types=[".csv"], label="Upload CSV File (.csv)")
csv_ai_output = gr.Textbox(label="CSV Analysis Report", lines=5)
analyze_csv_button = gr.Button(
"Analyze CSV with AI", elem_classes="cyberpunk-button"
) # Cyberpunk button style
# Connect AI Analysis Buttons - using REAL AI functions now
analyze_dicom_button.click(
analyze_dicom_file_with_ai, # Call REAL AI function
inputs=dicom_file,
outputs=dicom_ai_output,
)
analyze_hl7_button.click(
analyze_hl7_file_with_ai, # Call REAL AI function
inputs=hl7_file,
outputs=hl7_ai_output,
)
analyze_xml_button.click(
analyze_cda_xml_file_with_ai, # Call REAL AI function
inputs=xml_file,
outputs=xml_ai_output,
)
analyze_ccda_button.click(
analyze_cda_xml_file_with_ai, # Call REAL AI function
inputs=ccda_file,
outputs=ccda_ai_output,
)
analyze_ccd_button.click( # Redundant button, but kept for UI if needed
analyze_cda_xml_file_with_ai, # Call REAL AI function
inputs=ccd_file,
outputs=ccd_ai_output,
)
analyze_pdf_button.click(
analyze_pdf_file_with_ai, inputs=pdf_file, outputs=pdf_ai_output
)
analyze_csv_button.click(
analyze_csv_file_with_ai, inputs=csv_file, outputs=csv_ai_output
)
with gr.Tab(
"One-Click Discharge Paper (AI)", elem_classes="cyberpunk-tab"
): # New Tab for One-Click Discharge Paper with AI, styled
gr.Markdown(
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>One-Click Medical Discharge Paper Generation with AI Content</h2>"
) # Neon Tab Header
one_click_ai_pdf_button = gr.Button(
"Generate Discharge Paper with AI (One-Click)",
elem_classes="cyberpunk-button",
) # Updated button label and styled
one_click_ai_pdf_status = gr.Textbox(
label="Discharge Paper Generation Status (AI)"
) # Updated status label
one_click_ai_pdf_download = gr.File(
label="Download Discharge Paper (AI)"
) # Updated download label
one_click_ai_pdf_button.click(
generate_discharge_paper_one_click, # Use the one-click function that now calls AI
inputs=[],
outputs=[one_click_ai_pdf_download, one_click_ai_pdf_status],
)
# Connect the patient data buttons
patient_data_button.click(
fn=CALLBACK_MANAGER.get_patient_data, inputs=None, outputs=patient_data_output
)
# Connect refresh button to update dashboard
refresh_btn.click(fn=update_dashboard, inputs=None, outputs=dashboard_output)
# Corrected the button click function name here to `generate_pdf_from_meldrx` (No AI PDF)
meldrx_pdf_button.click(
fn=generate_pdf_from_meldrx,
inputs=patient_data_output,
outputs=[meldrx_pdf_download, meldrx_pdf_status],
)
# Connect patient data updates to dashboard
patient_data_button.click(
fn=update_dashboard, inputs=None, outputs=dashboard_output
)
# Launch with sharing enabled for public access
demo.launch(ssr_mode=False) |