Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,11 +12,9 @@ from io import BytesIO
|
|
| 12 |
import uuid
|
| 13 |
import logging
|
| 14 |
import textwrap
|
| 15 |
-
from
|
| 16 |
-
from
|
| 17 |
-
from
|
| 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
|
| 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 |
-
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 |
-
|
| 657 |
-
|
| 658 |
-
|
|
|
|
| 659 |
|
| 660 |
-
def analyze_pdf(file_obj) -> List:
|
| 661 |
-
"""Main analysis function
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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':
|
| 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 |
-
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
'
|
| 917 |
-
'
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
'
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
|
|
|
| 926 |
|
| 927 |
-
# Generate PDF report
|
| 928 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
|
| 930 |
-
# Return all outputs including the PDF data
|
| 931 |
return [
|
| 932 |
risk_summary_html,
|
| 933 |
-
"", # risk_visualization
|
| 934 |
-
|
| 935 |
-
|
| 936 |
-
|
| 937 |
-
|
| 938 |
-
|
| 939 |
-
"", # sentiment_analysis
|
| 940 |
-
keyword_matches_html
|
| 941 |
-
|
| 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 |
-
|
|
|
|
| 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 |
-
#
|
| 1010 |
-
|
| 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
|
| 1021 |
]
|
| 1022 |
-
)
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 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__":
|