Kushalmanda commited on
Commit
1b0b073
Β·
verified Β·
1 Parent(s): 6830511

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +293 -98
app.py CHANGED
@@ -11,6 +11,7 @@ import base64
11
  from io import BytesIO
12
  import uuid
13
  import logging
 
14
 
15
  # Set up logging
16
  logging.basicConfig(level=logging.INFO)
@@ -27,136 +28,236 @@ body {
27
  min-height: 100vh;
28
  margin: 0;
29
  padding: 0;
30
- font-family: Arial, sans-serif;
31
  }
32
  .gradio-container {
33
- background-color: rgba(240, 248, 255, 0.95) !important;
34
- border-radius: 10px;
35
- padding: 20px;
36
- margin: 20px;
37
- max-width: 100%;
38
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
39
  min-height: 90vh;
40
  border: 1px solid #1e90ff !important;
41
  }
42
  .risk-low { color: #28a745; font-weight: bold; }
43
- .risk-medium { color: #ffc107; font-weight: bold; }
44
- .risk-high { color: #dc3545; font-weight: bold; }
45
  .result-box {
46
  padding: 20px;
47
- border-radius: 5px;
48
- margin-bottom: 20px;
49
- background-color: rgba(240, 248, 255, 0.9);
 
50
  border-left: 5px solid #1e90ff !important;
51
  }
52
  .penalty-box {
53
- padding: 15px;
54
- border-radius: 5px;
55
- margin-bottom: 15px;
56
- border-left: 5px solid #dc3545;
57
- background-color: rgba(255, 245, 245, 0.9);
 
58
  }
59
  .obligation-box {
60
- padding: 15px;
61
- border-radius: 5px;
62
- margin-bottom: 15px;
63
- border-left: 5px solid #ffc107;
64
- background-color: rgba(255, 249, 230, 0.9);
 
65
  }
66
  .delay-box {
67
- padding: 15px;
68
- border-radius: 5px;
69
- margin-bottom: 15px;
70
- border-left: 5px solid #17a2b8;
71
- background-color: rgba(230, 249, 255, 0.9);
 
72
  }
73
  .combined-risk-container {
74
  display: flex;
75
  flex-direction: column;
76
- gap: 10px;
77
- margin-bottom: 20px;
 
 
 
 
78
  }
79
  .risk-row {
80
  display: flex;
81
  align-items: center;
82
- gap: 15px;
83
- padding: 10px;
84
- border-radius: 5px;
85
- background-color: rgba(240, 248, 255, 0.8);
86
- border: 1px solid #add8e6 !important;
 
 
 
 
 
87
  }
88
  .risk-label {
89
- width: 120px;
90
- font-weight: bold;
91
  font-size: 16px;
92
- color: #1e4b8f !important;
93
  }
94
  .risk-score {
95
- width: 100px;
96
- font-size: 18px;
97
  text-align: center;
 
 
98
  }
99
  .heatmap-wrapper {
100
  flex-grow: 1;
 
101
  }
102
  .warning-box {
103
- padding: 15px;
104
- border-radius: 5px;
105
- margin: 10px 0;
106
- background-color: rgba(255, 243, 205, 0.9);
107
- border-left: 5px solid #ffc107;
108
- font-weight: bold;
 
109
  }
110
  .danger-box {
111
- padding: 15px;
112
- border-radius: 5px;
113
- margin: 10px 0;
114
- background-color: rgba(248, 215, 218, 0.9);
115
- border-left: 5px solid #dc3545;
116
- font-weight: bold;
 
117
  }
118
  .success-box {
119
- padding: 15px;
120
- border-radius: 5px;
121
- margin: 10px 0;
122
- background-color: rgba(212, 237, 218, 0.9);
123
- border-left: 5px solid #28a745;
124
- font-weight: bold;
 
 
 
 
 
 
 
 
 
 
125
  }
126
  .section-title {
127
- font-size: 20px;
128
- font-weight: bold;
129
- margin-bottom: 15px;
130
  color: #1e4b8f !important;
 
 
 
131
  }
132
  .count-item {
133
  display: flex;
134
  justify-content: space-between;
135
- padding: 10px 0;
136
- border-bottom: 1px solid #add8e6 !important;
 
 
 
 
 
137
  }
138
  .count-label {
139
- font-weight: bold;
140
- color: #1e4b8f !important;
 
 
 
141
  }
142
  .count-value {
143
  color: #4169e1 !important;
 
 
144
  }
145
  button {
146
  background: linear-gradient(135deg, #1e90ff, #4169e1) !important;
147
  border: none !important;
148
  color: white !important;
149
- font-weight: bold !important;
 
 
 
 
150
  }
151
  button:hover {
152
  background: linear-gradient(135deg, #4169e1, #1e90ff) !important;
 
 
153
  }
154
  .upload-area {
155
  border: 2px dashed #1e90ff !important;
156
  background-color: rgba(240, 248, 255, 0.7) !important;
 
 
 
157
  }
158
  .upload-area:hover {
159
  background-color: rgba(224, 255, 255, 0.7) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  }
161
  /* Hide elements */
162
  footer, .gradio-footer, .hide, [data-testid="Use via API"], [data-testid="mmsettings"] {
@@ -272,6 +373,34 @@ def calculate_risk_score(penalty_count: int, penalty_values: List[float], obliga
272
  else:
273
  return score, "High"
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  def generate_heatmap(risk_level: str):
276
  """Generate a simple heatmap based on risk level"""
277
  try:
@@ -279,10 +408,13 @@ def generate_heatmap(risk_level: str):
279
 
280
  if risk_level == "Low":
281
  cmap = plt.cm.Blues
 
282
  elif risk_level == "Medium":
283
  cmap = plt.cm.Oranges
 
284
  else:
285
  cmap = plt.cm.Reds
 
286
 
287
  gradient = np.linspace(0, 1, 256).reshape(1, -1)
288
  gradient = np.vstack((gradient, gradient))
@@ -322,11 +454,22 @@ def save_to_salesforce(sf: Salesforce, data: Dict):
322
  def format_warning_message(count: int, item_type: str, emoji: str) -> str:
323
  """Format warning message based on count with appropriate color coding"""
324
  if count == 0:
325
- return f"""<div class="success-box">βœ… {emoji} No {item_type} clauses detected - Good!</div>"""
 
 
326
  elif count < 5:
327
  return f"""<div class="warning-box">⚠️ {emoji} {count} {item_type} clauses detected - Review recommended</div>"""
328
  else:
329
- return f"""<div class="danger-box">🚨 {emoji} {count} {item_type} clauses detected - High Risk!</div>"""
 
 
 
 
 
 
 
 
 
330
 
331
  def analyze_pdf(file_obj) -> List:
332
  """Main analysis function for Gradio interface"""
@@ -380,15 +523,17 @@ def analyze_pdf(file_obj) -> List:
380
 
381
  try:
382
  heatmap = generate_heatmap(risk_level)
 
 
383
  except Exception as e:
384
- raise Exception(f"Heatmap generation failed: {str(e)}")
385
 
386
  # Format details with warning messages and emojis
387
  penalty_details = f"""
388
  {penalty_warning}
389
  <div class='penalty-box'>
390
  <div class='section-title'>πŸ’° Penalty Clause Details</div>
391
- {"".join([f"<div class='count-item'><span class='count-label'>{kw}</span><span class='count-value'>{count}</span></div>" for kw, count in penalty_counts.items()])}
392
  </div>
393
  """
394
 
@@ -396,7 +541,7 @@ def analyze_pdf(file_obj) -> List:
396
  {obligation_warning}
397
  <div class='obligation-box'>
398
  <div class='section-title'>πŸ“ Obligation Clause Details</div>
399
- {"".join([f"<div class='count-item'><span class='count-label'>{kw}</span><span class='count-value'>{count}</span></div>" for kw, count in obligation_counts.items()])}
400
  </div>
401
  """
402
 
@@ -404,18 +549,18 @@ def analyze_pdf(file_obj) -> List:
404
  {delay_warning}
405
  <div class='delay-box'>
406
  <div class='section-title'>⏱️ Delay Clause Details</div>
407
- {"".join([f"<div class='count-item'><span class='count-label'>{kw}</span><span class='count-value'>{count}</span></div>" for kw, count in delay_counts.items()])}
408
  </div>
409
  """
410
 
411
- 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</div>"
412
 
413
  penalty_sentences = []
414
  for sentence in re.split(r'(?<=[.!?])\s+', text):
415
  if any(kw.lower() in sentence.lower() for kw in penalty_keywords):
416
  penalty_sentences.append(sentence.strip())
417
 
418
- penalty_examples = "\n\n".join([f"{i+1}. {sent}" for i, sent in enumerate(penalty_sentences[:3])]) if penalty_sentences else "<div class='success-box'>βœ… No penalty clauses found</div>"
419
 
420
  record_id = str(uuid.uuid4())
421
  sf_data = {
@@ -437,69 +582,119 @@ def analyze_pdf(file_obj) -> List:
437
  logger.error(f"Salesforce record creation failed: {str(e)}")
438
 
439
  box_class = "success-box" if risk_level == "Low" else "warning-box" if risk_level == "Medium" else "danger-box"
 
 
 
 
 
 
440
 
441
  return [
442
- f"<div class='risk-row'><span class='risk-label'>Risk Score</span><span class='risk-score risk-{risk_level.lower()}'>{risk_score:.1f}/100</span></div>",
443
- f"<div class='risk-row'><span class='risk-label'>Risk Level</span><span class='risk-score risk-{risk_level.lower()}'>{risk_level}</span></div>",
444
- heatmap,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  penalty_details,
446
- f"<div class='penalty-box'><div class='section-title'>πŸ’° Penalty Amounts</div>{penalty_amounts}</div>",
447
  obligation_details,
448
  delay_details,
449
  f"<div class='result-box'><div class='section-title'>πŸ“œ Example Penalty Clauses</div>{penalty_examples}</div>",
450
- f"<div class='{box_class}'><div class='section-title'>😊 Sentiment Analysis</div>Sentiment Score: {sentiment_score}</div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  ]
452
  except Exception as e:
453
  logger.error(f"Analysis failed: {str(e)}")
454
- error_message = f"<div class='danger-box'>❌ Error: {str(e)}</div>"
455
- return [error_message] * 9
 
 
 
 
 
 
 
 
 
 
 
456
 
457
  # Create Gradio interface with blue theme and hidden elements
458
  with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Default(primary_hue="blue")) as demo:
459
  gr.Markdown("""
460
- <div style='text-align: center;'>
461
- <h1 style='color: #1e4b8f;'>πŸ“„ PDF Contract Risk Analyzer</h1>
462
- <p style='color: #4169e1;'>Upload a contract PDF to analyze penalties, obligations, delays, and sentiment.</p>
 
 
463
  </div>
464
  """)
465
 
466
  with gr.Row():
467
  with gr.Column(scale=1):
468
- file_input = gr.File(label="πŸ“€ Upload PDF", file_types=[".pdf"], elem_classes="upload-area")
469
- submit_btn = gr.Button("πŸ” Analyze PDF", variant="primary")
 
 
 
 
 
470
 
471
  with gr.Column(scale=3):
472
- gr.Markdown("<div class='section-title'>πŸ” Overall Risk Assessment</div>")
473
- with gr.Group(elem_classes="combined-risk-container"):
474
- risk_score = gr.HTML(label="Risk Score")
475
- risk_level = gr.HTML(label="Risk Level")
476
- heatmap = gr.Plot(label="Risk Heatmap", elem_classes="heatmap-wrapper")
477
 
478
  with gr.Row():
479
  with gr.Column():
480
- penalty_count = gr.HTML(label="πŸ’° Penalty Clauses")
481
- penalty_amounts = gr.HTML(label="πŸ’° Penalty Amounts")
482
 
483
  with gr.Column():
484
- obligation_count = gr.HTML(label="πŸ“ Obligation Clauses")
485
 
486
  with gr.Column():
487
- delay_count = gr.HTML(label="⏱️ Delay Clauses")
488
 
489
  with gr.Row():
490
- penalty_examples = gr.HTML(label="πŸ“œ Example Penalty Clauses")
491
 
492
  with gr.Row():
493
- additional_results = gr.HTML(label="😊 Sentiment Analysis")
494
 
495
  submit_btn.click(
496
  fn=analyze_pdf,
497
  inputs=file_input,
498
  outputs=[
499
- risk_score, risk_level, heatmap,
500
  penalty_count, penalty_amounts,
501
  obligation_count, delay_count,
502
- penalty_examples, additional_results
503
  ]
504
  )
505
 
 
11
  from io import BytesIO
12
  import uuid
13
  import logging
14
+ import textwrap
15
 
16
  # Set up logging
17
  logging.basicConfig(level=logging.INFO)
 
28
  min-height: 100vh;
29
  margin: 0;
30
  padding: 0;
31
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
32
  }
33
  .gradio-container {
34
+ background-color: rgba(255, 255, 255, 0.97) !important;
35
+ border-radius: 15px;
36
+ padding: 25px;
37
+ margin: 20px auto;
38
+ max-width: 1200px;
39
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12);
40
  min-height: 90vh;
41
  border: 1px solid #1e90ff !important;
42
  }
43
  .risk-low { color: #28a745; font-weight: bold; }
44
+ .risk-medium { color: #ff9800; font-weight: bold; }
45
+ .risk-high { color: #f44336; font-weight: bold; }
46
  .result-box {
47
  padding: 20px;
48
+ border-radius: 10px;
49
+ margin-bottom: 25px;
50
+ background-color: white;
51
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
52
  border-left: 5px solid #1e90ff !important;
53
  }
54
  .penalty-box {
55
+ padding: 20px;
56
+ border-radius: 10px;
57
+ margin-bottom: 20px;
58
+ border-left: 5px solid #f44336;
59
+ background-color: white;
60
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
61
  }
62
  .obligation-box {
63
+ padding: 20px;
64
+ border-radius: 10px;
65
+ margin-bottom: 20px;
66
+ border-left: 5px solid #ff9800;
67
+ background-color: white;
68
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
69
  }
70
  .delay-box {
71
+ padding: 20px;
72
+ border-radius: 10px;
73
+ margin-bottom: 20px;
74
+ border-left: 5px solid #2196F3;
75
+ background-color: white;
76
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
77
  }
78
  .combined-risk-container {
79
  display: flex;
80
  flex-direction: column;
81
+ gap: 15px;
82
+ margin-bottom: 25px;
83
+ background-color: white;
84
+ padding: 20px;
85
+ border-radius: 10px;
86
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
87
  }
88
  .risk-row {
89
  display: flex;
90
  align-items: center;
91
+ gap: 20px;
92
+ padding: 15px;
93
+ border-radius: 8px;
94
+ background-color: #f8f9fa;
95
+ border: 1px solid #e0e0e0 !important;
96
+ transition: all 0.3s ease;
97
+ }
98
+ .risk-row:hover {
99
+ transform: translateY(-2px);
100
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
101
  }
102
  .risk-label {
103
+ width: 150px;
104
+ font-weight: 600;
105
  font-size: 16px;
106
+ color: #2c3e50 !important;
107
  }
108
  .risk-score {
109
+ width: 120px;
110
+ font-size: 20px;
111
  text-align: center;
112
+ padding: 8px 12px;
113
+ border-radius: 6px;
114
  }
115
  .heatmap-wrapper {
116
  flex-grow: 1;
117
+ margin-top: 15px;
118
  }
119
  .warning-box {
120
+ padding: 18px;
121
+ border-radius: 8px;
122
+ margin: 15px 0;
123
+ background-color: #fff3e0;
124
+ border-left: 5px solid #ff9800;
125
+ font-weight: 600;
126
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
127
  }
128
  .danger-box {
129
+ padding: 18px;
130
+ border-radius: 8px;
131
+ margin: 15px 0;
132
+ background-color: #ffebee;
133
+ border-left: 5px solid #f44336;
134
+ font-weight: 600;
135
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
136
  }
137
  .success-box {
138
+ padding: 18px;
139
+ border-radius: 8px;
140
+ margin: 15px 0;
141
+ background-color: #e8f5e9;
142
+ border-left: 5px solid #4CAF50;
143
+ font-weight: 600;
144
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
145
+ }
146
+ .info-box {
147
+ padding: 18px;
148
+ border-radius: 8px;
149
+ margin: 15px 0;
150
+ background-color: #e3f2fd;
151
+ border-left: 5px solid #2196F3;
152
+ font-weight: 600;
153
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
154
  }
155
  .section-title {
156
+ font-size: 22px;
157
+ font-weight: 700;
158
+ margin-bottom: 18px;
159
  color: #1e4b8f !important;
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 10px;
163
  }
164
  .count-item {
165
  display: flex;
166
  justify-content: space-between;
167
+ padding: 12px 0;
168
+ border-bottom: 1px solid #e0e0e0 !important;
169
+ transition: all 0.2s ease;
170
+ }
171
+ .count-item:hover {
172
+ background-color: #f5f5f5;
173
+ transform: translateX(5px);
174
  }
175
  .count-label {
176
+ font-weight: 600;
177
+ color: #2c3e50 !important;
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 8px;
181
  }
182
  .count-value {
183
  color: #4169e1 !important;
184
+ font-weight: 600;
185
+ font-size: 16px;
186
  }
187
  button {
188
  background: linear-gradient(135deg, #1e90ff, #4169e1) !important;
189
  border: none !important;
190
  color: white !important;
191
+ font-weight: 600 !important;
192
+ padding: 12px 24px !important;
193
+ border-radius: 8px !important;
194
+ transition: all 0.3s ease !important;
195
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
196
  }
197
  button:hover {
198
  background: linear-gradient(135deg, #4169e1, #1e90ff) !important;
199
+ transform: translateY(-2px) !important;
200
+ box-shadow: 0 6px 12px rgba(0,0,0,0.15) !important;
201
  }
202
  .upload-area {
203
  border: 2px dashed #1e90ff !important;
204
  background-color: rgba(240, 248, 255, 0.7) !important;
205
+ border-radius: 10px !important;
206
+ padding: 30px !important;
207
+ transition: all 0.3s ease !important;
208
  }
209
  .upload-area:hover {
210
  background-color: rgba(224, 255, 255, 0.7) !important;
211
+ border-color: #4169e1 !important;
212
+ }
213
+ .risk-meter {
214
+ width: 100%;
215
+ height: 20px;
216
+ background: linear-gradient(90deg, #4CAF50, #FFC107, #F44336);
217
+ border-radius: 10px;
218
+ margin: 15px 0;
219
+ position: relative;
220
+ }
221
+ .risk-meter-indicator {
222
+ position: absolute;
223
+ top: -5px;
224
+ width: 3px;
225
+ height: 30px;
226
+ background-color: #2c3e50;
227
+ transform: translateX(-50%);
228
+ }
229
+ .risk-meter-labels {
230
+ display: flex;
231
+ justify-content: space-between;
232
+ margin-top: 5px;
233
+ font-size: 12px;
234
+ color: #666;
235
+ }
236
+ .clause-example {
237
+ background-color: #f5f7fa;
238
+ padding: 15px;
239
+ border-radius: 8px;
240
+ margin-bottom: 10px;
241
+ border-left: 3px solid #1e90ff;
242
+ font-family: 'Courier New', monospace;
243
+ line-height: 1.5;
244
+ }
245
+ .clause-number {
246
+ font-weight: bold;
247
+ color: #1e90ff;
248
+ margin-right: 8px;
249
+ }
250
+ .sentiment-meter {
251
+ width: 100%;
252
+ height: 20px;
253
+ background: linear-gradient(90deg, #F44336, #FFC107, #4CAF50);
254
+ border-radius: 10px;
255
+ margin: 15px 0;
256
+ }
257
+ .sentiment-score {
258
+ height: 100%;
259
+ border-radius: 10px;
260
+ background-color: rgba(255,255,255,0.3);
261
  }
262
  /* Hide elements */
263
  footer, .gradio-footer, .hide, [data-testid="Use via API"], [data-testid="mmsettings"] {
 
373
  else:
374
  return score, "High"
375
 
376
+ def generate_risk_meter(risk_score: float) -> str:
377
+ """Generate a visual risk meter with indicator"""
378
+ position = risk_score
379
+ return f"""
380
+ <div class="risk-meter">
381
+ <div class="risk-meter-indicator" style="left: {position}%"></div>
382
+ </div>
383
+ <div class="risk-meter-labels">
384
+ <span>Low (0-30)</span>
385
+ <span>Medium (31-69)</span>
386
+ <span>High (70-100)</span>
387
+ </div>
388
+ """
389
+
390
+ def generate_sentiment_meter(sentiment_score: float) -> str:
391
+ """Generate a visual sentiment meter"""
392
+ width = sentiment_score * 100
393
+ return f"""
394
+ <div class="sentiment-meter">
395
+ <div class="sentiment-score" style="width: {width}%"></div>
396
+ </div>
397
+ <div style="display: flex; justify-content: space-between; margin-top: 5px;">
398
+ <span>Negative</span>
399
+ <span>Neutral</span>
400
+ <span>Positive</span>
401
+ </div>
402
+ """
403
+
404
  def generate_heatmap(risk_level: str):
405
  """Generate a simple heatmap based on risk level"""
406
  try:
 
408
 
409
  if risk_level == "Low":
410
  cmap = plt.cm.Blues
411
+ color = '#4CAF50'
412
  elif risk_level == "Medium":
413
  cmap = plt.cm.Oranges
414
+ color = '#FF9800'
415
  else:
416
  cmap = plt.cm.Reds
417
+ color = '#F44336'
418
 
419
  gradient = np.linspace(0, 1, 256).reshape(1, -1)
420
  gradient = np.vstack((gradient, gradient))
 
454
  def format_warning_message(count: int, item_type: str, emoji: str) -> str:
455
  """Format warning message based on count with appropriate color coding"""
456
  if count == 0:
457
+ return f"""<div class="success-box">βœ… {emoji} No {item_type} clauses detected - This is excellent!</div>"""
458
+ elif count < 3:
459
+ return f"""<div class="info-box">ℹ️ {emoji} {count} {item_type} clauses detected - Standard contract language</div>"""
460
  elif count < 5:
461
  return f"""<div class="warning-box">⚠️ {emoji} {count} {item_type} clauses detected - Review recommended</div>"""
462
  else:
463
+ return f"""<div class="danger-box">🚨 {emoji} {count} {item_type} clauses detected - High Risk! Needs immediate attention</div>"""
464
+
465
+ def format_clause_example(example: str, index: int) -> str:
466
+ """Format a clause example with proper wrapping and styling"""
467
+ wrapped_text = textwrap.fill(example, width=80)
468
+ return f"""
469
+ <div class="clause-example">
470
+ <span class="clause-number">{index}.</span> {wrapped_text}
471
+ </div>
472
+ """
473
 
474
  def analyze_pdf(file_obj) -> List:
475
  """Main analysis function for Gradio interface"""
 
523
 
524
  try:
525
  heatmap = generate_heatmap(risk_level)
526
+ risk_meter = generate_risk_meter(risk_score)
527
+ sentiment_meter = generate_sentiment_meter(sentiment_score)
528
  except Exception as e:
529
+ raise Exception(f"Visual generation failed: {str(e)}")
530
 
531
  # Format details with warning messages and emojis
532
  penalty_details = f"""
533
  {penalty_warning}
534
  <div class='penalty-box'>
535
  <div class='section-title'>πŸ’° Penalty Clause Details</div>
536
+ {"".join([f"<div class='count-item'><span class='count-label'><span style='color: #f44336'>β€’</span> {kw}</span><span class='count-value'>{count}</span></div>" for kw, count in penalty_counts.items()])}
537
  </div>
538
  """
539
 
 
541
  {obligation_warning}
542
  <div class='obligation-box'>
543
  <div class='section-title'>πŸ“ Obligation Clause Details</div>
544
+ {"".join([f"<div class='count-item'><span class='count-label'><span style='color: #ff9800'>β€’</span> {kw}</span><span class='count-value'>{count}</span></div>" for kw, count in obligation_counts.items()])}
545
  </div>
546
  """
547
 
 
549
  {delay_warning}
550
  <div class='delay-box'>
551
  <div class='section-title'>⏱️ Delay Clause Details</div>
552
+ {"".join([f"<div class='count-item'><span class='count-label'><span style='color: #2196F3'>β€’</span> {kw}</span><span class='count-value'>{count}</span></div>" for kw, count in delay_counts.items()])}
553
  </div>
554
  """
555
 
556
+ 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>"
557
 
558
  penalty_sentences = []
559
  for sentence in re.split(r'(?<=[.!?])\s+', text):
560
  if any(kw.lower() in sentence.lower() for kw in penalty_keywords):
561
  penalty_sentences.append(sentence.strip())
562
 
563
+ 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>"
564
 
565
  record_id = str(uuid.uuid4())
566
  sf_data = {
 
582
  logger.error(f"Salesforce record creation failed: {str(e)}")
583
 
584
  box_class = "success-box" if risk_level == "Low" else "warning-box" if risk_level == "Medium" else "danger-box"
585
+ risk_icon = "βœ…" if risk_level == "Low" else "⚠️" if risk_level == "Medium" else "🚨"
586
+ risk_advice = {
587
+ "Low": "This contract appears to be low risk. Standard review recommended.",
588
+ "Medium": "This contract has moderate risk. Careful review advised.",
589
+ "High": "This contract is high risk! Immediate legal review required."
590
+ }
591
 
592
  return [
593
+ f"""
594
+ <div class='result-box'>
595
+ <div class='section-title'>{risk_icon} Contract Risk Summary</div>
596
+ <div class='risk-row'>
597
+ <span class='risk-label'>Overall Risk Score</span>
598
+ <span class='risk-score risk-{risk_level.lower()}'>{risk_score:.1f}/100</span>
599
+ </div>
600
+ {risk_meter}
601
+ <div style='margin-top: 15px; font-size: 16px;'>
602
+ <strong>Assessment:</strong> {risk_advice[risk_level]}
603
+ </div>
604
+ </div>
605
+ """,
606
+ f"""
607
+ <div class='result-box'>
608
+ <div class='section-title'>πŸ“Š Risk Visualization</div>
609
+ {heatmap}
610
+ </div>
611
+ """,
612
  penalty_details,
613
+ f"<div class='penalty-box'><div class='section-title'>πŸ’° Penalty Amounts Found</div>{penalty_amounts}</div>",
614
  obligation_details,
615
  delay_details,
616
  f"<div class='result-box'><div class='section-title'>πŸ“œ Example Penalty Clauses</div>{penalty_examples}</div>",
617
+ f"""
618
+ <div class='result-box'>
619
+ <div class='section-title'>😊 Contract Sentiment Analysis</div>
620
+ <div style='margin-bottom: 10px;'>Score: {sentiment_score:.2f} (0 = Negative, 1 = Positive)</div>
621
+ {sentiment_meter}
622
+ <div style='margin-top: 15px;'>
623
+ <strong>Interpretation:</strong> {
624
+ "Positive tone detected" if sentiment_score > 0.7
625
+ else "Neutral tone detected" if sentiment_score > 0.4
626
+ else "Negative tone detected"
627
+ }
628
+ </div>
629
+ </div>
630
+ """
631
  ]
632
  except Exception as e:
633
  logger.error(f"Analysis failed: {str(e)}")
634
+ error_message = f"""
635
+ <div class='danger-box'>
636
+ <div style='display: flex; align-items: center; gap: 10px;'>
637
+ <span style='font-size: 24px;'>❌</span>
638
+ <span style='font-size: 18px; font-weight: bold;'>Analysis Error</span>
639
+ </div>
640
+ <div style='margin-top: 10px;'>{str(e)}</div>
641
+ <div style='margin-top: 15px; font-size: 14px;'>
642
+ Please ensure you've uploaded a valid PDF document with selectable text.
643
+ </div>
644
+ </div>
645
+ """
646
+ return [error_message] * 8
647
 
648
  # Create Gradio interface with blue theme and hidden elements
649
  with gr.Blocks(css=css, title="PDF Contract Risk Analyzer", theme=gr.themes.Default(primary_hue="blue")) as demo:
650
  gr.Markdown("""
651
+ <div style='text-align: center; margin-bottom: 30px;'>
652
+ <h1 style='color: #1e4b8f; margin-bottom: 10px;'>πŸ“„ PDF Contract Risk Analyzer</h1>
653
+ <p style='color: #4169e1; font-size: 16px;'>
654
+ Upload a contract PDF to analyze risks, obligations, and sentiment. Get instant insights into potential issues.
655
+ </p>
656
  </div>
657
  """)
658
 
659
  with gr.Row():
660
  with gr.Column(scale=1):
661
+ file_input = gr.File(
662
+ label="πŸ“€ Upload Contract PDF",
663
+ file_types=[".pdf"],
664
+ elem_classes="upload-area",
665
+ info="Drag and drop your contract PDF file here"
666
+ )
667
+ submit_btn = gr.Button("πŸ” Analyze Contract", variant="primary")
668
 
669
  with gr.Column(scale=3):
670
+ risk_summary = gr.HTML(label="Contract Risk Summary")
671
+ risk_visualization = gr.HTML(label="Risk Visualization")
 
 
 
672
 
673
  with gr.Row():
674
  with gr.Column():
675
+ penalty_count = gr.HTML(label="Penalty Clauses Analysis")
676
+ penalty_amounts = gr.HTML(label="Penalty Amounts Found")
677
 
678
  with gr.Column():
679
+ obligation_count = gr.HTML(label="Obligation Clauses Analysis")
680
 
681
  with gr.Column():
682
+ delay_count = gr.HTML(label="Delay Clauses Analysis")
683
 
684
  with gr.Row():
685
+ penalty_examples = gr.HTML(label="Example Penalty Clauses")
686
 
687
  with gr.Row():
688
+ sentiment_analysis = gr.HTML(label="Contract Sentiment Analysis")
689
 
690
  submit_btn.click(
691
  fn=analyze_pdf,
692
  inputs=file_input,
693
  outputs=[
694
+ risk_summary, risk_visualization,
695
  penalty_count, penalty_amounts,
696
  obligation_count, delay_count,
697
+ penalty_examples, sentiment_analysis
698
  ]
699
  )
700