sagar007 commited on
Commit
363f959
Β·
verified Β·
1 Parent(s): 87e7bbd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +559 -56
app.py CHANGED
@@ -1,106 +1,609 @@
1
  import gradio as gr
2
  import os
3
- from typing import List, Dict
4
  import json
 
5
 
6
  # Mock backend functions to simulate AWS Bedrock Agents
7
- def validate_documents(files: List[str]) -> str:
8
  """Simulate Intake Agent: Check if all required docs are present."""
 
 
 
9
  required = ["ID", "Pay Stubs", "W-2", "Tax Returns", "Bank Statements", "Offer", "Insurance", "Title"]
10
  uploaded = [os.path.basename(f).split(".")[0] for f in files]
11
  missing = [doc for doc in required if doc not in uploaded]
 
12
  if missing:
13
- return f"Missing documents: {', '.join(missing)}"
14
- return "All documents validated successfully."
15
 
16
  def extract_data(files: List[str], ssn: str) -> Dict:
17
  """Simulate Extraction Agent: Extract data from documents."""
 
 
 
18
  return {
19
- "application_id": 123,
20
  "ssn": ssn,
21
- "monthly_income": 5000,
22
- "monthly_debts": 1000,
23
- "credit_score": 650,
24
- "property_value": 300000,
25
- "loan_amount": 240000,
 
26
  "insurance_coverage": "12_months",
27
- "title_status": "Clear"
 
 
28
  }
29
 
30
  def analyze_data(data: Dict) -> Dict:
31
  """Simulate Credit/Capacity/Collateral/Compliance Agents: Analyze data."""
 
 
 
32
  dti = (data["monthly_debts"] / data["monthly_income"]) * 100
33
  ltv = (data["loan_amount"] / data["property_value"]) * 100
34
  flags = []
 
 
35
  if dti > 45:
36
- flags.append("High DTI")
 
 
 
 
 
37
  if data["credit_score"] < 620:
38
- flags.append("Low credit score")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  if "Clear" not in data["title_status"]:
40
- flags.append("Title issue")
 
 
 
 
 
 
41
  return {
42
- "DTI": round(dti, 2),
43
- "LTV": round(ltv, 2),
 
 
44
  "credit_score": data["credit_score"],
 
 
 
 
 
45
  "flags": flags,
46
- "status": "Needs review" if flags else "Ready for approval"
 
47
  }
48
 
49
  def handle_conditions(additional_files: List[str], comments: str) -> str:
50
  """Simulate Conditions Agent: Process additional uploads."""
51
- if additional_files:
52
- return f"Additional files received: {', '.join([os.path.basename(f) for f in additional_files])}. Comments: {comments}"
53
- return "No additional files uploaded."
 
 
 
 
 
 
54
 
55
- def finalize_approval(approve: bool, comments: str, analysis: Dict) -> str:
56
  """Simulate Final Approval Agent: Finalize decision."""
57
- if approve and not analysis["flags"]:
58
- return "Clear to Close. Closing package generated."
59
- elif approve:
60
- return f"Approved with conditions: {', '.join(analysis['flags'])}. Comments: {comments}"
61
- else:
62
- return f"Rejected. Comments: {comments}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  # Gradio Interface
65
- with gr.Blocks(title="Mortgage Underwriting Automation") as app:
66
- gr.Markdown("# Mortgage Underwriting Automation")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  # Section 1: Document Upload
69
  with gr.Row():
70
  with gr.Column():
