Surajkumaar commited on
Commit
9838247
·
verified ·
1 Parent(s): c462a88

Upload 2 files

Browse files
Files changed (2) hide show
  1. main.py +196 -191
  2. requirements.txt +1 -2
main.py CHANGED
@@ -18,7 +18,7 @@ import subprocess
18
  import platform
19
  import docx
20
  from reportlab.lib.pagesizes import letter
21
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage, PageBreak
22
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
23
  from reportlab.lib import colors
24
  from reportlab.lib.units import inch
@@ -501,10 +501,7 @@ async def generate_report(analysis_id: str, include_consultation: bool = True):
501
  doc.add_paragraph(f"Primary Diagnosis: {analysis_data['highest_probability_class']}")
502
  doc.add_paragraph(f"Severity Index: {analysis_data['severity_index']}/100")
503
 
504
- # Add a page break before the detailed classification
505
- doc.add_page_break()
506
-
507
- # Detailed classification table
508
  doc.add_heading('Detailed Classification', level=2)
509
  table = doc.add_table(rows=1, cols=4)
510
  table.style = 'Table Grid'
@@ -560,10 +557,10 @@ async def generate_report(analysis_id: str, include_consultation: bool = True):
560
  docx_path = os.path.join(temp_dir, f"report_{analysis_id}.docx")
561
  doc.save(docx_path)
562
 
 
563
  try:
564
- # Now generate PDF directly using ReportLab
565
  from reportlab.lib.pagesizes import letter
566
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage, PageBreak
567
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
568
  from reportlab.lib import colors
569
  from reportlab.lib.units import inch
@@ -572,200 +569,208 @@ async def generate_report(analysis_id: str, include_consultation: bool = True):
572
 
573
  # Create PDF file
574
  pdf_path = os.path.join(temp_dir, f"report_{analysis_id}.pdf")
 
 
575
 
