Kushalmanda commited on
Commit
63477bf
·
verified ·
1 Parent(s): 1acce17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -148
app.py CHANGED
@@ -12,11 +12,9 @@ from io import BytesIO
12
  import uuid
13
  import logging
14
  import textwrap
15
- from reportlab.lib.pagesizes import letter
16
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
17
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
18
- from reportlab.lib.enums import TA_CENTER
19
- from reportlab.lib import colors
20
 
21
  # Set up logging
22
  logging.basicConfig(level=logging.INFO)
@@ -341,6 +339,9 @@ mark {
341
  margin-top: 20px !important;
342
  background: linear-gradient(135deg, #4CAF50, #2E7D32) !important;
343
  }
 
 
 
344
  /* Hide elements */
345
  footer, .gradio-footer, .hide, [data-testid="Use via API"], [data-testid="mmsettings"],
346
  .sentiment-analysis, .risk-visualization {
@@ -501,13 +502,7 @@ def generate_heatmap(risk_level: str):
501
 
502
  ax.set_axis_off()
503
  plt.tight_layout()
504
-
505
- # Save the figure to a BytesIO object
506
- buf = BytesIO()
507
- plt.savefig(buf, format='png', bbox_inches='tight')
508
- plt.close(fig)
509
- buf.seek(0)
510
- return buf
511
  except Exception as e:
512
  logger.error(f"Heatmap generation failed: {str(e)}")
513
  raise Exception(f"Heatmap generation failed: {str(e)}")
@@ -585,80 +580,112 @@ def format_clause_example(example: str, index: int) -> str:
585
  </div>
586
  """
587
 
588
- def generate_pdf_report(analysis_results: dict, heatmap_buffer: BytesIO) -> BytesIO:
589
- """Generate a PDF report from the analysis results"""
590
- buffer = BytesIO()
591
- doc = SimpleDocTemplate(buffer, pagesize=letter)
592
-
593
- styles = getSampleStyleSheet()
594
- styles.add(ParagraphStyle(name='Center', alignment=TA_CENTER))
595
- styles.add(ParagraphStyle(name='RiskHigh', textColor=colors.red, fontSize=14))
596
- styles.add(ParagraphStyle(name='RiskMedium', textColor=colors.orange, fontSize=14))
597
- styles.add(ParagraphStyle(name='RiskLow', textColor=colors.green, fontSize=14))
598
- styles.add(ParagraphStyle(name='NormalBold', fontName='Helvetica-Bold', parent=styles['Normal']))
599
-
600
- story = []
601
-
602
- # Title
603
- story.append(Paragraph("Contract Risk Analysis Report", styles['Title']))
604
- story.append(Spacer(1, 12))
605
-
606
- # Risk Summary
607
- story.append(Paragraph("<b>Risk Summary</b>", styles['Heading2']))
608
- risk_style = 'RiskHigh' if analysis_results['risk_level'] == 'High' else 'RiskMedium' if analysis_results['risk_level'] == 'Medium' else 'RiskLow'
609
- story.append(Paragraph(f"Risk Score: {analysis_results['risk_score']:.1f}/100 - <b>{analysis_results['risk_level']} Risk</b>", styles[risk_style]))
610
- story.append(Spacer(1, 12))
611
-
612
- # Add heatmap to PDF
613
- if heatmap_buffer:
614
- story.append(Image(heatmap_buffer, width=400, height=100))
615
- story.append(Spacer(1, 24))
616
-
617
- # Key Findings
618
- story.append(Paragraph("<b>Key Findings</b>", styles['Heading2']))
619
- story.append(Paragraph(f"<b>Penalty Clauses:</b> {analysis_results['penalty_count']}", styles['Normal']))
620
- story.append(Paragraph(f"<b>Obligation Clauses:</b> {analysis_results['obligation_count']}", styles['Normal']))
621
- story.append(Paragraph(f"<b>Delay Clauses:</b> {analysis_results['delay_count']}", styles['Normal']))
622
- story.append(Spacer(1, 12))
623
-
624
- if analysis_results['penalty_values']:
625
- story.append(Paragraph("<b>Penalty Amounts Found</b>", styles['Heading3']))
626
- for amount in analysis_results['penalty_values'][:5]:
627
- story.append(Paragraph(f"${amount:,.2f}", styles['Normal']))
628
- story.append(Spacer(1, 12))
629
-
630
- # Detailed Findings
631
- story.append(Paragraph("<b>Detailed Findings</b>", styles['Heading2']))
632
-
633
- # Penalty Details
634
- if analysis_results['penalty_details']:
635
- story.append(Paragraph("<b>Penalty Clauses</b>", styles['Heading3']))
636
- story.append(Paragraph(analysis_results['penalty_details'], styles['Normal']))
637
- story.append(Spacer(1, 12))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
 
639
- # Obligation Details
640
- if analysis_results['obligation_details']:
641
- story.append(Paragraph("<b>Obligation Clauses</b>", styles['Heading3']))
642
- story.append(Paragraph(analysis_results['obligation_details'], styles['Normal']))
643
- story.append(Spacer(1, 12))
644
 
645
- # Delay Details
646
- if analysis_results['delay_details']:
647
- story.append(Paragraph("<b>Delay Clauses</b>", styles['Heading3']))
648
- story.append(Paragraph(analysis_results['delay_details'], styles['Normal']))
649
- story.append(Spacer(1, 12))
650
 
651
- # Sentiment Analysis
652
- story.append(Paragraph("<b>Sentiment Analysis</b>", styles['Heading2']))
653
- sentiment_text = "Positive" if analysis_results['sentiment_score'] > 0.6 else "Negative" if analysis_results['sentiment_score'] < 0.4 else "Neutral"
654
- story.append(Paragraph(f"Sentiment Score: {analysis_results['sentiment_score']:.2f} ({sentiment_text})", styles['Normal']))
655
 
656
- doc.build(story)
657
- buffer.seek(0)
658
- return buffer
 
659
 
660
- def analyze_pdf(file_obj) -> List:
661
- """Main analysis function for Gradio interface"""
662
  try:
663
  if not file_obj:
664
  raise Exception("No PDF file uploaded. Please upload a valid PDF file.")
@@ -757,7 +784,7 @@ def analyze_pdf(file_obj) -> List:
757
  raise Exception(f"Risk score calculation failed: {str(e)}")
758
 
759
  try:
760
- heatmap_buffer = generate_heatmap(risk_level)
761
  risk_meter = generate_risk_meter(risk_score)
762
  sentiment_meter = generate_sentiment_meter(sentiment_score)
763
  except Exception as e:
@@ -779,7 +806,7 @@ def analyze_pdf(file_obj) -> List:
779
  """)
780
  return "".join(details)
781
 
782
- penalty_details = f"""
783
  {penalty_warning}
784
  <div class='penalty-box'>
785
  <div class='section-title'>💰 Penalty Clause Analysis</div>
@@ -787,7 +814,7 @@ def analyze_pdf(file_obj) -> List:
787
  </div>
788
  """
789
 
790
- obligation_details = f"""
791
  {obligation_warning}
792
  <div class='obligation-box'>
793
  <div class='section-title'>📝 Obligation Clause Analysis</div>
@@ -795,7 +822,7 @@ def analyze_pdf(file_obj) -> List:
795
  </div>
796
  """
797
 
798
- delay_details = f"""
799
  {delay_warning}
800
  <div class='delay-box'>
801
  <div class='section-title'>⏱️ Delay Clause Analysis</div>
@@ -803,14 +830,14 @@ def analyze_pdf(file_obj) -> List:
803
  </div>
804
  """
805
 
806
- penalty_amounts = "\n".join([f"<div class='count-item'><span class='count-label'>💰 Amount</span><span class='count-value'>${amt:,.2f}</span></div>" for amt in penalty_values[:5]]) if penalty_values else "<div class='success-box'>✅ No specific penalty amounts found - This is good news!</div>"
807
 
808
  penalty_sentences = []
809
  for sentence in re.split(r'(?<=[.!?])\s+', text):
810
  if any(kw.lower() in sentence.lower() for kw in penalty_keywords.keys()):
811
  penalty_sentences.append(sentence.strip())
812
 
813
- penalty_examples = "\n".join([format_clause_example(sent, i+1) for i, sent in enumerate(penalty_sentences[:3])]) if penalty_sentences else "<div class='success-box'>✅ No penalty clauses found - Excellent contract terms!</div>"
814
 
815
  record_id = str(uuid.uuid4())
816
  sf_data = {
@@ -818,7 +845,7 @@ def analyze_pdf(file_obj) -> List:
818
  'risk_score': risk_score,
819
  'risk_level': risk_level,
820
  'record_id': record_id,
821
- 'penalty_examples': penalty_examples,
822
  'penalty_details': "\n".join([f"{kw}: {count}" for kw, count in penalty_counts.items()]),
823
  'penalty_amounts': "\n".join([f"${amt:,.2f}" for amt in penalty_values[:5]]) if penalty_values else "No specific penalty amounts found",
824
  'obligation_details': "\n".join([f"{kw}: {count}" for kw, count in obligation_counts.items()]),
@@ -863,21 +890,6 @@ def analyze_pdf(file_obj) -> List:
863
  "High": "This contract is high risk! Immediate legal review required."
864
  }
865
 
866
- risk_summary_html = f"""
867
- <div class='result-box'>
868
- {sf_link_html}
869
- <div class='section-title'>{risk_icon} Contract Risk Summary</div>
870
- <div class='risk-row'>
871
- <span class='risk-label'>Overall Risk Score</span>
872
- <span class='risk-score risk-{risk_level.lower()}'>{risk_score:.1f}/100</span>
873
- </div>
874
- {risk_meter}
875
- <div style='margin-top: 15px; font-size: 16px;'>
876
- <strong>Assessment:</strong> {risk_advice[risk_level]}
877
- </div>
878
- </div>
879
- """
880
-
881
  # Generate keyword matches section with highlighted context
882
  def format_keyword_matches(contexts_dict, title):
883
  sections = []
@@ -910,36 +922,44 @@ def analyze_pdf(file_obj) -> List:
910
  </div>
911
  """
912
 
913
- # Prepare data for PDF report
914
- analysis_results = {
915
- 'risk_score': risk_score,
916
- 'risk_level': risk_level,
917
- 'penalty_count': total_penalties,
918
- 'obligation_count': total_obligations,
919
- 'delay_count': total_delays,
920
- 'penalty_values': penalty_values,
921
- 'penalty_details': "Penalty clauses found in the document",
922
- 'obligation_details': "Obligation clauses found in the document",
923
- 'delay_details': "Delay clauses found in the document",
924
- 'sentiment_score': sentiment_score
925
- }
 
926
 
927
- # Generate PDF report
928
- pdf_buffer = generate_pdf_report(analysis_results, heatmap_buffer)
 
 
 
 
 
 
 
 
929
 
930
- # Return all outputs including the PDF data
931
  return [
932
  risk_summary_html,
933
- "", # risk_visualization
934
- penalty_details,
935
- f"<div class='penalty-box'><div class='section-title'>💰 Penalty Amounts Found</div>{penalty_amounts}</div>",
936
- obligation_details,
937
- delay_details,
938
- f"<div class='result-box'><div class='section-title'>📜 Text Extracted from PDF</div>{penalty_examples}</div>",
939
- "", # sentiment_analysis
940
- keyword_matches_html,
941
- pdf_buffer # PDF data as the last output
942
- ]
943
  except Exception as e:
944
  logger.error(f"Analysis failed: {str(e)}")
945
  error_message = f"""
@@ -954,7 +974,8 @@ def analyze_pdf(file_obj) -> List:
954
  </div>
955
  </div>
956
  """
957
- return [error_message] * 10 # Update to match total outputs including PDF
 
958
 
959
  # Create Gradio interface with blue theme and hidden elements
960
  with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Default(primary_hue="blue")) as demo:
@@ -983,7 +1004,6 @@ with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Defa
983
 
984
  with gr.Column(scale=3):
985
  risk_summary = gr.HTML(label="Contract Risk Summary")
986
- # Hidden risk visualization (kept in code but not displayed)
987
  risk_visualization = gr.HTML(label="Risk Visualization", visible=False)
988
 
989
  with gr.Row():
@@ -1000,15 +1020,13 @@ with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Defa
1000
  with gr.Row():
1001
  penalty_examples = gr.HTML(label="Text Extracted from PDF")
1002
 
1003
- # Hidden sentiment analysis (kept in code but not displayed)
1004
  sentiment_analysis = gr.HTML(label="Contract Sentiment Analysis", visible=False)
1005
-
1006
- # Keyword matches output
1007
  keyword_matches = gr.HTML(label="Keyword Matches in Document")
1008
 
1009
- # PDF download button
1010
- pdf_download = gr.File(label="Download PDF Report", visible=False)
1011
-
 
1012
  submit_btn.click(
1013
  fn=analyze_pdf,
1014
  inputs=file_input,
@@ -1017,18 +1035,12 @@ with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Defa
1017
  penalty_count, penalty_amounts,
1018
  obligation_count, delay_count,
1019
  penalty_examples, sentiment_analysis,
1020
- keyword_matches, pdf_download
1021
  ]
1022
- )
1023
-
1024
- # Show download button when PDF is ready
1025
- def toggle_download_visibility(pdf_data):
1026
- return gr.File.update(value=pdf_data, visible=pdf_data is not None)
1027
-
1028
- keyword_matches.change(
1029
- fn=toggle_download_visibility,
1030
- inputs=pdf_download,
1031
- outputs=pdf_download
1032
  )
1033
 
1034
  if __name__ == "__main__":
 
12
  import uuid
13
  import logging
14
  import textwrap
15
+ from fpdf import FPDF
16
+ from datetime import datetime
17
+ from bs4 import BeautifulSoup
 
 
18
 
19
  # Set up logging
20
  logging.basicConfig(level=logging.INFO)
 
339
  margin-top: 20px !important;
340
  background: linear-gradient(135deg, #4CAF50, #2E7D32) !important;
341
  }
342
+ .download-btn:hover {
343
+ background: linear-gradient(135deg, #2E7D32, #4CAF50) !important;
344
+ }
345
  /* Hide elements */
346
  footer, .gradio-footer, .hide, [data-testid="Use via API"], [data-testid="mmsettings"],
347
  .sentiment-analysis, .risk-visualization {
 
502
 
503
  ax.set_axis_off()
504
  plt.tight_layout()
505
+ return fig
 
 
 
 
 
 
506
  except Exception as e:
507
  logger.error(f"Heatmap generation failed: {str(e)}")
508
  raise Exception(f"Heatmap generation failed: {str(e)}")
 
580
  </div>
581
  """
582
 
583
+ def generate_full_pdf_report(
584
+ risk_summary: str,
585
+ penalty_details: str,
586
+ penalty_amounts: str,
587
+ obligation_details: str,
588
+ delay_details: str,
589
+ penalty_examples: str,
590
+ keyword_matches: str,
591
+ file_name: str
592
+ ) -> BytesIO:
593
+ """Generate a comprehensive PDF report of all analysis results"""
594
+ try:
595
+ # Remove HTML tags for PDF content
596
+ def clean_html(html):
597
+ soup = BeautifulSoup(html, 'html.parser')
598
+ return soup.get_text(separator='\n')
599
+
600
+ # Create PDF object
601
+ pdf = FPDF()
602
+ pdf.add_page()
603
+ pdf.set_auto_page_break(auto=True, margin=15)
604
+
605
+ # Set font and colors
606
+ pdf.set_font("Arial", size=12)
607
+
608
+ # Add title
609
+ pdf.set_fill_color(30, 144, 255) # DodgerBlue
610
+ pdf.set_text_color(255, 255, 255)
611
+ pdf.cell(0, 10, f"Contract Risk Analysis Report - {file_name}", ln=1, fill=True, align='C')
612
+ pdf.ln(10)
613
+
614
+ # Add report date
615
+ pdf.set_text_color(0, 0, 0)
616
+ pdf.set_font("Arial", 'I', 10)
617
+ pdf.cell(0, 10, f"Report generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", ln=1)
618
+ pdf.ln(5)
619
+
620
+ # Add sections
621
+ sections = [
622
+ ("Risk Summary", risk_summary),
623
+ ("Penalty Analysis", penalty_details),
624
+ ("Penalty Amounts", penalty_amounts),
625
+ ("Obligation Analysis", obligation_details),
626
+ ("Delay Analysis", delay_details),
627
+ ("Sample Clauses", penalty_examples),
628
+ ("Keyword Matches", keyword_matches)
629
+ ]
630
+
631
+ for title, content in sections:
632
+ # Add section header
633
+ pdf.set_font("Arial", 'B', 14)
634
+ pdf.set_text_color(30, 144, 255) # DodgerBlue
635
+ pdf.cell(0, 10, title, ln=1)
636
+ pdf.ln(2)
637
+
638
+ # Add section content
639
+ pdf.set_font("Arial", size=10)
640
+ pdf.set_text_color(0, 0, 0)
641
+
642
+ # Clean HTML and add multi_cell for proper wrapping
643
+ cleaned_content = clean_html(content)
644
+ pdf.multi_cell(0, 8, cleaned_content)
645
+ pdf.ln(5)
646
+
647
+ # Add page break if needed
648
+ if pdf.get_y() > 250:
649
+ pdf.add_page()
650
+
651
+ # Save to bytes buffer
652
+ pdf_bytes = BytesIO()
653
+ pdf.output(pdf_bytes)
654
+ pdf_bytes.seek(0)
655
+ return pdf_bytes
656
+
657
+ except Exception as e:
658
+ logger.error(f"PDF generation failed: {str(e)}")
659
+ raise Exception(f"Failed to generate PDF report: {str(e)}")
660
+
661
+ def generate_error_pdf(error_msg: str) -> BytesIO:
662
+ """Generate a PDF for error cases"""
663
+ pdf = FPDF()
664
+ pdf.add_page()
665
+ pdf.set_font("Arial", size=12)
666
 
667
+ # Add title
668
+ pdf.set_fill_color(255, 0, 0) # Red
669
+ pdf.set_text_color(255, 255, 255)
670
+ pdf.cell(0, 10, "Contract Analysis Error Report", ln=1, fill=True, align='C')
671
+ pdf.ln(20)
672
 
673
+ # Add error message
674
+ pdf.set_text_color(0, 0, 0)
675
+ pdf.set_font("Arial", 'B', 14)
676
+ pdf.cell(0, 10, "Analysis Failed", ln=1)
677
+ pdf.ln(5)
678
 
679
+ pdf.set_font("Arial", size=10)
680
+ pdf.multi_cell(0, 8, error_msg)
 
 
681
 
682
+ pdf_bytes = BytesIO()
683
+ pdf.output(pdf_bytes)
684
+ pdf_bytes.seek(0)
685
+ return pdf_bytes
686
 
687
+ def analyze_pdf(file_obj) -> Tuple[List, BytesIO]:
688
+ """Main analysis function that now also returns PDF bytes"""
689
  try:
690
  if not file_obj:
691
  raise Exception("No PDF file uploaded. Please upload a valid PDF file.")
 
784
  raise Exception(f"Risk score calculation failed: {str(e)}")
785
 
786
  try:
787
+ heatmap = generate_heatmap(risk_level)
788
  risk_meter = generate_risk_meter(risk_score)
789
  sentiment_meter = generate_sentiment_meter(sentiment_score)
790
  except Exception as e:
 
806
  """)
807
  return "".join(details)
808
 
809
+ penalty_details_html = f"""
810
  {penalty_warning}
811
  <div class='penalty-box'>
812
  <div class='section-title'>💰 Penalty Clause Analysis</div>
 
814
  </div>
815
  """
816
 
817
+ obligation_details_html = f"""
818
  {obligation_warning}
819
  <div class='obligation-box'>
820
  <div class='section-title'>📝 Obligation Clause Analysis</div>
 
822
  </div>
823
  """
824
 
825
+ delay_details_html = f"""
826
  {delay_warning}
827
  <div class='delay-box'>
828
  <div class='section-title'>⏱️ Delay Clause Analysis</div>
 
830
  </div>
831
  """
832
 
833
+ penalty_amounts_html = "\n".join([f"<div class='count-item'><span class='count-label'>💰 Amount</span><span class='count-value'>${amt:,.2f}</span></div>" for amt in penalty_values[:5]]) if penalty_values else "<div class='success-box'>✅ No specific penalty amounts found - This is good news!</div>"
834
 
835
  penalty_sentences = []
836
  for sentence in re.split(r'(?<=[.!?])\s+', text):
837
  if any(kw.lower() in sentence.lower() for kw in penalty_keywords.keys()):
838
  penalty_sentences.append(sentence.strip())
839
 
840
+ penalty_examples_html = "\n".join([format_clause_example(sent, i+1) for i, sent in enumerate(penalty_sentences[:3])]) if penalty_sentences else "<div class='success-box'>✅ No penalty clauses found - Excellent contract terms!</div>"
841
 
842
  record_id = str(uuid.uuid4())
843
  sf_data = {
 
845
  'risk_score': risk_score,
846
  'risk_level': risk_level,
847
  'record_id': record_id,
848
+ 'penalty_examples': penalty_examples_html,
849
  'penalty_details': "\n".join([f"{kw}: {count}" for kw, count in penalty_counts.items()]),
850
  'penalty_amounts': "\n".join([f"${amt:,.2f}" for amt in penalty_values[:5]]) if penalty_values else "No specific penalty amounts found",
851
  'obligation_details': "\n".join([f"{kw}: {count}" for kw, count in obligation_counts.items()]),
 
890
  "High": "This contract is high risk! Immediate legal review required."
891
  }
892
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
893
  # Generate keyword matches section with highlighted context
894
  def format_keyword_matches(contexts_dict, title):
895
  sections = []
 
922
  </div>
923
  """
924
 
925
+ risk_summary_html = f"""
926
+ <div class='result-box'>
927
+ {sf_link_html}
928
+ <div class='section-title'>{risk_icon} Contract Risk Summary</div>
929
+ <div class='risk-row'>
930
+ <span class='risk-label'>Overall Risk Score</span>
931
+ <span class='risk-score risk-{risk_level.lower()}'>{risk_score:.1f}/100</span>
932
+ </div>
933
+ {risk_meter}
934
+ <div style='margin-top: 15px; font-size: 16px;'>
935
+ <strong>Assessment:</strong> {risk_advice[risk_level]}
936
+ </div>
937
+ </div>
938
+ """
939
 
940
+ # Generate the PDF report
941
+ pdf_bytes = generate_full_pdf_report(
942
+ risk_summary=risk_summary_html,
943
+ penalty_details=penalty_details_html,
944
+ penalty_amounts=penalty_amounts_html,
945
+ obligation_details=obligation_details_html,
946
+ delay_details=delay_details_html,
947
+ penalty_examples=penalty_examples_html,
948
+ keyword_matches=keyword_matches_html,
949
+ file_name=os.path.basename(file_obj.name)
950
 
 
951
  return [
952
  risk_summary_html,
953
+ "", # risk_visualization (hidden)
954
+ penalty_details_html,
955
+ penalty_amounts_html,
956
+ obligation_details_html,
957
+ delay_details_html,
958
+ penalty_examples_html,
959
+ "", # sentiment_analysis (hidden)
960
+ keyword_matches_html
961
+ ], pdf_bytes
962
+
963
  except Exception as e:
964
  logger.error(f"Analysis failed: {str(e)}")
965
  error_message = f"""
 
974
  </div>
975
  </div>
976
  """
977
+ error_pdf = generate_error_pdf(str(e))
978
+ return [error_message] * 9, error_pdf
979
 
980
  # Create Gradio interface with blue theme and hidden elements
981
  with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Default(primary_hue="blue")) as demo:
 
1004
 
1005
  with gr.Column(scale=3):
1006
  risk_summary = gr.HTML(label="Contract Risk Summary")
 
1007
  risk_visualization = gr.HTML(label="Risk Visualization", visible=False)
1008
 
1009
  with gr.Row():
 
1020
  with gr.Row():
1021
  penalty_examples = gr.HTML(label="Text Extracted from PDF")
1022
 
 
1023
  sentiment_analysis = gr.HTML(label="Contract Sentiment Analysis", visible=False)
 
 
1024
  keyword_matches = gr.HTML(label="Keyword Matches in Document")
1025
 
1026
+ # Add download button
1027
+ download_btn = gr.Download(label="📥 Download Full Report as PDF")
1028
+
1029
+ # Define the click event
1030
  submit_btn.click(
1031
  fn=analyze_pdf,
1032
  inputs=file_input,
 
1035
  penalty_count, penalty_amounts,
1036
  obligation_count, delay_count,
1037
  penalty_examples, sentiment_analysis,
1038
+ keyword_matches
1039
  ]
1040
+ ).then(
1041
+ fn=lambda pdf_bytes: gr.File(value=pdf_bytes, visible=True),
1042
+ inputs=submit_btn.outputs[1], # The PDF bytes are the second return value
1043
+ outputs=download_btn
 
 
 
 
 
 
1044
  )
1045
 
1046
  if __name__ == "__main__":