sujataprakashdatycs commited on
Commit
8f9f1bd
·
verified ·
1 Parent(s): 4d57ded

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +406 -0
app.py CHANGED
@@ -402,6 +402,410 @@ def process_pipeline(
402
 
403
 
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  # ---------- Gradio Theme ----------
406
  simple_theme = gr.themes.Soft(
407
  primary_hue=gr.themes.colors.blue,
@@ -470,3 +874,5 @@ if __name__ == "__main__":
470
  interface.queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
471
 
472
 
 
 
 
402
 
403
 
404
 
405
+ # # app.py
406
+ import os
407
+ import gradio as gr
408
+ import json
409
+ import time
410
+ from dotenv import load_dotenv
411
+ from ClinicalStatusAgent import ClinicalStatusAgent
412
+ from TestFindingAgent import TestFindingAgent
413
+ from ComorbidityCheckerAgent import ComorbidityCheckerAgent
414
+ from HCCDiagnosisListEngine import HCCDiagnosisListEngine
415
+ from chartdiagnosischecker import ChartDiagnosisChecker
416
+ from MeatValidatorAgent import MEATValidatorAgent
417
+ from typing import Optional
418
+
419
+ load_dotenv()
420
+ APP_TITLE = "Risk Adjustment (HCC Chart Validation)"
421
+ CSV_PATH = "hcc_mapping.csv"
422
+ SAMPLE_PDF = "sample_patient_chart.pdf" # Place a sample PDF in the same folder
423
+
424
+
425
+ def json_to_markdown(data) -> str:
426
+ """
427
+ Convert JSON (dict or list) into a clean, styled Markdown/HTML string.
428
+ Handles both:
429
+ - Full dict with patient_id, hcc_code, model_version, final_analysis
430
+ - Just a list of diagnoses
431
+ Priorities:
432
+ - Explicit = yes takes priority over implicit
433
+ - MEAT, Tests, Comorbidities are expandable
434
+ """
435
+ try:
436
+ # Case 1: full dict
437
+ if isinstance(data, dict) and "final_analysis" in data:
438
+ patient_id = data.get("patient_id", "Unknown Patient")
439
+ hcc_code = data.get("hcc_code", "N/A")
440
+ model_version = data.get("model_version", "N/A")
441
+ analyses = data.get("final_analysis", [])
442
+ # Case 2: list of diagnoses
443
+ elif isinstance(data, list):
444
+ patient_id = "N/A"
445
+ hcc_code = "N/A"
446
+ model_version = "N/A"
447
+ analyses = data
448
+ else:
449
+ return "<div style='color:red; font-weight:bold;'>⚠️ Invalid data format for report.</div>"
450
+
451
+ # Header
452
+ md = f"""
453
+ <div style="border:2px solid #4CAF50; padding:15px; border-radius:10px; background:#f9fdf9;">
454
+ <h2 style="color:#2e7d32;">📋 HCC Chart Validation Report </h2>
455
+ <p><b>🧾 Patient ID:</b> {patient_id}</p>
456
+ <p><b>🏷️ HCC Code:</b> {hcc_code}</p>
457
+ <p><b>⚙️ Model Version:</b> {model_version}</p>
458
+ </div>
459
+ <br/>
460
+ """
461
+
462
+ # Each diagnosis
463
+ for idx, diag in enumerate(analyses, 1):
464
+ md += f"""
465
+ <div style="border:1px solid #ccc; padding:12px; border-radius:8px; margin-bottom:12px;">
466
+ <h3 style="color:#1565c0;">{idx}. {diag.get("diagnosis", "Unknown Diagnosis")}</h3>
467
+ <p><b>ICD-10:</b> {diag.get("icd10", "N/A")}</p>
468
+ <p><b>Reference:</b> <a href="{diag.get("reference","")}" target="_blank">{diag.get("reference","")}</a></p>
469
+ """
470
+
471
+ # --- Explicit / Implicit Priority ---
472
+ explicit_ans = diag.get("answer_explicit", "N/A")
473
+ explicit_rat = diag.get("rationale_explicit", "")
474
+ implicit_ans = diag.get("answer_implicit", "N/A")
475
+ implicit_rat = diag.get("rationale_implicit", "")
476
+
477
+ if explicit_ans.lower() == "yes":
478
+ md += f"<p><b>Explicit:</b> {explicit_ans} — {explicit_rat}</p>"
479
+ else:
480
+ md += f"<p><b>Implicit:</b> {implicit_ans} — {implicit_rat}</p>"
481
+
482
+ md += f"""
483
+ <p><b>Clinical Status:</b> {diag.get("clinical_status","N/A")}</p>
484
+ <p><b>Status Rationale:</b> {diag.get("status_rationale","")}</p>
485
+ """
486
+
487
+ # --- Tests ---
488
+ if "tests" in diag:
489
+ md += "<details><summary><b>🧪 Tests & Procedures</b></summary><ul>"
490
+ tests = diag["tests"]
491
+
492
+ if "vitals" in tests:
493
+ md += "<li><b>Vitals:</b><ul>"
494
+ for k, v in tests["vitals"].items():
495
+ md += f"<li>{k}: {v}</li>"
496
+ md += "</ul></li>"
497
+
498
+ if "procedures" in tests:
499
+ md += "<li><b>Procedures:</b><ul>"
500
+ for k, v in tests["procedures"].items():
501
+ md += f"<li>{k}: {v}</li>"
502
+ md += "</ul></li>"
503
+
504
+ if "lab_test" in tests:
505
+ md += "<li><b>Lab Tests:</b><ul>"
506
+ for k, v in tests["lab_test"].items():
507
+ md += f"<li>{k}: {v}</li>"
508
+ md += "</ul></li>"
509
+
510
+ md += "</ul></details>"
511
+
512
+ # --- MEAT ---
513
+ if "meat" in diag:
514
+ md += "<details><summary><b>🍖 MEAT Validation</b></summary><ul>"
515
+ for k, v in diag["meat"].items():
516
+ emoji = "✅" if v else "❌"
517
+ md += f"<li>{k.capitalize()}: {emoji}</li>"
518
+ md += "</ul>"
519
+ md += f"<p><b>MEAT Rationale:</b> {diag.get('meat_rationale','')}</p>"
520
+ md += "</details>"
521
+
522
+ # --- Comorbidities ---
523
+ if "comorbidities" in diag and diag["comorbidities"]:
524
+ md += "<details><summary><b>🩺 Comorbidities</b></summary><ul>"
525
+ for c in diag["comorbidities"]:
526
+ emoji = "✅" if c.get("is_present") else "❌"
527
+ md += f"<li>{emoji} <b>{c.get('condition')}</b><br/><i>{c.get('rationale')}</i></li>"
528
+ md += "</ul></details>"
529
+
530
+ md += "</div>"
531
+
532
+ return md
533
+
534
+ except Exception as e:
535
+ return f"<div style='color:red; font-weight:bold;'>⚠️ Error rendering report: {e}</div>"
536
+
537
+ # def process_pipeline(pdf_file, hcc_code, model_version, csv_path=CSV_PATH, output_folder="outputs"):
538
+ # try:
539
+ # start = time.time()
540
+
541
+ # if pdf_file is None:
542
+ # yield "<div style='color:orange; font-weight:bold;'>⚠️ Please upload a patient chart PDF.</div>"
543
+ # return
544
+
545
+ # hcc_code_str = str(hcc_code or "").strip()
546
+ # if not hcc_code_str:
547
+ # yield "<div style='color:orange; font-weight:bold;'>⚠️ Please enter a valid HCC Code before running validation.</div>"
548
+ # return
549
+
550
+ # os.makedirs(output_folder, exist_ok=True)
551
+ # pdf_path = pdf_file.name
552
+ # patient_name = os.path.splitext(os.path.basename(pdf_path))[0]
553
+ # print(f"\n[PROCESSING] {patient_name}")
554
+
555
+ # # Step 1: Get all possible HCC Diagnoses
556
+ # yield "🔍 Step 1: Extracting possible HCC Diagnoses..."
557
+ # diagnoses = HCCDiagnosisListEngine(hcc_code_str, model_version, csv_path).run()
558
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
559
+
560
+ # if not diagnoses:
561
+ # msg = f"<b>❌ No diagnoses found for HCC {hcc_code_str}.</b>"
562
+ # yield msg
563
+ # return
564
+
565
+ # # Step 2: Check patient chart
566
+ # yield "📄 Step 2: Checking diagnoses in patient chart..."
567
+ # all_checked_results = ChartDiagnosisChecker(pdf_path).run(diagnoses)
568
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
569
+
570
+ # confirmed_diagnoses = [
571
+ # d for d in all_checked_results
572
+ # if d.get("answer_explicit", "").lower() == "yes"
573
+ # or d.get("answer_implicit", "").lower() == "yes"
574
+ # ]
575
+
576
+ # if not confirmed_diagnoses:
577
+ # msg = f"<b>❌ No confirmed diagnoses for HCC {hcc_code_str} in {patient_name}. Report saved.</b>"
578
+ # output_data = {
579
+ # "patient_id": patient_name,
580
+ # "hcc_code": hcc_code_str,
581
+ # "model_version": model_version,
582
+ # "final_analysis": all_checked_results
583
+ # }
584
+ # outpath = os.path.join(output_folder, f"{patient_name}.json")
585
+ # with open(outpath, "w", encoding="utf-8") as f:
586
+ # json.dump(output_data, f, indent=2, ensure_ascii=False)
587
+ # print(f"[DONE] Saved basic chart check report to {outpath}")
588
+ # yield msg
589
+ # return
590
+
591
+ # # Step 3: Tests
592
+ # yield "🧪 Step 3: Finding relevant tests..."
593
+ # diagnoses_with_tests = TestFindingAgent(hcc_code=hcc_code_str, model_version=model_version).run(confirmed_diagnoses)
594
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
595
+
596
+ # # Step 4: Clinical Status
597
+ # yield "⚕️ Step 4: Determining clinical status..."
598
+ # diagnoses_with_status = ClinicalStatusAgent().run(diagnoses_with_tests)
599
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
600
+
601
+ # active_diagnoses = [d for d in diagnoses_with_status if d.get("clinical_status") == "ACTIVE"]
602
+
603
+ # validated_meat_diagnoses = []
604
+ # if active_diagnoses:
605
+ # yield "🍖 Step 5: Validating MEAT..."
606
+ # validated_meat_diagnoses = MEATValidatorAgent().run(active_diagnoses)
607
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
608
+ # else:
609
+ # yield "ℹ️ No ACTIVE diagnoses found. Skipping MEAT/Comorbidity."
610
+
611
+ # diagnoses_passed_meat = [d for d in validated_meat_diagnoses if any(d.get("meat", {}).values())]
612
+
613
+ # comorbidity_results = []
614
+ # if diagnoses_passed_meat:
615
+ # yield "🤝 Step 6: Checking comorbidities..."
616
+ # comorbidity_results = ComorbidityCheckerAgent(pdf_path, hcc_code_str, model_version).run(diagnoses_passed_meat)
617
+ # yield f"⏳ Elapsed: {time.time() - start:.1f} sec"
618
+
619
+ # # Merge results
620
+ # status_map = {d["diagnosis"]: d for d in diagnoses_with_status}
621
+ # meat_map = {d["diagnosis"]: d for d in validated_meat_diagnoses}
622
+ # comorbidity_map = {d["diagnosis"]: d for d in comorbidity_results}
623
+
624
+ # final_analysis = []
625
+ # for entry in all_checked_results:
626
+ # diag_name = entry["diagnosis"]
627
+ # if diag_name in status_map:
628
+ # updated_entry = status_map[diag_name]
629
+ # if diag_name in meat_map:
630
+ # updated_entry.update(meat_map[diag_name])
631
+ # if diag_name in comorbidity_map:
632
+ # updated_entry.update(comorbidity_map[diag_name])
633
+ # final_analysis.append(updated_entry)
634
+ # else:
635
+ # final_analysis.append(entry)
636
+
637
+ # filtered_final_analysis = [
638
+ # e for e in final_analysis
639
+ # if e.get("answer_explicit", "").lower() == "yes"
640
+ # or e.get("answer_implicit", "").lower() == "yes"
641
+ # ]
642
+
643
+ # output_data = {
644
+ # "patient_id": patient_name,
645
+ # "hcc_code": hcc_code_str,
646
+ # "model_version": model_version,
647
+ # "final_analysis": filtered_final_analysis
648
+ # }
649
+
650
+ # # Final Report
651
+ # yield "✅ Step 7: Generating final report..."
652
+ # elapsed = time.time() - start
653
+ # final_report = json_to_markdown(output_data) + f"<br/><div style='color:green;'>✅ Completed in {elapsed:.1f} sec</div>"
654
+ # yield final_report
655
+
656
+ # except Exception as e:
657
+ # print(f"[ERROR] {e}")
658
+ # yield f"<div style='color:red; font-weight:bold;'>⚠️ Error: {e}</div>"
659
+
660
+
661
+ import time
662
+ import gradio as gr
663
+
664
+ def process_pipeline(
665
+ pdf_file,
666
+ hcc_code,
667
+ model_version,
668
+ csv_path=CSV_PATH,
669
+ output_folder="outputs",
670
+ progress=gr.Progress()
671
+ ):
672
+ try:
673
+ start = time.time()
674
+
675
+ if pdf_file is None:
676
+ yield "<div style='color:orange; font-weight:bold;'>⚠️ Please upload a patient chart PDF.</div>"
677
+ return
678
+
679
+ hcc_code_str = str(hcc_code or "").strip()
680
+ if not hcc_code_str:
681
+ yield "<div style='color:orange; font-weight:bold;'>⚠️ Please enter a valid HCC Code before running validation.</div>"
682
+ return
683
+
684
+ os.makedirs(output_folder, exist_ok=True)
685
+ pdf_path = pdf_file.name
686
+ patient_name = os.path.splitext(os.path.basename(pdf_path))[0]
687
+ print(f"\n[PROCESSING] {patient_name}")
688
+
689
+ # total steps for progress bar
690
+ total_steps = 7
691
+ step = 0
692
+
693
+ def log(msg):
694
+ """Helper to include elapsed time like tqdm"""
695
+ elapsed = time.time() - start
696
+ return f"{msg}<br/>⏳ Elapsed: {elapsed:.1f} sec"
697
+
698
+ # ----------------------
699
+ # Step 1: Diagnoses
700
+ step += 1
701
+ progress((step, total_steps), desc="Extracting diagnoses")
702
+ yield log(f"🔍 Step {step}/{total_steps}: Extracting possible HCC Diagnoses...")
703
+ diagnoses = HCCDiagnosisListEngine(hcc_code_str, model_version, csv_path).run()
704
+
705
+ if not diagnoses:
706
+ yield f"<b>❌ No diagnoses found for HCC {hcc_code_str}.</b>"
707
+ return
708
+
709
+ # ----------------------
710
+ # Step 2: Chart checking
711
+ step += 1
712
+ progress((step, total_steps), desc="Checking chart")
713
+ yield log(f"📄 Step {step}/{total_steps}: Checking diagnoses in patient chart...")
714
+ all_checked_results = ChartDiagnosisChecker(pdf_path).run(diagnoses)
715
+
716
+ confirmed_diagnoses = [
717
+ d for d in all_checked_results
718
+ if d.get("answer_explicit", "").lower() == "yes"
719
+ or d.get("answer_implicit", "").lower() == "yes"
720
+ ]
721
+ if not confirmed_diagnoses:
722
+ msg = f"<b>❌ No confirmed diagnoses for HCC {hcc_code_str} in {patient_name}. Report saved.</b>"
723
+ yield log(msg)
724
+ return
725
+
726
+ # ----------------------
727
+ # Step 3: Tests
728
+ step += 1
729
+ progress((step, total_steps), desc="Finding tests")
730
+ yield log(f"🧪 Step {step}/{total_steps}: Finding relevant tests...")
731
+ diagnoses_with_tests = TestFindingAgent(hcc_code=hcc_code_str, model_version=model_version).run(confirmed_diagnoses)
732
+
733
+ # ----------------------
734
+ # Step 4: Clinical Status
735
+ step += 1
736
+ progress((step, total_steps), desc="Determining clinical status")
737
+ yield log(f"⚕️ Step {step}/{total_steps}: Determining clinical status...")
738
+ diagnoses_with_status = ClinicalStatusAgent().run(diagnoses_with_tests)
739
+
740
+ active_diagnoses = [d for d in diagnoses_with_status if d.get("clinical_status") == "ACTIVE"]
741
+
742
+ validated_meat_diagnoses = []
743
+ if active_diagnoses:
744
+ # ----------------------
745
+ # Step 5: MEAT
746
+ step += 1
747
+ progress((step, total_steps), desc="Validating MEAT")
748
+ yield log(f"🍖 Step {step}/{total_steps}: Validating MEAT...")
749
+ validated_meat_diagnoses = MEATValidatorAgent().run(active_diagnoses)
750
+ else:
751
+ yield log("ℹ️ No ACTIVE diagnoses found. Skipping MEAT/Comorbidity.")
752
+
753
+ # ----------------------
754
+ # Step 6: Comorbidities
755
+ diagnoses_passed_meat = [d for d in validated_meat_diagnoses if any(d.get("meat", {}).values())]
756
+ comorbidity_results = []
757
+ if diagnoses_passed_meat:
758
+ step += 1
759
+ progress((step, total_steps), desc="Checking comorbidities")
760
+ yield log(f"🤝 Step {step}/{total_steps}: Checking comorbidities...")
761
+ comorbidity_results = ComorbidityCheckerAgent(pdf_path, hcc_code_str, model_version).run(diagnoses_passed_meat)
762
+
763
+ # ----------------------
764
+ # Step 7: Final Report
765
+ step += 1
766
+ progress((step, total_steps), desc="Generating report")
767
+ yield log(f"✅ Step {step}/{total_steps}: Generating final report...")
768
+
769
+ # merge & filter results
770
+ status_map = {d["diagnosis"]: d for d in diagnoses_with_status}
771
+ meat_map = {d["diagnosis"]: d for d in validated_meat_diagnoses}
772
+ comorbidity_map = {d["diagnosis"]: d for d in comorbidity_results}
773
+
774
+ final_analysis = []
775
+ for entry in all_checked_results:
776
+ diag_name = entry["diagnosis"]
777
+ updated_entry = entry.copy()
778
+ if diag_name in status_map:
779
+ updated_entry.update(status_map[diag_name])
780
+ if diag_name in meat_map:
781
+ updated_entry.update(meat_map[diag_name])
782
+ if diag_name in comorbidity_map:
783
+ updated_entry.update(comorbidity_map[diag_name])
784
+ final_analysis.append(updated_entry)
785
+
786
+ filtered_final_analysis = [
787
+ e for e in final_analysis
788
+ if e.get("answer_explicit", "").lower() == "yes"
789
+ or e.get("answer_implicit", "").lower() == "yes"
790
+ ]
791
+
792
+ output_data = {
793
+ "patient_id": patient_name,
794
+ "hcc_code": hcc_code_str,
795
+ "model_version": model_version,
796
+ "final_analysis": filtered_final_analysis
797
+ }
798
+
799
+ elapsed = time.time() - start
800
+ final_report = json_to_markdown(output_data) + f"<br/><div style='color:green;'>✅ Completed in {elapsed:.1f} sec</div>"
801
+ yield final_report
802
+
803
+ except Exception as e:
804
+ print(f"[ERROR] {e}")
805
+ yield f"<div style='color:red; font-weight:bold;'>⚠️ Error: {e}</div>"
806
+
807
+
808
+
809
  # ---------- Gradio Theme ----------
810
  simple_theme = gr.themes.Soft(
811
  primary_hue=gr.themes.colors.blue,
 
874
  interface.queue().launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
875
 
876
 
877
+
878
+