71
- gr.Markdown("## Upload Documents")
72
- files_input = gr.File(label="Upload Documents (ID, Pay Stubs, W-2s, etc.)", file_count="multiple", file_types=[".pdf", ".png", ".jpg"])
73
- ssn_input = gr.Textbox(label="Social Security Number", placeholder="XXX-XX-XXXX")
74
- validate_btn = gr.Button("Validate Documents")
75
- validate_output = gr.Textbox(label="Validation Result")
76
- validate_btn.click(fn=validate_documents, inputs=files_input, outputs=validate_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  # Section 2: Data Extraction and Analysis
79
  with gr.Row():
80
  with gr.Column():
81
- gr.Markdown("## Extracted Data and Analysis")
82
- extract_btn = gr.Button("Extract and Analyze")
83
- analysis_output = gr.JSON(label="Analysis Results (DTI, LTV, Flags)")
84
- extract_btn.click(fn=lambda files, ssn: analyze_data(extract_data(files, ssn)), inputs=[files_input, ssn_input], outputs=analysis_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- # Section 3: Conditions
87
  with gr.Row():
88
  with gr.Column():
89
- gr.Markdown("## Conditions (Additional Documents)")
90
- additional_files = gr.File(label="Upload Additional Documents (e.g., Gift Letter)", file_count="multiple", file_types=[".pdf", ".png", ".jpg"])
91
- condition_comments = gr.Textbox(label="Comments for Conditions")
92
- condition_btn = gr.Button("Submit Additional Documents")
93
- condition_output = gr.Textbox(label="Condition Status")
94
- condition_btn.click(fn=handle_conditions, inputs=[additional_files, condition_comments], outputs=condition_output)
95
-
96
- # Section 4: Final Approval
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  with gr.Row():
98
  with gr.Column():
99
- gr.Markdown("## Final Approval")
100
- approve_radio = gr.Radio(choices=["Approve", "Reject"], label="Decision")
101
- approval_comments = gr.Textbox(label="Approval/Rejection Comments")
102
- approve_btn = gr.Button("Submit Decision")
103
- final_output = gr.Textbox(label="Final Status")
104
- approve_btn.click(fn=finalize_approval, inputs=[approve_radio, approval_comments, analysis_output], outputs=final_output)
105
-
106
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
  import os
3
+ from typing import List, Dict, Tuple
4
  import json
5
+ import datetime
6
 
7
  # Mock backend functions to simulate AWS Bedrock Agents
8
+ def validate_documents(files: List[str]) -> Tuple[str, str]:
9
  """Simulate Intake Agent: Check if all required docs are present."""
10
+ if not files:
11
+ return "❌ No documents uploaded", "error"
12
+
13
  required = ["ID", "Pay Stubs", "W-2", "Tax Returns", "Bank Statements", "Offer", "Insurance", "Title"]
14
  uploaded = [os.path.basename(f).split(".")[0] for f in files]
15
  missing = [doc for doc in required if doc not in uploaded]
16
+
17
  if missing:
18
+ return f"⚠️ Missing documents: {', '.join(missing)}", "warning"
19
+ return "βœ… All documents validated successfully", "success"
20
 
21
  def extract_data(files: List[str], ssn: str) -> Dict:
22
  """Simulate Extraction Agent: Extract data from documents."""
23
+ if not files or not ssn:
24
+ return {}
25
+
26
  return {
27
+ "application_id": "APP-2024-001234",
28
  "ssn": ssn,
29
+ "applicant_name": "John Doe",
30
+ "monthly_income": 8500,
31
+ "monthly_debts": 2100,
32
+ "credit_score": 720,
33
+ "property_value": 450000,
34
+ "loan_amount": 360000,
35
  "insurance_coverage": "12_months",
36
+ "title_status": "Clear",
37
+ "employment_status": "Full-time",
38
+ "years_employed": 3.5
39
  }
40
 
41
  def analyze_data(data: Dict) -> Dict:
42
  """Simulate Credit/Capacity/Collateral/Compliance Agents: Analyze data."""
43
+ if not data:
44
+ return {}
45
+
46
  dti = (data["monthly_debts"] / data["monthly_income"]) * 100
47
  ltv = (data["loan_amount"] / data["property_value"]) * 100
48
  flags = []
49
+ risk_level = "Low"
50
+
51
  if dti > 45:
52
+ flags.append("High DTI Ratio")
53
+ risk_level = "High"
54
+ elif dti > 36:
55
+ flags.append("Elevated DTI Ratio")
56
+ risk_level = "Medium"
57
+
58
  if data["credit_score"] < 620:
59
+ flags.append("Low Credit Score")
60
+ risk_level = "High"
61
+ elif data["credit_score"] < 680:
62
+ flags.append("Fair Credit Score")
63
+ if risk_level != "High":
64
+ risk_level = "Medium"
65
+
66
+ if ltv > 95:
67
+ flags.append("High LTV Ratio")
68
+ risk_level = "High"
69
+ elif ltv > 80:
70
+ flags.append("Elevated LTV Ratio")
71
+ if risk_level == "Low":
72
+ risk_level = "Medium"
73
+
74
  if "Clear" not in data["title_status"]:
75
+ flags.append("Title Issues")
76
+ risk_level = "High"
77
+
78
+ status = "Ready for Approval" if not flags else "Requires Review"
79
+ if risk_level == "High":
80
+ status = "High Risk - Manual Review Required"
81
+
82
  return {
83
+ "application_id": data["application_id"],
84
+ "applicant_name": data["applicant_name"],
85
+ "dti_ratio": round(dti, 2),
86
+ "ltv_ratio": round(ltv, 2),
87
  "credit_score": data["credit_score"],
88
+ "monthly_income": f"${data['monthly_income']:,}",
89
+ "monthly_debts": f"${data['monthly_debts']:,}",
90
+ "loan_amount": f"${data['loan_amount']:,}",
91
+ "property_value": f"${data['property_value']:,}",
92
+ "risk_level": risk_level,
93
  "flags": flags,
94
+ "status": status,
95
+ "processed_date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
96
  }
97
 
98
  def handle_conditions(additional_files: List[str], comments: str) -> str:
99
  """Simulate Conditions Agent: Process additional uploads."""
100
+ if additional_files and comments.strip():
101
+ file_names = [os.path.basename(f) for f in additional_files]
102
+ return f"βœ… Additional documentation received: {', '.join(file_names)}\n\nπŸ“ Comments: {comments}\n\n⏳ Status: Under Review"
103
+ elif additional_files:
104
+ file_names = [os.path.basename(f) for f in additional_files]
105
+ return f"βœ… Additional files uploaded: {', '.join(file_names)}\n\n⚠️ Please provide comments for review"
106
+ elif comments.strip():
107
+ return f"πŸ“ Comments noted: {comments}\n\n⚠️ Please upload supporting documents"
108
+ return "❌ No additional files or comments provided"
109
 
110
+ def finalize_approval(decision: str, comments: str, analysis: Dict) -> str:
111
  """Simulate Final Approval Agent: Finalize decision."""
112
+ if not analysis:
113
+ return "❌ Error: No analysis data available. Please complete analysis first."
114
+
115
+ app_id = analysis.get("application_id", "N/A")
116
+ applicant = analysis.get("applicant_name", "N/A")
117
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
118
+
119
+ if decision == "Approve":
120
+ if not analysis.get("flags", []):
121
+ return f"""
122
+ πŸŽ‰ **APPLICATION APPROVED**
123
+
124
+ πŸ“‹ **Application ID:** {app_id}
125
+ πŸ‘€ **Applicant:** {applicant}
126
+ πŸ“… **Decision Date:** {timestamp}
127
+ βœ… **Status:** Clear to Close
128
+
129
+ πŸ“¦ **Next Steps:**
130
+ - Closing package will be generated automatically
131
+ - Borrower will be contacted within 24 hours
132
+ - Expected closing date: {(datetime.datetime.now() + datetime.timedelta(days=14)).strftime("%Y-%m-%d")}
133
+
134
+ πŸ’¬ **Comments:** {comments if comments.strip() else 'Standard approval - all criteria met'}
135
+ """
136
+ else:
137
+ return f"""
138
+ βœ… **APPLICATION APPROVED WITH CONDITIONS**
139
+
140
+ πŸ“‹ **Application ID:** {app_id}
141
+ πŸ‘€ **Applicant:** {applicant}
142
+ πŸ“… **Decision Date:** {timestamp}
143
+ ⚠️ **Conditions:** {', '.join(analysis['flags'])}
144
+
145
+ πŸ“‹ **Required Actions:**
146
+ - Address flagged conditions before closing
147
+ - Submit additional documentation if required
148
+ - Schedule manual review if needed
149
+
150
+ πŸ’¬ **Comments:** {comments}
151
+ """
152
+ else: # Reject
153
+ return f"""
154
+ ❌ **APPLICATION REJECTED**
155
+
156
+ πŸ“‹ **Application ID:** {app_id}
157
+ πŸ‘€ **Applicant:** {applicant}
158
+ πŸ“… **Decision Date:** {timestamp}
159
+
160
+ 🚫 **Rejection Reasons:**
161
+ {chr(10).join([f"β€’ {flag}" for flag in analysis.get('flags', ['Manual rejection'])])}
162
+
163
+ πŸ“ž **Next Steps:**
164
+ - Applicant will be contacted within 48 hours
165
+ - Adverse action notice will be sent
166
+ - Reapplication possible after addressing issues
167
+
168
+ πŸ’¬ **Comments:** {comments}
169
+ """
170
+
171
+ # Custom CSS for production-grade styling
172
+ custom_css = """
173
+ /* Global Styles */
174
+ .gradio-container {
175
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
176
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
177
+ min-height: 100vh;
178
+ }
179
+
180
+ .main {
181
+ background: rgba(255, 255, 255, 0.95) !important;
182
+ border-radius: 20px !important;
183
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1) !important;
184
+ margin: 20px !important;
185
+ padding: 0 !important;
186
+ }
187
+
188
+ /* Header Styling */
189
+ .header-section {
190
+ background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%) !important;
191
+ color: white !important;
192
+ padding: 30px !important;
193
+ border-radius: 20px 20px 0 0 !important;
194
+ margin-bottom: 0 !important;
195
+ text-align: center !important;
196
+ }
197
+
198
+ .header-section h1 {
199
+ margin: 0 !important;
200
+ font-size: 2.5rem !important;
201
+ font-weight: 700 !important;
202
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important;
203
+ }
204
+
205
+ .header-section p {
206
+ margin: 10px 0 0 0 !important;
207
+ font-size: 1.1rem !important;
208
+ opacity: 0.9 !important;
209
+ }
210
+
211
+ /* Section Styling */
212
+ .section-card {
213
+ background: white !important;
214
+ border-radius: 15px !important;
215
+ padding: 25px !important;
216
+ margin: 20px !important;
217
+ box-shadow: 0 8px 25px rgba(0,0,0,0.1) !important;
218
+ border: 1px solid rgba(0,0,0,0.05) !important;
219
+ transition: all 0.3s ease !important;
220
+ }
221
+
222
+ .section-card:hover {
223
+ transform: translateY(-2px) !important;
224
+ box-shadow: 0 12px 35px rgba(0,0,0,0.15) !important;
225
+ }
226
+
227
+ .section-title {
228
+ color: #2c3e50 !important;
229
+ font-size: 1.5rem !important;
230
+ font-weight: 600 !important;
231
+ margin-bottom: 20px !important;
232
+ padding-bottom: 10px !important;
233
+ border-bottom: 3px solid #3498db !important;
234
+ display: flex !important;
235
+ align-items: center !important;
236
+ gap: 10px !important;
237
+ }
238
+
239
+ /* Button Styling */
240
+ .btn-primary {
241
+ background: linear-gradient(45deg, #667eea, #764ba2) !important;
242
+ border: none !important;
243
+ border-radius: 10px !important;
244
+ padding: 12px 30px !important;
245
+ font-weight: 600 !important;
246
+ font-size: 1rem !important;
247
+ transition: all 0.3s ease !important;
248
+ text-transform: uppercase !important;
249
+ letter-spacing: 0.5px !important;
250
+ }
251
+
252
+ .btn-primary:hover {
253
+ transform: translateY(-2px) !important;
254
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4) !important;
255
+ }
256
+
257
+ .btn-success {
258
+ background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
259
+ border: none !important;
260
+ border-radius: 10px !important;
261
+ padding: 12px 30px !important;
262
+ font-weight: 600 !important;
263
+ }
264
+
265
+ .btn-danger {
266
+ background: linear-gradient(45deg, #ff416c, #ff4b2b) !important;
267
+ border: none !important;
268
+ border-radius: 10px !important;
269
+ padding: 12px 30px !important;
270
+ font-weight: 600 !important;
271
+ }
272
+
273
+ /* Input Styling */
274
+ input, textarea, select {
275
+ border-radius: 8px !important;
276
+ border: 2px solid #e1e8ed !important;
277
+ padding: 12px !important;
278
+ font-size: 1rem !important;
279
+ transition: all 0.3s ease !important;
280
+ }
281
+
282
+ input:focus, textarea:focus, select:focus {
283
+ border-color: #667eea !important;
284
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
285
+ outline: none !important;
286
+ }
287
+
288
+ /* Status Messages */
289
+ .status-success {
290
+ background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
291
+ color: white !important;
292
+ padding: 15px !important;
293
+ border-radius: 10px !important;
294
+ font-weight: 600 !important;
295
+ }
296
+
297
+ .status-warning {
298
+ background: linear-gradient(45deg, #f7971e, #ffd200) !important;
299
+ color: #333 !important;
300
+ padding: 15px !important;
301
+ border-radius: 10px !important;
302
+ font-weight: 600 !important;
303
+ }
304
+
305
+ .status-error {
306
+ background: linear-gradient(45deg, #ff416c, #ff4b2b) !important;
307
+ color: white !important;
308
+ padding: 15px !important;
309
+ border-radius: 10px !important;
310
+ font-weight: 600 !important;
311
+ }
312
+
313
+ /* Progress Indicators */
314
+ .step-indicator {
315
+ display: flex !important;
316
+ justify-content: space-between !important;
317
+ margin: 20px 0 !important;
318
+ position: relative !important;
319
+ }
320
+
321
+ .step {
322
+ background: #e1e8ed !important;
323
+ border-radius: 50% !important;
324
+ width: 40px !important;
325
+ height: 40px !important;
326
+ display: flex !important;
327
+ align-items: center !important;
328
+ justify-content: center !important;
329
+ font-weight: bold !important;
330
+ color: #666 !important;
331
+ z-index: 2 !important;
332
+ }
333
+
334
+ .step.active {
335
+ background: linear-gradient(45deg, #667eea, #764ba2) !important;
336
+ color: white !important;
337
+ }
338
+
339
+ .step.completed {
340
+ background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
341
+ color: white !important;
342
+ }
343
+
344
+ /* JSON Display Enhancement */
345
+ .json-display {
346
+ background: #f8f9fa !important;
347
+ border: 1px solid #e9ecef !important;
348
+ border-radius: 10px !important;
349
+ padding: 20px !important;
350
+ font-family: 'Monaco', 'Consolas', monospace !important;
351
+ max-height: 400px !important;
352
+ overflow-y: auto !important;
353
+ }
354
+
355
+ /* File Upload Styling */
356
+ .file-upload {
357
+ border: 2px dashed #667eea !important;
358
+ border-radius: 10px !important;
359
+ padding: 30px !important;
360
+ text-align: center !important;
361
+ background: rgba(102, 126, 234, 0.05) !important;
362
+ transition: all 0.3s ease !important;
363
+ }
364
+
365
+ .file-upload:hover {
366
+ background: rgba(102, 126, 234, 0.1) !important;
367
+ border-color: #5a67d8 !important;
368
+ }
369
+
370
+ /* Responsive Design */
371
+ @media (max-width: 768px) {
372
+ .main {
373
+ margin: 10px !important;
374
+ }
375
+
376
+ .section-card {
377
+ margin: 10px !important;
378
+ padding: 20px !important;
379
+ }
380
+
381
+ .header-section h1 {
382
+ font-size: 2rem !important;
383
+ }
384
+ }
385
+ """
386
 
387
  # Gradio Interface
388
+ with gr.Blocks(css=custom_css, title="Mortgage Underwriting System", theme=gr.themes.Soft()) as app:
389
+
390
+ # Header Section
391
+ with gr.Row():
392
+ with gr.Column():
393
+ gr.HTML("""
394
+ <div class="header-section">
395
+ <h1>🏠 Mortgage Underwriting Automation</h1>
396
+ <p>Intelligent Document Processing & Risk Assessment Platform</p>
397
+ </div>
398
+ """)
399
+
400
+ # Progress Indicator
401
+ with gr.Row():
402
+ with gr.Column():
403
+ gr.HTML("""
404
+ <div class="section-card">
405
+ <div class="step-indicator">
406
+ <div class="step active">1</div>
407
+ <div class="step">2</div>
408
+ <div class="step">3</div>
409
+ <div class="step">4</div>
410
+ </div>
411
+ <div style="text-align: center; margin-top: 10px; color: #666;">
412
+ <strong>Document Upload</strong> β†’ Data Analysis β†’ Conditions β†’ Final Approval
413
+ </div>
414
+ </div>
415
+ """)
416
 
417
  # Section 1: Document Upload
418
  with gr.Row():
419
  with gr.Column():
420
+ gr.HTML('<div class="section-card">')
421
+ gr.HTML('<div class="section-title">πŸ“„ Document Upload & Validation</div>')
422
+
423
+ with gr.Row():
424
+ with gr.Column(scale=2):
425
+ files_input = gr.File(
426
+ label="Required Documents",
427
+ file_count="multiple",
428
+ file_types=[".pdf", ".png", ".jpg", ".jpeg"],
429
+ elem_classes=["file-upload"]
430
+ )
431
+ gr.HTML("""
432
+ <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
433
+ <strong>πŸ“‹ Required Documents Checklist:</strong><br>
434
+ β€’ Government-issued ID<br>
435
+ β€’ Recent Pay Stubs (2-3 months)<br>
436
+ β€’ W-2 Forms (2 years)<br>
437
+ β€’ Tax Returns (2 years)<br>
438
+ β€’ Bank Statements (3 months)<br>
439
+ β€’ Purchase Offer/Contract<br>
440
+ β€’ Insurance Documentation<br>
441
+ β€’ Property Title Information
442
+ </div>
443
+ """)
444
+
445
+ with gr.Column(scale=1):
446
+ ssn_input = gr.Textbox(
447
+ label="Social Security Number",
448
+ placeholder="XXX-XX-XXXX",
449
+ type="password"
450
+ )
451
+ validate_btn = gr.Button("πŸ” Validate Documents", variant="primary", size="lg")
452
+ validate_output = gr.Textbox(
453
+ label="Validation Status",
454
+ interactive=False,
455
+ lines=2
456
+ )
457
+
458
+ gr.HTML('</div>')
459
 
460
  # Section 2: Data Extraction and Analysis
461
  with gr.Row():
462
  with gr.Column():
463
+ gr.HTML('<div class="section-card">')
464
+ gr.HTML('<div class="section-title">πŸ” Automated Analysis & Risk Assessment</div>')
465
+
466
+ with gr.Row():
467
+ with gr.Column():
468
+ extract_btn = gr.Button("⚑ Extract Data & Analyze Risk", variant="primary", size="lg")
469
+
470
+ with gr.Row():
471
+ with gr.Column():
472
+ analysis_output = gr.JSON(
473
+ label="πŸ“Š Comprehensive Analysis Results",
474
+ elem_classes=["json-display"]
475
+ )
476
+
477
+ with gr.Column():
478
+ gr.HTML("""
479
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px;">
480
+ <h3 style="margin: 0 0 15px 0;">🎯 Risk Assessment Criteria</h3>
481
+ <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px;">
482
+ <strong>πŸ“ˆ DTI Ratio:</strong> ≀ 45% (Optimal: ≀ 36%)<br>
483
+ <strong>πŸ’³ Credit Score:</strong> β‰₯ 620 (Preferred: β‰₯ 680)<br>
484
+ <strong>🏘️ LTV Ratio:</strong> ≀ 95% (Conventional: ≀ 80%)<br>
485
+ <strong>πŸ“‹ Documentation:</strong> Complete & Verified<br>
486
+ <strong>🏒 Employment:</strong> Stable Income History
487
+ </div>
488
+ </div>
489
+ """)
490
+
491
+ gr.HTML('</div>')
492
+
493
+ # Section 3: Conditional Requirements
494
+ with gr.Row():
495
+ with gr.Column():
496
+ gr.HTML('<div class="section-card">')
497
+ gr.HTML('<div class="section-title">πŸ“Ž Additional Documentation & Conditions</div>')
498
+
499
+ with gr.Row():
500
+ with gr.Column():
501
+ additional_files = gr.File(
502
+ label="Upload Additional Documents",
503
+ file_count="multiple",
504
+ file_types=[".pdf", ".png", ".jpg", ".jpeg"],
505
+ elem_classes=["file-upload"]
506
+ )
507
+ condition_comments = gr.Textbox(
508
+ label="Underwriter Comments & Instructions",
509
+ placeholder="Provide detailed comments about additional requirements...",
510
+ lines=4
511
+ )
512
+ condition_btn = gr.Button("πŸ“€ Submit Additional Documentation", variant="secondary", size="lg")
513
+
514
+ with gr.Column():
515
+ condition_output = gr.Textbox(
516
+ label="πŸ“‹ Conditions Status",
517
+ interactive=False,
518
+ lines=6
519
+ )
520
+
521
+ gr.HTML("""
522
+ <div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 8px; color: #856404;">
523
+ <strong>πŸ’‘ Common Additional Requirements:</strong><br>
524
+ β€’ Gift Letter (for down payment assistance)<br>
525
+ β€’ Employment Verification Letter<br>
526
+ β€’ Asset Verification Documents<br>
527
+ β€’ Property Appraisal Report<br>
528
+ β€’ Explanation Letters (for credit items)<br>
529
+ β€’ Homeowner's Insurance Proof
530
+ </div>
531
+ """)
532
+
533
+ gr.HTML('</div>')
534
 
535
+ # Section 4: Final Decision
536
  with gr.Row():
537
  with gr.Column():
538
+ gr.HTML('<div class="section-card">')
539
+ gr.HTML('<div class="section-title">βš–οΈ Final Underwriting Decision</div>')
540
+
541
+ with gr.Row():
542
+ with gr.Column():
543
+ approve_radio = gr.Radio(
544
+ choices=["Approve", "Reject"],
545
+ label="πŸ“‹ Underwriting Decision",
546
+ info="Select final decision based on comprehensive analysis"
547
+ )
548
+ approval_comments = gr.Textbox(
549
+ label="πŸ’¬ Decision Comments & Rationale",
550
+ placeholder="Provide detailed reasoning for the decision...",
551
+ lines=4
552
+ )
553
+ approve_btn = gr.Button("βœ… Submit Final Decision", variant="primary", size="lg")
554
+
555
+ with gr.Column():
556
+ final_output = gr.Textbox(
557
+ label="πŸ† Final Approval Status",
558
+ interactive=False,
559
+ lines=12,
560
+ elem_classes=["status-display"]
561
+ )
562
+
563
+ gr.HTML('</div>')
564
+
565
+ # Footer
566
  with gr.Row():
567
  with gr.Column():
568
+ gr.HTML("""
569
+ <div style="text-align: center; padding: 20px; color: #666; background: rgba(0,0,0,0.05); border-radius: 0 0 20px 20px;">
570
+ <p>πŸ”’ Secure β€’ πŸ€– AI-Powered β€’ ⚑ Real-time Processing</p>
571
+ <p><small>Β© 2024 Mortgage Underwriting Automation Platform | Version 2.0</small></p>
572
+ </div>
573
+ """)
574
+
575
+ # Event Handlers
576
+ validate_btn.click(
577
+ fn=lambda files: validate_documents(files)[0],
578
+ inputs=files_input,
579
+ outputs=validate_output
580
+ )
581
+
582
+ extract_btn.click(
583
+ fn=lambda files, ssn: analyze_data(extract_data(files, ssn)),
584
+ inputs=[files_input, ssn_input],
585
+ outputs=analysis_output
586
+ )
587
+
588
+ condition_btn.click(
589
+ fn=handle_conditions,
590
+ inputs=[additional_files, condition_comments],
591
+ outputs=condition_output
592
+ )
593
+
594
+ approve_btn.click(
595
+ fn=finalize_approval,
596
+ inputs=[approve_radio, approval_comments, analysis_output],
597
+ outputs=final_output
598
+ )
599
+
600
+ if __name__ == "__main__":
601
+ app.launch(
602
+ server_name="0.0.0.0",
603
+ server_port=7860,
604
+ share=False,
605
+ show_error=True,
606
+ favicon_path=None,
607
+ ssl_verify=False,
608
+ debug=True
609
+ )