576
- try:
577
- # Create the PDF document with proper settings
578
- pdf_doc = SimpleDocTemplate(
579
- pdf_path,
580
- pagesize=letter,
581
- rightMargin=72,
582
- leftMargin=72,
583
- topMargin=72,
584
- bottomMargin=72,
585
- title=f"ClarirAI Medical Report - {analysis_id}",
586
- author="ClarirAI",
587
- subject="Diabetic Retinopathy Analysis",
588
- creator="ClarirAI Report Generator"
589
- )
590
-
591
- # Get the styles
592
- styles = getSampleStyleSheet()
593
-
594
- # Create custom styles with explicit fonts to avoid font issues
595
- title_style = ParagraphStyle(
596
- 'Title',
597
- parent=styles['Title'],
598
- fontName='Helvetica-Bold',
599
- fontSize=16,
600
- spaceAfter=12
601
- )
602
- heading1_style = ParagraphStyle(
603
- 'Heading1',
604
- parent=styles['Heading1'],
605
- fontName='Helvetica-Bold',
606
- fontSize=14,
607
- spaceAfter=10,
608
- spaceBefore=10
609
- )
610
- heading2_style = ParagraphStyle(
611
- 'Heading2',
612
- parent=styles['Heading2'],
613
- fontName='Helvetica-Bold',
614
- fontSize=12,
615
- spaceAfter=8,
616
- spaceBefore=8
617
- )
618
- normal_style = ParagraphStyle(
619
- 'Normal',
620
- parent=styles["Normal"],
621
- fontName='Helvetica',
622
- fontSize=10,
623
- leading=12
624
- )
625
-
626
- # Build PDF content
627
- elements = []
628
-
629
- # Title
630
- elements.append(Paragraph('ClarirAI Medical Report', title_style))
631
- elements.append(Spacer(1, 0.25*inch))
632
-
633
- # Metadata
634
- elements.append(Paragraph(f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", normal_style))
635
- elements.append(Paragraph(f"Analysis ID: {analysis_id}", normal_style))
636
- elements.append(Paragraph(f"Original Analysis Date: {analysis_data['timestamp']}", normal_style))
637
- elements.append(Spacer(1, 0.25*inch))
638
 
639
- # Add the analyzed image if available
640
- if image_path and os.path.exists(image_path):
641
- try:
642
- elements.append(Paragraph('Analyzed Retinal Image', heading1_style))
643
- elements.append(Paragraph('Below is the retinal image that was analyzed:', normal_style))
644
-
645
- # Process the image for ReportLab
646
- img = PILImage.open(image_path)
647
- img_width, img_height = img.size
648
- aspect = img_height / float(img_width)
649
- rl_img_width = 4 * inch
650
- rl_img_height = rl_img_width * aspect
651
-
652
- # Convert PIL Image to ReportLab Image
653
- img_buffer = BytesIO()
654
- img.save(img_buffer, format='JPEG')
655
- img_buffer.seek(0)
656
- rl_img = RLImage(img_buffer, width=rl_img_width, height=rl_img_height)
657
- elements.append(rl_img)
658
- elements.append(Spacer(1, 0.25*inch))
659
- except Exception as img_err:
660
- logger.error(f"Error adding image to PDF: {img_err}")
661
- elements.append(Paragraph('Error loading retinal image', normal_style))
662
- elements.append(Spacer(1, 0.25*inch))
663
 
664
- # Diagnosis section
665
- elements.append(Paragraph('Diabetic Retinopathy Assessment', heading1_style))
666
- elements.append(Paragraph(f"Primary Diagnosis: {analysis_data['highest_probability_class']}", normal_style))
667
- elements.append(Paragraph(f"Severity Index: {analysis_data['severity_index']}/100", normal_style))
 
 
668
  elements.append(Spacer(1, 0.25*inch))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
 
670
- # Add a page break before the detailed classification
671
- elements.append(PageBreak())
672
-
673
- # Detailed classification table
674
- elements.append(Paragraph('Detailed Classification', heading2_style))
675
-
676
- # Create table data
677
- table_data = [
678
- ['Classification', 'Description', 'Probability', 'Confidence Level']
679
- ]
680
-
681
- for item in analysis_data['detailed_classification']:
682
- table_data.append([
683
- item['class'],
684
- item['description'],
685
- f"{item['percentage']}%",
686
- item['confidence_level']
687
- ])
688
 
689
- # Create table
690
- table = Table(table_data, colWidths=[1.5*inch, 2.5*inch, 1*inch, 1.5*inch])
691
- table.setStyle(TableStyle([
692
- ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
693
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),
694
- ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
695
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
696
- ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
697
- ('GRID', (0, 0), (-1, -1), 1, colors.black),
698
- ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
699
- ]))
700
- elements.append(table)
701
- elements.append(Spacer(1, 0.25*inch))
702
 
703
- # Add simple text-based classification summary instead of chart
704
- elements.append(Paragraph('Classification Probability Summary', heading2_style))
 
705
 
706
- # Create a simple text summary instead of a chart
707
- for item in analysis_data['detailed_classification']:
708
- elements.append(Paragraph(
709
- f"{item['class']}: {item['percentage']}% ({item['confidence_level']})",
710
- normal_style
711
- ))
712
  elements.append(Spacer(1, 0.25*inch))
713
-
714
- # Clinical information
715
- if clinical_info:
716
- elements.append(Paragraph('Clinical Information', heading1_style))
717
-
718
- elements.append(Paragraph('Findings', heading2_style))
719
- elements.append(Paragraph(clinical_info.get('findings', 'Not available'), normal_style))
720
- elements.append(Spacer(1, 0.15*inch))
721
-
722
- elements.append(Paragraph('Associated Risks', heading2_style))
723
- elements.append(Paragraph(clinical_info.get('risks', 'Not available'), normal_style))
724
- elements.append(Spacer(1, 0.15*inch))
725
-
726
- elements.append(Paragraph('Recommendations', heading2_style))
727
- elements.append(Paragraph(clinical_info.get('recommendations', 'Not available'), normal_style))
728
- elements.append(Spacer(1, 0.15*inch))
729
-
730
- elements.append(Paragraph('Follow-up', heading2_style))
731
- elements.append(Paragraph(clinical_info.get('follow_up', 'Not available'), normal_style))
732
- elements.append(Spacer(1, 0.25*inch))
733
-
734
- # AI explanation
735
- elements.append(Paragraph('AI Analysis Explanation', heading1_style))
736
- elements.append(Paragraph(clinical_info.get('explanation', 'No explanation available for this classification.'), normal_style))
737
  elements.append(Spacer(1, 0.25*inch))
738
-
739
- # AI consultation
740
- if consultation_text:
741
- elements.append(Paragraph('AI-Generated Medical Consultation', heading1_style))
742
- elements.append(Paragraph(consultation_text, normal_style))
743
- elements.append(Spacer(1, 0.25*inch))
744
-
745
- # Disclaimer
746
- elements.append(Paragraph('Disclaimer', heading1_style))
747
- elements.append(Paragraph('This report is generated using artificial intelligence and should not replace professional medical advice. The analysis and recommendations provided are based on automated image processing and AI consultation. Please consult with a qualified healthcare provider for proper diagnosis, treatment, and follow-up care.', normal_style))
748
-
749
- # Build the PDF
750
- pdf_doc.build(elements)
751
-
752
- # Return the PDF file
753
- return FileResponse(
754
- path=pdf_path,
755
- filename=f"ClarirAI_Report_{analysis_id}.pdf",
756
- media_type="application/pdf",
757
- headers={"Content-Disposition": f"attachment; filename=ClarirAI_Report_{analysis_id}.pdf"}
758
- )
759
- except Exception as e:
760
- logger.error(f"Error generating PDF with ReportLab: {e}")
761
- # Fall back to DOCX if PDF generation fails
762
- return FileResponse(
763
- path=docx_path,
764
- filename=f"ClarirAI_Report_{analysis_id}.docx",
765
- media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
766
- headers={"Content-Disposition": f"attachment; filename=ClarirAI_Report_{analysis_id}.docx"}
767
- )
768
-
769
  except Exception as e:
770
  logger.error(f"Error generating report: {e}")
771
  raise HTTPException(status_code=500, detail=f"Error generating report: {str(e)}")
 
18
  import platform
19
  import docx
20
  from reportlab.lib.pagesizes import letter
21
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage
22
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
23
  from reportlab.lib import colors
24
  from reportlab.lib.units import inch
 
501
  doc.add_paragraph(f"Primary Diagnosis: {analysis_data['highest_probability_class']}")
502
  doc.add_paragraph(f"Severity Index: {analysis_data['severity_index']}/100")
503
 
504
+ # Add detailed classification table
 
 
 
505
  doc.add_heading('Detailed Classification', level=2)
506
  table = doc.add_table(rows=1, cols=4)
507
  table.style = 'Table Grid'
 
557
  docx_path = os.path.join(temp_dir, f"report_{analysis_id}.docx")
558
  doc.save(docx_path)
559
 
560
+ # Now generate PDF directly using ReportLab
561
  try:
 
562
  from reportlab.lib.pagesizes import letter
563
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage
564
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
565
  from reportlab.lib import colors
566
  from reportlab.lib.units import inch
 
569
 
570
  # Create PDF file
571
  pdf_path = os.path.join(temp_dir, f"report_{analysis_id}.pdf")
572
+ pdf_doc = SimpleDocTemplate(pdf_path, pagesize=letter)
573
+ styles = getSampleStyleSheet()
574
 
575
+ # Create custom styles
576
+ title_style = ParagraphStyle(
577
+ 'Title',
578
+ parent=styles['Title'],
579
+ fontSize=16,
580
+ spaceAfter=12
581
+ )
582
+ heading1_style = ParagraphStyle(
583
+ 'Heading1',
584
+ parent=styles['Heading1'],
585
+ fontSize=14,
586
+ spaceAfter=10,
587
+ spaceBefore=10
588
+ )
589
+ heading2_style = ParagraphStyle(
590
+ 'Heading2',
591
+ parent=styles['Heading2'],
592
+ fontSize=12,
593
+ spaceAfter=8,
594
+ spaceBefore=8
595
+ )
596
+ normal_style = styles["Normal"]
597
+
598
+ # Build PDF content
599
+ elements = []
600
+
601
+ # Title
602
+ elements.append(Paragraph('ClarirAI Medical Report', title_style))
603
+ elements.append(Spacer(1, 0.25*inch))
604
+
605
+ # Metadata
606
+ elements.append(Paragraph(f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", normal_style))
607
+ elements.append(Paragraph(f"Analysis ID: {analysis_id}", normal_style))
608
+ elements.append(Paragraph(f"Original Analysis Date: {analysis_data['timestamp']}", normal_style))
609
+ elements.append(Spacer(1, 0.25*inch))
610
+
611
+ # Add the analyzed image if available
612
+ if image_path and os.path.exists(image_path):
613
+ elements.append(Paragraph('Analyzed Retinal Image', heading1_style))
614
+ elements.append(Paragraph('Below is the retinal image that was analyzed:', normal_style))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
 
616
+ # Process the image for ReportLab
617
+ img = PILImage.open(image_path)
618
+ img_width, img_height = img.size
619
+ aspect = img_height / float(img_width)
620
+ rl_img_width = 4 * inch
621
+ rl_img_height = rl_img_width * aspect
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622
 
623
+ # Convert PIL Image to ReportLab Image
624
+ img_buffer = BytesIO()
625
+ img.save(img_buffer, format='JPEG')
626
+ img_buffer.seek(0)
627
+ rl_img = RLImage(img_buffer, width=rl_img_width, height=rl_img_height)
628
+ elements.append(rl_img)
629
  elements.append(Spacer(1, 0.25*inch))
630
+
631
+ # Diagnosis section
632
+ elements.append(Paragraph('Diabetic Retinopathy Assessment', heading1_style))
633
+ elements.append(Paragraph(f"Primary Diagnosis: {analysis_data['highest_probability_class']}", normal_style))
634
+ elements.append(Paragraph(f"Severity Index: {analysis_data['severity_index']}/100", normal_style))
635
+ elements.append(Spacer(1, 0.25*inch))
636
+
637
+ # Detailed classification table
638
+ elements.append(Paragraph('Detailed Classification', heading2_style))
639
+
640
+ # Create table data
641
+ table_data = [
642
+ ['Classification', 'Description', 'Probability', 'Confidence Level']
643
+ ]
644
+
645
+ for item in analysis_data['detailed_classification']:
646
+ table_data.append([
647
+ item['class'],
648
+ item['description'],
649
+ f"{item['percentage']}%",
650
+ item['confidence_level']
651
+ ])
652
+
653
+ # Create table
654
+ table = Table(table_data, colWidths=[1.5*inch, 2.5*inch, 1*inch, 1.5*inch])
655
+ table.setStyle(TableStyle([
656
+ ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
657
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),
658
+ ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
659
+ ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
660
+ ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
661
+ ('GRID', (0, 0), (-1, -1), 1, colors.black),
662
+ ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
663
+ ]))
664
+ elements.append(table)
665
+ elements.append(Spacer(1, 0.25*inch))
666
+
667
+ # Add bar chart visualization
668
+ elements.append(Paragraph('Classification Probability Chart', heading2_style))
669
+
670
+ # Create the drawing with a proper size
671
+ drawing = Drawing(500, 250)
672
+
673
+ # Create the bar chart
674
+ chart = VerticalBarChart()
675
+ chart.x = 50
676
+ chart.y = 50
677
+ chart.height = 150
678
+ chart.width = 350
679
+
680
+ # Extract data for the chart
681
+ data = []
682
+ categories = []
683
+ for item in analysis_data['detailed_classification']:
684
+ data.append(item['percentage'])
685
+ categories.append(item['class'])
686
+
687
+ # Set chart data
688
+ chart.data = [data]
689
+ chart.categoryAxis.categoryNames = categories
690
+ chart.categoryAxis.labels.boxAnchor = 'ne'
691
+ chart.categoryAxis.labels.dx = -8
692
+ chart.categoryAxis.labels.dy = -2
693
+ chart.categoryAxis.labels.angle = 30
694
+
695
+ # Set value axis properties
696
+ chart.valueAxis.valueMin = 0
697
+ chart.valueAxis.valueMax = 100
698
+ chart.valueAxis.valueStep = 10
699
+
700
+ # Set bar properties
701
+ chart.bars[0].fillColor = colors.skyblue
702
+ chart.bars[0].strokeColor = colors.black
703
+ chart.bars[0].strokeWidth = 0.5
704
+
705
+ # Add a legend
706
+ legend = Legend()
707
+ legend.alignment = 'right'
708
+ legend.x = 400
709
+ legend.y = 150
710
+ legend.colorNamePairs = [(colors.skyblue, 'Probability (%)')]
711
+
712
+ # Add chart and legend to the drawing
713
+ drawing.add(chart)
714
+ drawing.add(legend)
715
+
716
+ # Add the drawing to the PDF
717
+ elements.append(drawing)
718
+ elements.append(Spacer(1, 0.25*inch))
719
+
720
+ # Clinical information
721
+ if clinical_info:
722
+ elements.append(Paragraph('Clinical Information', heading1_style))
723
 
724
+ elements.append(Paragraph('Findings', heading2_style))
725
+ elements.append(Paragraph(clinical_info.get('findings', 'Not available'), normal_style))
726
+ elements.append(Spacer(1, 0.15*inch))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
 
728
+ elements.append(Paragraph('Associated Risks', heading2_style))
729
+ elements.append(Paragraph(clinical_info.get('risks', 'Not available'), normal_style))
730
+ elements.append(Spacer(1, 0.15*inch))
 
 
 
 
 
 
 
 
 
 
731
 
732
+ elements.append(Paragraph('Recommendations', heading2_style))
733
+ elements.append(Paragraph(clinical_info.get('recommendations', 'Not available'), normal_style))
734
+ elements.append(Spacer(1, 0.15*inch))
735
 
736
+ elements.append(Paragraph('Follow-up', heading2_style))
737
+ elements.append(Paragraph(clinical_info.get('follow_up', 'Not available'), normal_style))
 
 
 
 
738
  elements.append(Spacer(1, 0.25*inch))
739
+
740
+ # AI explanation
741
+ elements.append(Paragraph('AI Analysis Explanation', heading1_style))
742
+ elements.append(Paragraph(clinical_info.get('explanation', 'No explanation available for this classification.'), normal_style))
743
+ elements.append(Spacer(1, 0.25*inch))
744
+
745
+ # AI consultation
746
+ if consultation_text:
747
+ elements.append(Paragraph('AI-Generated Medical Consultation', heading1_style))
748
+ elements.append(Paragraph(consultation_text, normal_style))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  elements.append(Spacer(1, 0.25*inch))
750
+
751
+ # Disclaimer
752
+ elements.append(Paragraph('Disclaimer', heading1_style))
753
+ elements.append(Paragraph('This report is generated using artificial intelligence and should not replace professional medical advice. The analysis and recommendations provided are based on automated image processing and AI consultation. Please consult with a qualified healthcare provider for proper diagnosis, treatment, and follow-up care.', normal_style))
754
+
755
+ # Build the PDF
756
+ pdf_doc.build(elements)
757
+
758
+ # Return the PDF file
759
+ return FileResponse(
760
+ path=pdf_path,
761
+ filename=f"ClarirAI_Report_{analysis_id}.pdf",
762
+ media_type="application/pdf"
763
+ )
764
+
765
+ except Exception as e:
766
+ logger.error(f"Error generating PDF with ReportLab: {e}")
767
+ # Fall back to DOCX if PDF generation fails
768
+ return FileResponse(
769
+ path=docx_path,
770
+ filename=f"ClarirAI_Report_{analysis_id}.docx",
771
+ media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
772
+ )
773
+
 
 
 
 
 
 
 
774
  except Exception as e:
775
  logger.error(f"Error generating report: {e}")
776
  raise HTTPException(status_code=500, detail=f"Error generating report: {str(e)}")
requirements.txt CHANGED
@@ -8,5 +8,4 @@ openai
8
  python-docx
9
  docx
10
  python-dotenv
11
- reportlab>=3.6.12
12
- pypdf2
 
8
  python-docx
9
  docx
10
  python-dotenv
11
+ reportlab