malavikapradeep2001 commited on
Commit
ac76f6e
·
1 Parent(s): 095c768
Files changed (1) hide show
  1. backend/app.py +69 -23
backend/app.py CHANGED
@@ -569,7 +569,7 @@ async def predict(model_name: str = Form(...), file: UploadFile = File(...)):
569
 
570
  # =====================================================
571
 
572
- def create_designed_pdf(pdf_path, report_data, analysis_summary_json):
573
  doc = SimpleDocTemplate(pdf_path, pagesize=letter,
574
  rightMargin=72, leftMargin=72,
575
  topMargin=72, bottomMargin=18)
@@ -665,6 +665,19 @@ def create_designed_pdf(pdf_path, report_data, analysis_summary_json):
665
  story.append(Paragraph(ai_summary.get('ai_interpretation', 'Not available.'), styles['NormalSmall']))
666
  story.append(Spacer(1, 0.2*inch))
667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  # Doctor's Notes
669
  story.append(Paragraph("Doctor's Notes", styles['Section']))
670
  story.append(Paragraph(report_data.get('doctor_notes') or 'No additional notes provided.', styles['NormalSmall']))
@@ -677,8 +690,8 @@ def create_designed_pdf(pdf_path, report_data, analysis_summary_json):
677
 
678
  # Signatures
679
  story.append(Paragraph("Signatures", styles['Section']))
680
- story.append(Paragraph("Dr. Emily Roberts, MD (Cytopathologist)", styles['NormalSmall']))
681
- story.append(Paragraph("Dr. James Wilson, MD (Pathologist)", styles['NormalSmall']))
682
  story.append(Spacer(1, 0.1*inch))
683
  story.append(Paragraph(f"Generated on: {datetime.datetime.now().strftime('%b %d, %Y, %I:%M %p')}", styles['NormalSmall']))
684
 
@@ -694,6 +707,7 @@ async def generate_report(
694
  notes: str = Form(None),
695
  analysis_id: str = Form(None),
696
  analysis_summary: str = Form(None),
 
697
  ):
698
  """Generate a structured medical report from analysis results and metadata."""
699
  try:
@@ -735,23 +749,62 @@ async def generate_report(
735
  with open(report_json, "w", encoding="utf-8") as f:
736
  json.dump(report_data, f, indent=2, ensure_ascii=False)
737
 
738
- # Attempt to create a PDF version if reportlab is available
 
739
  pdf_url = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
  if REPORTLAB_AVAILABLE:
741
  try:
742
  pdf_path = os.path.join(report_dir, "report.pdf")
743
- create_designed_pdf(pdf_path, report_data, analysis_summary)
744
  pdf_url = f"/outputs/reports/{report_id}/report.pdf"
745
  except Exception as e:
746
  print(f"Error creating designed PDF: {e}")
747
  pdf_url = None
748
 
749
- # Parse analysis_summary to get AI results
750
- try:
751
- ai_summary = json.loads(analysis_summary) if analysis_summary else {}
752
- except (json.JSONDecodeError, TypeError):
753
- ai_summary = {}
754
-
755
  # Determine report type based on analysis summary or model used
756
  model_used = ai_summary.get('model_used', '')
757
  if 'YOLO' in model_used or 'yolo' in str(analysis_id).lower():
@@ -809,18 +862,11 @@ async def generate_report(
809
  json_url = f"/outputs/reports/{report_id}/report.json"
810
  html_url = f"/outputs/reports/{report_id}/report.html"
811
 
812
- # Prefer annotated image URL from the analysis summary (ai_summary) if present,
813
- # otherwise fall back to the analysis annotated_image_url (which may be the analysis_id).
814
- annotated_img = ai_summary.get('annotated_image_url') or report_data.get("analysis", {}).get("annotated_image_url") or ""
815
-
816
- # Normalize annotated image path: allow absolute http(s) URLs or ensure relative paths start with '/'.
817
- annotated_img_full = ""
818
- if annotated_img:
819
- if isinstance(annotated_img, str) and annotated_img.startswith(('http://', 'https://')):
820
- annotated_img_full = annotated_img
821
- else:
822
- # ensure leading slash for relative paths so StaticFiles routing works
823
- annotated_img_full = annotated_img if str(annotated_img).startswith('/') else '/' + str(annotated_img)
824
 
825
  download_pdf_btn = f'<a href="{pdf_url}" download style="text-decoration:none"><button class="btn-secondary">Download PDF</button></a>' if pdf_url else ''
826
 
 
569
 
570
  # =====================================================
571
 
572
+ def create_designed_pdf(pdf_path, report_data, analysis_summary_json, annotated_image_path=None):
573
  doc = SimpleDocTemplate(pdf_path, pagesize=letter,
574
  rightMargin=72, leftMargin=72,
575
  topMargin=72, bottomMargin=18)
 
665
  story.append(Paragraph(ai_summary.get('ai_interpretation', 'Not available.'), styles['NormalSmall']))
666
  story.append(Spacer(1, 0.2*inch))
667
 
668
+ # If an annotated image path was provided and exists on disk, embed it
669
+ if annotated_image_path:
670
+ try:
671
+ if os.path.isfile(annotated_image_path):
672
+ story.append(Spacer(1, 0.1*inch))
673
+ # Use a reasonable width for the embedded image
674
+ img = ReportLabImage(annotated_image_path, width=5*inch, kind='proportional')
675
+ story.append(img)
676
+ story.append(Spacer(1, 0.2*inch))
677
+ except Exception as e:
678
+ # Don't fail the whole PDF creation if image embedding fails
679
+ print(f"⚠️ Could not embed annotated image in PDF: {e}")
680
+
681
  # Doctor's Notes
682
  story.append(Paragraph("Doctor's Notes", styles['Section']))
683
  story.append(Paragraph(report_data.get('doctor_notes') or 'No additional notes provided.', styles['NormalSmall']))
 
690
 
691
  # Signatures
692
  story.append(Paragraph("Signatures", styles['Section']))
693
+ story.append(Paragraph("Rajesh Venugopal, Physician", styles['NormalSmall']))
694
+ #story.append(Paragraph("", styles['NormalSmall']))
695
  story.append(Spacer(1, 0.1*inch))
696
  story.append(Paragraph(f"Generated on: {datetime.datetime.now().strftime('%b %d, %Y, %I:%M %p')}", styles['NormalSmall']))
697
 
 
707
  notes: str = Form(None),
708
  analysis_id: str = Form(None),
709
  analysis_summary: str = Form(None),
710
+ file: UploadFile = File(None),
711
  ):
712
  """Generate a structured medical report from analysis results and metadata."""
713
  try:
 
749
  with open(report_json, "w", encoding="utf-8") as f:
750
  json.dump(report_data, f, indent=2, ensure_ascii=False)
751
 
752
+ # We'll create PDF later (after parsing analysis_summary and resolving
753
+ # any annotated/input image). Initialize pdf_url here.
754
  pdf_url = None
755
+
756
+ # Parse analysis_summary to get AI results
757
+ try:
758
+ ai_summary = json.loads(analysis_summary) if analysis_summary else {}
759
+ except (json.JSONDecodeError, TypeError):
760
+ ai_summary = {}
761
+
762
+ # Resolve annotated image: prefer AI/analysis annotated image; if none,
763
+ # save the uploaded input image (if provided) into the report folder
764
+ # and use that as the embedded image.
765
+ annotated_img = ai_summary.get('annotated_image_url') or report_data.get("analysis", {}).get("annotated_image_url") or ""
766
+ annotated_img_full = ""
767
+ annotated_img_local = None
768
+
769
+ if annotated_img:
770
+ # If it's an outputs path served by StaticFiles, map to local file
771
+ if isinstance(annotated_img, str) and annotated_img.startswith('/outputs/'):
772
+ rel = annotated_img[len('/outputs/'):].lstrip('/')
773
+ annotated_img_local = os.path.join(OUTPUT_DIR, rel)
774
+ annotated_img_full = annotated_img
775
+ else:
776
+ # keep absolute URLs as-is for HTML
777
+ annotated_img_full = annotated_img if isinstance(annotated_img, str) else ''
778
+
779
+ # If no annotated image provided, but an input file was uploaded, save it
780
+ if not annotated_img_full and file is not None and getattr(file, 'filename', None):
781
+ try:
782
+ input_filename = f"input_image{os.path.splitext(file.filename)[1] or '.jpg'}"
783
+ input_path = os.path.join(report_dir, input_filename)
784
+ contents = await file.read()
785
+ with open(input_path, 'wb') as out_f:
786
+ out_f.write(contents)
787
+ annotated_img_full = f"/outputs/reports/{report_id}/{input_filename}"
788
+ annotated_img_local = input_path
789
+ except Exception as e:
790
+ print(f"⚠️ Failed to save uploaded input image for report: {e}")
791
+
792
+ # Ensure annotated_img_full has a leading slash if it's a relative path
793
+ if annotated_img_full and not annotated_img_full.startswith(('http://', 'https://')):
794
+ annotated_img_full = annotated_img_full if annotated_img_full.startswith('/') else '/' + annotated_img_full
795
+
796
+ # Now attempt to create the PDF (passing the local annotated image path
797
+ # so the PDF writer can embed it). If annotation is remote or not
798
+ # available, PDF creation will still proceed without the image.
799
  if REPORTLAB_AVAILABLE:
800
  try:
801
  pdf_path = os.path.join(report_dir, "report.pdf")
802
+ create_designed_pdf(pdf_path, report_data, analysis_summary, annotated_img_local)
803
  pdf_url = f"/outputs/reports/{report_id}/report.pdf"
804
  except Exception as e:
805
  print(f"Error creating designed PDF: {e}")
806
  pdf_url = None
807
 
 
 
 
 
 
 
808
  # Determine report type based on analysis summary or model used
809
  model_used = ai_summary.get('model_used', '')
810
  if 'YOLO' in model_used or 'yolo' in str(analysis_id).lower():
 
862
  json_url = f"/outputs/reports/{report_id}/report.json"
863
  html_url = f"/outputs/reports/{report_id}/report.html"
864
 
865
+ # annotated_img_full was computed earlier; ensure it's defined and set
866
+ # annotated_img (used by the HTML template conditional) accordingly.
867
+ if 'annotated_img_full' not in locals() or not annotated_img_full:
868
+ annotated_img_full = ''
869
+ annotated_img = annotated_img_full
 
 
 
 
 
 
 
870
 
871
  download_pdf_btn = f'<a href="{pdf_url}" download style="text-decoration:none"><button class="btn-secondary">Download PDF</button></a>' if pdf_url else ''
872