ifiecas commited on
Commit
5fc3fc3
·
verified ·
1 Parent(s): 4edfc10

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -603
app.py CHANGED
@@ -3,244 +3,19 @@ import joblib
3
  import numpy as np
4
  from huggingface_hub import hf_hub_download
5
 
6
- # Page configuration
7
  st.set_page_config(
8
  page_title="Loan Approval System",
9
  page_icon="🏦",
10
- layout="wide", # Changed from "centered" to "wide" for better use of space
11
  initial_sidebar_state="collapsed"
12
  )
13
 
14
- # Custom CSS for styling with the specified color theme
15
- st.markdown("""
16
- <style>
17
- /* Color Theme */
18
- :root {
19
- --primary-purple: #7950F2;
20
- --primary-purple-light: #9775F3;
21
- --primary-purple-dark: #5F3DC4;
22
- --complementary-orange: #FF5E3A;
23
- --complementary-orange-light: #FF8A6C;
24
- --light-gray: #F8F9FA;
25
- --dark-gray: #343A40;
26
- }
27
-
28
- /* Main containers */
29
- .main .block-container {
30
- padding: 2rem;
31
- border-radius: 10px;
32
- background-color: white;
33
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
34
- max-width: 1200px;
35
- margin: 0 auto;
36
- }
37
-
38
- /* Font family - applied globally */
39
- * {
40
- font-family: 'Helvetica', 'Arial', sans-serif !important;
41
- }
42
-
43
- /* Specific selectors to ensure Helvetica is applied everywhere */
44
- body, .stMarkdown, p, h1, h2, h3, h4, h5, h6, .stButton, .stSelectbox, .stNumberInput,
45
- .stTextInput, div, span, .streamlit-container, .stAlert, .stText, button, input, select,
46
- textarea, .streamlit-expanderHeader, .streamlit-expanderContent {
47
- font-family: 'Helvetica', 'Arial', sans-serif !important;
48
- }
49
-
50
- /* Headers */
51
- h1, h2, h3 {
52
- color: var(--primary-purple-dark);
53
- }
54
-
55
- /* Custom cards for sections */
56
- .section-card {
57
- background-color: var(--light-gray);
58
- border-radius: 8px;
59
- padding: 1.5rem;
60
- margin-bottom: 1.5rem;
61
- border-left: 4px solid var(--primary-purple);
62
- transition: all 0.3s ease;
63
- }
64
-
65
- .section-card:hover {
66
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.08);
67
- transform: translateY(-2px);
68
- }
69
-
70
- /* Remove purple left border from the first section card */
71
- .remove-border {
72
- border-left: none !important;
73
- }
74
-
75
- /* Button styling */
76
- .stButton > button {
77
- background-color: var(--primary-purple);
78
- color: white;
79
- border: none;
80
- border-radius: 5px;
81
- padding: 0.5rem 1rem;
82
- font-weight: bold;
83
- width: 100%;
84
- transition: all 0.3s;
85
- }
86
- .stButton > button:hover {
87
- background-color: var(--primary-purple-dark);
88
- transform: translateY(-2px);
89
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
90
- }
91
-
92
- /* Result styling */
93
- .result-approved {
94
- background-color: #E8F5E9;
95
- border-left: 4px solid #4CAF50;
96
- padding: 1.5rem;
97
- border-radius: 5px;
98
- margin-top: 1.5rem;
99
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
100
- transition: all 0.3s ease;
101
- }
102
- .result-rejected {
103
- background-color: #FFEBEE;
104
- border-left: 4px solid #F44336;
105
- padding: 1.5rem;
106
- border-radius: 5px;
107
- margin-top: 1.5rem;
108
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
109
- transition: all 0.3s ease;
110
- }
111
-
112
- /* Input widgets */
113
- .stNumberInput, .stSelectbox {
114
- margin-bottom: 1rem;
115
- }
116
-
117
- /* Footer */
118
- .footer {
119
- text-align: center;
120
- margin-top: 2rem;
121
- padding-top: 1rem;
122
- border-top: 1px solid #EEEEEE;
123
- font-size: 0.8rem;
124
- color: #666666;
125
- }
126
-
127
- /* Divider */
128
- .divider {
129
- border-top: 1px solid #EEEEEE;
130
- margin: 1.5rem 0;
131
- }
132
-
133
- /* Badge */
134
- .badge {
135
- display: inline-block;
136
- background-color: var(--complementary-orange);
137
- color: white;
138
- padding: 0.25rem 0.5rem;
139
- border-radius: 4px;
140
- font-size: 0.8rem;
141
- margin-left: 0.5rem;
142
- }
143
-
144
- /* Banner image styling */
145
- .banner-image {
146
- width: 100%;
147
- margin-bottom: 1.5rem;
148
- border-radius: 10px;
149
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
150
- transition: all 0.3s ease;
151
- }
152
-
153
- .banner-image:hover {
154
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
155
- }
156
-
157
- /* Footer disclaimer */
158
- .footer-disclaimer {
159
- text-align: center;
160
- margin-top: 2rem;
161
- padding: 1.5rem;
162
- border-top: 1px solid #EEEEEE;
163
- font-size: 0.9rem;
164
- color: #666666;
165
- line-height: 1.5;
166
- background-color: var(--light-gray);
167
- border-radius: 5px;
168
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
169
- }
170
-
171
- /* Tabs styling */
172
- .stTabs [data-baseweb="tab-list"] {
173
- gap: 8px;
174
- }
175
-
176
- .stTabs [data-baseweb="tab"] {
177
- background-color: white;
178
- border-radius: 4px 4px 0 0;
179
- padding: 10px 16px;
180
- height: auto;
181
- }
182
-
183
- .stTabs [aria-selected="true"] {
184
- background-color: var(--primary-purple-light) !important;
185
- color: white !important;
186
- font-weight: bold;
187
- }
188
-
189
- /* Improved form inputs */
190
- div[data-testid="stFormSubmit"] > button {
191
- background-color: var(--primary-purple);
192
- color: white;
193
- }
194
-
195
- /* Tooltip improvements */
196
- div[data-baseweb="tooltip"] {
197
- background-color: var(--dark-gray);
198
- border-radius: 4px;
199
- padding: 8px;
200
- font-size: 14px;
201
- }
202
-
203
- /* Metrics styling */
204
- [data-testid="stMetric"] {
205
- background-color: white;
206
- border-radius: 8px;
207
- padding: 1rem;
208
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
209
- transition: all 0.2s ease;
210
- }
211
-
212
- [data-testid="stMetric"]:hover {
213
- transform: translateY(-3px);
214
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
215
- }
216
-
217
- [data-testid="stMetricLabel"] {
218
- color: var(--primary-purple-dark);
219
- }
220
-
221
- [data-testid="stMetricValue"] {
222
- font-weight: bold;
223
- font-size: 1.5rem !important;
224
- color: var(--primary-purple);
225
- }
226
-
227
- /* Animation for loading */
228
- @keyframes pulse {
229
- 0% { opacity: 0.6; }
230
- 50% { opacity: 1; }
231
- 100% { opacity: 0.6; }
232
- }
233
-
234
- .loading-pulse {
235
- animation: pulse 1.5s infinite ease-in-out;
236
- }
237
- </style>
238
- """, unsafe_allow_html=True)
239
-
240
- # App header with banner image instead of title
241
- st.markdown('<img src="https://i.postimg.cc/R0gGW9kb/ACTION-PLAN.png" class="banner-image" alt="SmartLoanAI Banner">', unsafe_allow_html=True)
242
 
243
- # Load the trained model from Hugging Face
244
  @st.cache_resource
245
  def load_model():
246
  model_path = hf_hub_download(repo_id="ifiecas/LoanApproval-DT-v1.0", filename="best_pruned_dt.pkl")
@@ -248,451 +23,279 @@ def load_model():
248
 
249
  model = load_model()
250
 
251
- # Initialize session state for restart functionality
252
- if 'restart_clicked' not in st.session_state:
253
- st.session_state.restart_clicked = False
254
 
255
- # Global disclaimer at the top
 
 
 
256
  st.markdown("""<div class="footer-disclaimer" style="margin-bottom: 20px; background-color: #fff3cd; border-left: 4px solid #ffc107;">
257
- <p><strong>Educational Project Disclaimer:</strong> This application is a prototype created to demonstrate machine learning model deployment and is not an actual financial service. The loan approval decisions are based on a trained model for educational purposes only and should not be used for real financial decisions.</p>
258
  </div>""", unsafe_allow_html=True)
259
 
260
- # Create tabs for better organization
261
- tab1, tab2 = st.tabs(["📝 Loan Application", "ℹ️ About the System"])
262
 
263
- with tab1:
264
- # Reset all form values if restart was clicked
265
- if st.session_state.restart_clicked:
266
- st.session_state.restart_clicked = False # Reset flag
267
-
268
- # Introduction text
269
  st.markdown("""
270
  <h2 style="text-align: center; color: var(--primary-purple-dark); margin-bottom: 20px;">
271
  Smart Loan Application System
272
  </h2>
273
  <p style="text-align: center; margin-bottom: 30px; font-size: 1.1rem;">
274
- Fill out the form below to check your loan eligibility. Our AI system will analyze your information and provide an instant decision.
275
  </p>
276
  """, unsafe_allow_html=True)
277
-
278
- # Personal Information Section
279
  st.markdown('<div class="section-card"><h3>👤 Personal Information</h3>', unsafe_allow_html=True)
280
-
281
  col1, col2, col3 = st.columns(3)
282
  with col1:
283
  gender = st.selectbox("Gender", ["Male", "Female"])
284
- number_of_dependents = st.number_input("Number of Dependents", min_value=0, max_value=10, value=0,
285
- help="Number of people dependent on the applicant's income")
286
-
287
  with col2:
288
  marital_status = st.selectbox("Marital Status", ["Married", "Not Married"])
289
- self_employed = st.selectbox("Self-Employed", ["No", "Yes"],
290
- help="Whether the applicant is self-employed or works for an organization")
291
-
292
  with col3:
293
- education = st.selectbox("Education Level", ["Graduate", "Under Graduate"],
294
- help="Higher education status of the applicant")
295
-
296
  st.markdown('</div>', unsafe_allow_html=True)
297
-
298
- # Financial Details Section
299
  st.markdown('<div class="section-card"><h3>💰 Financial Details</h3>', unsafe_allow_html=True)
300
-
301
  col1, col2, col3 = st.columns(3)
302
  with col1:
303
- applicant_income = st.number_input("Monthly Income ($)", min_value=0, value=5000,
304
- help="Applicant's monthly income in dollars")
305
  credit_history = st.selectbox("Credit History Status", [1, 0],
306
- format_func=lambda x: "Good Credit History (1)" if x == 1 else "Poor Credit History (0)",
307
- help="1 indicates no existing unsettled loans, 0 indicates having unsettled loans")
308
-
309
  with col2:
310
- coapplicant_income = st.number_input("Co-Applicant's Income ($)", min_value=0,
311
- help="Co-applicant's monthly income in dollars (if applicable)")
312
- location = st.selectbox("Property Location", ["Urban", "Semiurban", "Rural"],
313
- help="The location where the property is situated")
314
-
315
  with col3:
316
- loan_amount = st.number_input("Loan Amount ($)", min_value=0, value=100000,
317
- help="The amount of loan requested in dollars")
318
- loan_term = st.slider("Loan Term (months)", min_value=12, max_value=360, value=180, step=12,
319
- help="Duration of the loan in months")
320
-
321
  st.markdown('</div>', unsafe_allow_html=True)
322
-
323
- # Summary section with improved visualization
324
- st.markdown('<div class="section-card"><h3>📊 Application Summary</h3>', unsafe_allow_html=True)
325
-
326
  total_income = applicant_income + coapplicant_income
327
-
328
- # Calculate monthly payment (simplified calculation)
329
- interest_rate = 0.05 # Assuming 5% annual interest rate
330
  monthly_interest = interest_rate / 12
331
  num_payments = loan_term
332
-
333
- # Monthly payment using the loan amortization formula
334
  if monthly_interest == 0 or num_payments == 0:
335
  monthly_payment = 0
336
  else:
337
  monthly_payment = loan_amount * (monthly_interest * (1 + monthly_interest) ** num_payments) / \
338
- ((1 + monthly_interest) ** num_payments - 1)
339
-
340
- # Calculate DTI for backend use only (not displayed initially)
341
- dti = (monthly_payment / total_income) if total_income > 0 else 0
342
- dti_percent = dti * 100
343
-
344
- # Display summary metrics
345
  col1, col2, col3, col4 = st.columns(4)
346
- col1.metric("Total Monthly Income", f"${total_income:,}")
347
- col2.metric("Estimated Monthly Payment", f"${monthly_payment:.2f}")
348
- col3.metric("Loan Term", f"{loan_term//12} years")
349
- col4.metric("Debt-to-Income Ratio", f"{dti_percent:.1f}%",
350
- delta="-" if dti_percent < 36 else f"{dti_percent - 36:.1f}%",
351
- delta_color="normal" if dti_percent < 36 else "inverse")
352
-
353
- # Add interest rate disclaimer
354
- st.markdown(f"""
355
- <div style="font-size: 0.9rem; color: #666; margin-top: 10px; margin-bottom: 20px; background-color: #f8f9fa; padding: 10px; border-radius: 5px;">
356
- <strong>Note:</strong> Estimated payment based on {interest_rate*100:.1f}% annual interest rate. Actual rates may vary based on credit score and market conditions.
357
- <br>A healthy debt-to-income ratio is typically below 36%.
358
- </div>
359
- """, unsafe_allow_html=True)
360
-
361
  st.markdown('</div>', unsafe_allow_html=True)
362
-
363
- # Prediction and restart buttons
364
- col1, col2 = st.columns([3, 1])
365
-
366
- with col1:
367
- predict_button = st.button("🔍 Check Loan Approval Status", use_container_width=True)
368
-
369
- with col2:
370
- restart_button = st.button("🔄 Reset Form", use_container_width=True,
371
- help="Clear all inputs and start over")
372
-
373
- # Handle restart button click
374
- if restart_button:
375
  st.session_state.restart_clicked = True
376
  st.rerun()
377
-
378
- def preprocess_input():
379
- # Convert categorical inputs to numerical format based on encoding reference
380
  gender_num = 0 if gender == "Male" else 1
381
- marital_status_num = 0 if marital_status == "Not Married" else 1
382
- education_num = 0 if education == "Under Graduate" else 1
383
- self_employed_num = 0 if self_employed == "No" else 1
384
- credit_history_num = credit_history # Already numerical (0,1)
385
-
386
- # One-Hot Encoding for Location
387
  location_semiurban = 1 if location == "Semiurban" else 0
388
  location_urban = 1 if location == "Urban" else 0
389
-
390
- # Convert Term from months to years
391
  term_years = loan_term / 12
392
-
393
- # Compute Derived Features - use the same monthly payment calculated above
394
- debt_to_income = monthly_payment / total_income if total_income > 0 else 0
395
- credit_amount_interaction = loan_amount * credit_history_num # Interaction effect
396
- income_term_ratio = total_income / term_years if term_years > 0 else 0 # Avoid divide by zero
397
-
398
- # Return array with all 16 features
399
  return np.array([[
400
  gender_num, marital_status_num, number_of_dependents, education_num, self_employed_num,
401
  applicant_income, coapplicant_income, loan_amount, credit_history_num,
402
- total_income, debt_to_income, location_semiurban, location_urban, term_years,
403
- credit_amount_interaction, income_term_ratio
404
  ]])
405
-
406
- # Display prediction with enhanced visualization
407
- if predict_button:
408
- with st.spinner("Processing your application..."):
409
- st.markdown("""
410
- <div class="loading-pulse" style="text-align: center; margin: 20px 0;">
411
- <p style="font-size: 1.1rem;">Analyzing your application data...</p>
412
- </div>
413
- """, unsafe_allow_html=True)
414
-
415
- input_data = preprocess_input()
416
- prediction = model.predict(input_data)
417
-
418
- # Apply additional rules to override the model in certain cases (backend only)
419
- manual_rejection = False
420
- rejection_reason = ""
421
-
422
- # Rule-based rejections that override the model
423
  if total_income < 1500:
424
- manual_rejection = True
425
- rejection_reason = "Insufficient total income (minimum $1,500 required)"
426
- elif dti_percent > 50:
427
- manual_rejection = True
428
- rejection_reason = "Debt-to-income ratio too high (exceeds 50%)"
429
- elif credit_history == 0 and dti_percent > 35:
430
- manual_rejection = True
431
- rejection_reason = "Poor credit history combined with high debt-to-income ratio"
432
-
433
- # Final decision combines model prediction and manual eligibility checks
434
- final_approval = (prediction[0] == 1) and not manual_rejection
435
-
436
- # Show result with enhanced styling
437
- if final_approval:
438
  st.markdown("""
439
  <div class="result-approved">
440
- <h3 style="color: #2E7D32; margin-top: 0;">✅ Loan Approved</h3>
441
- <p style="font-size: 1.1rem;">Congratulations! Based on your information, you're eligible for this loan.</p>
442
- <p>Our AI model has determined that your application meets our criteria for approval. Here's what happens next:</p>
443
  <ol>
444
- <li>Verification of the submitted information</li>
445
- <li>Final loan terms proposal</li>
446
- <li>Document signing and disbursement</li>
447
  </ol>
448
- <p><em>In a real application, you would receive further instructions on next steps.</em></p>
449
  </div>
450
  """, unsafe_allow_html=True)
451
  else:
452
  st.markdown(f"""
453
  <div class="result-rejected">
454
- <h3 style="color: #C62828; margin-top: 0;">❌ Loan Not Approved</h3>
455
- <p style="font-size: 1.1rem;">Unfortunately, based on your current information, we cannot approve your loan application.</p>
456
- <p><strong>Potential factors affecting the decision:</strong></p>
457
- <ul>
458
- <li>{rejection_reason if rejection_reason else "The combination of factors in your application does not meet our current criteria"}</li>
459
- <li>Income to loan amount ratio may be insufficient</li>
460
- <li>Credit history concerns</li>
461
- </ul>
462
- <p><strong>Suggestions for improvement:</strong></p>
463
  <ul>
464
- <li>Consider improving your credit score</li>
465
- <li>Reduce existing debt before reapplying</li>
466
- <li>Apply with a co-applicant with higher income</li>
467
- <li>Request a lower loan amount or longer term</li>
468
  </ul>
469
  </div>
470
  """, unsafe_allow_html=True)
471
-
472
- # Add disclaimer at the bottom of tab1 as well
473
- st.markdown("""<div class="footer-disclaimer">
474
- <p><strong>Educational Project Disclaimer:</strong> This application is a prototype created to demonstrate machine learning model deployment and is not an actual financial service. The loan approval decisions are based on a trained model for educational purposes only and should not be used for real financial decisions.</p>
475
- <p>© 2025 SmartLoanAI - Machine Learning Showcase Project</p>
476
- </div>""", unsafe_allow_html=True)
477
 
478
- with tab2:
479
- # Add custom CSS to make font sizes consistent
480
- st.markdown("""
481
- <style>
482
- /* Make all text in the About tab the same size */
483
- [data-testid="stAppViewContainer"] .stTabs [aria-label="About the System"] p,
484
- [data-testid="stAppViewContainer"] .stTabs [aria-label="About the System"] li {
485
- font-size: 16px !important;
486
- line-height: 1.5 !important;
487
- }
488
-
489
- /* Main container styling */
490
- .about-container {
491
- background-color: #f8f9fa;
492
- border-radius: 10px;
493
- padding: 20px;
494
- margin-bottom: 20px;
495
- }
496
-
497
- /* Section styling */
498
- .about-section {
499
- margin-bottom: 25px;
500
- }
501
-
502
- /* Section headers */
503
- .section-header {
504
- color: #1e3a8a;
505
- font-size: 20px;
506
- font-weight: 600;
507
- margin-bottom: 10px;
508
- border-bottom: 2px solid #e5e7eb;
509
- padding-bottom: 5px;
510
- }
511
-
512
- /* Regular text */
513
- .about-text {
514
- font-size: 16px;
515
- line-height: 1.6;
516
- color: #374151;
517
- }
518
-
519
- /* Metrics card container */
520
- .metrics-container {
521
- display: flex;
522
- flex-wrap: wrap;
523
- gap: 15px;
524
- margin: 15px 0;
525
- }
526
-
527
- /* Individual metric card */
528
- .metric-card {
529
- background-color: white;
530
- border-radius: 8px;
531
- padding: 15px;
532
- min-width: 120px;
533
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
534
- flex: 1;
535
- text-align: center;
536
- }
537
-
538
- /* Metric value */
539
- .metric-value {
540
- font-size: 22px;
541
- font-weight: 600;
542
- color: #2563eb;
543
- }
544
-
545
- /* Metric label */
546
- .metric-label {
547
- font-size: 14px;
548
- color: #6b7280;
549
- margin-top: 5px;
550
- }
551
-
552
- /* Footer styling */
553
- .footer-disclaimer {
554
- background-color: #f3f4f6;
555
- border-radius: 8px;
556
- padding: 15px;
557
- margin-top: 30px;
558
- border-left: 4px solid #9ca3af;
559
- font-size: 14px;
560
- color: #4b5563;
561
- }
562
-
563
- /* Author bio section */
564
- .author-bio {
565
- display: flex;
566
- align-items: center;
567
- gap: 15px;
568
- background-color: white;
569
- border-radius: 8px;
570
- padding: 15px;
571
- margin: 20px 0;
572
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
573
- }
574
-
575
- /* Author image placeholder */
576
- .author-image {
577
- width: 60px;
578
- height: 60px;
579
- border-radius: 50%;
580
- background-color: #e5e7eb;
581
- display: flex;
582
- align-items: center;
583
- justify-content: center;
584
- color: #9ca3af;
585
- font-size: 20px;
586
- font-weight: bold;
587
- }
588
- </style>
589
- """, unsafe_allow_html=True)
590
 
591
- # Main content container
592
  st.markdown('<div class="about-container">', unsafe_allow_html=True)
593
-
594
- # System overview section
595
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
596
  st.markdown('<h2 class="section-header">About the Loan Approval System</h2>', unsafe_allow_html=True)
597
- st.markdown(
598
- '<p class="about-text">Our AI-powered system evaluates loan applications using machine learning and '
599
- 'industry-standard criteria. It analyzes your financial information, credit history, and loan requirements '
600
- 'to provide fast, objective loan decisions.</p>', unsafe_allow_html=True
601
- )
 
602
  st.markdown('</div>', unsafe_allow_html=True)
603
-
604
- # Model information section
605
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
606
- st.markdown('<h2 class="section-header">About the ML Model</h2>', unsafe_allow_html=True)
607
- st.markdown(
608
- '<p class="about-text">The machine learning model powering this system is a Decision Tree classifier, '
609
- 'which outperformed several alternatives including KNN, Random Forest, Logistic Regression, and Support '
610
- 'Vector Machine in our testing phase. The model was refined using Cost Complexity Pruning (CCP) to identify '
611
- 'the optimal alpha value, preventing overfitting while maintaining high predictive accuracy.</p>',
612
- unsafe_allow_html=True
613
- )
614
  st.markdown('</div>', unsafe_allow_html=True)
615
-
616
- # Performance metrics section with cards
617
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
618
- st.markdown('<h2 class="section-header">Model Performance Metrics</h2>', unsafe_allow_html=True)
619
-
620
- # Metrics cards using HTML for better styling
621
- st.markdown(
622
- '<div class="metrics-container">'
623
- ' <div class="metric-card">'
624
- ' <div class="metric-value">83.61%</div>'
625
- ' <div class="metric-label">Accuracy</div>'
626
- ' </div>'
627
- ' <div class="metric-card">'
628
- ' <div class="metric-value">80.77%</div>'
629
- ' <div class="metric-label">Precision</div>'
630
- ' </div>'
631
- ' <div class="metric-card">'
632
- ' <div class="metric-value">100.00%</div>'
633
- ' <div class="metric-label">Recall</div>'
634
- ' </div>'
635
- ' <div class="metric-card">'
636
- ' <div class="metric-value">89.36%</div>'
637
- ' <div class="metric-label">F1 Score</div>'
638
- ' </div>'
639
- '</div>',
640
- unsafe_allow_html=True
641
- )
642
-
643
- # Link to documentation/more info
644
- st.markdown(
645
- '<p class="about-text">For more information about the modeling process (from loading the dataset to fine-tuning '
646
- 'the model), check here: <a href="https://github.com/ifiecas/bankloan2" target="_blank" style="color: #2563eb;">https://github.com/ifiecas/bankloan2</a></p>',
647
- unsafe_allow_html=True
648
- )
649
-
650
- # YouTube video section
651
- st.markdown('<h2 class="section-header">Brief Explanation</h2>', unsafe_allow_html=True)
652
- st.markdown('<p class="about-text">Watch this video for a brief explanation of the assessment:</p>', unsafe_allow_html=True)
653
-
654
- # YouTube embed with responsive container
655
  st.markdown("""
656
- <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; margin-bottom: 20px;">
657
- <iframe
658
- style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
659
- src="https://www.youtube.com/embed/y88GidhkAE8?si=iesfB084u4qrtPB_"
660
- title="Assessment Explanation"
661
- frameborder="0"
662
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
663
- allowfullscreen>
664
- </iframe>
665
- </div>
 
 
 
 
 
 
 
 
666
  """, unsafe_allow_html=True)
667
-
668
- st.markdown('</div>', unsafe_allow_html=True)
669
-
670
- # Author section with profile
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
672
- st.markdown('<h2 class="section-header">Behind the Build</h2>', unsafe_allow_html=True)
673
-
674
- st.markdown(
675
- '<div class="author-bio">'
676
- ' <div class="author-image">IF</div>'
677
- ' <div>'
678
- ' <p style="margin: 0; font-weight: 600; color: #1f2937;">Ivy Fiecas-Borjal</p>'
679
- ' <p style="margin: 0; font-size: 14px; color: #6b7280;">Building with AI & ML | Biz Dev in Tech</p>'
680
- ' <p style="margin-top: 5px; font-size: 14px;">'
681
- ' <a href="https://ifiecas.com/" target="_blank" style="color: #2563eb; text-decoration: none;">Visit Portfolio</a>'
682
- ' </p>'
683
- ' </div>'
684
- '</div>',
685
- unsafe_allow_html=True
686
- )
687
-
688
 
 
 
 
 
 
 
 
689
 
 
 
 
 
 
 
690
 
691
- st.markdown('<p class="about-text">Inspired by an assessment in BCO6008 Predictive Analytics class in Victoria University (Melbourne) with Dr. Omid Ameri Sianaki. Enjoyed doing this and learned a lot! 😊</p>', unsafe_allow_html=True)
692
  st.markdown('</div>', unsafe_allow_html=True)
693
-
694
- # Disclaimer footer
695
- st.markdown("""<div class="footer-disclaimer">
696
- <p><strong>Educational Project Disclaimer:</strong> This application is a prototype created to demonstrate machine learning model deployment and is not an actual financial service. The loan approval decisions are based on a trained model for educational purposes only and should not be used for real financial decisions.</p>
697
- <p>© 2025 SmartLoanAI - Machine Learning Showcase Project</p>
698
- </div>""", unsafe_allow_html=True)
 
3
  import numpy as np
4
  from huggingface_hub import hf_hub_download
5
 
6
+ # Page setup
7
  st.set_page_config(
8
  page_title="Loan Approval System",
9
  page_icon="🏦",
10
+ layout="wide",
11
  initial_sidebar_state="collapsed"
12
  )
13
 
14
+ # Custom styling (retain your existing full CSS here — unchanged)
15
+ # You can paste your full <style> block here as-is
16
+ st.markdown("""<style>...your existing CSS...</style>""", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ # Load model once using caching
19
  @st.cache_resource
20
  def load_model():
21
  model_path = hf_hub_download(repo_id="ifiecas/LoanApproval-DT-v1.0", filename="best_pruned_dt.pkl")
 
23
 
24
  model = load_model()
25
 
 
 
 
26
 
27
+ # Application banner
28
+ st.markdown('<img src="https://i.postimg.cc/R0gGW9kb/ACTION-PLAN.png" class="banner-image" alt="SmartLoanAI Banner">', unsafe_allow_html=True)
29
+
30
+ # Global disclaimer
31
  st.markdown("""<div class="footer-disclaimer" style="margin-bottom: 20px; background-color: #fff3cd; border-left: 4px solid #ffc107;">
32
+ <p><strong>Educational Project Disclaimer:</strong> This tool is a demonstration of machine learning model deployment and is not a real financial service. Loan decisions shown here are based on a sample dataset and trained model, intended for educational use only.</p>
33
  </div>""", unsafe_allow_html=True)
34
 
 
 
35
 
36
+ # Use Streamlit Tabs
37
+ tab_application, tab_about = st.tabs(["📝 Loan Application", "ℹ️ About the System"])
38
+
39
+ with tab_application:
40
+ # Header
 
41
  st.markdown("""
42
  <h2 style="text-align: center; color: var(--primary-purple-dark); margin-bottom: 20px;">
43
  Smart Loan Application System
44
  </h2>
45
  <p style="text-align: center; margin-bottom: 30px; font-size: 1.1rem;">
46
+ Fill out the form below to check your loan eligibility. Our system will evaluate your information instantly.
47
  </p>
48
  """, unsafe_allow_html=True)
49
+
50
+ # Input Sections
51
  st.markdown('<div class="section-card"><h3>👤 Personal Information</h3>', unsafe_allow_html=True)
 
52
  col1, col2, col3 = st.columns(3)
53
  with col1:
54
  gender = st.selectbox("Gender", ["Male", "Female"])
55
+ number_of_dependents = st.number_input("Number of Dependents", min_value=0, max_value=10, value=0)
56
+
 
57
  with col2:
58
  marital_status = st.selectbox("Marital Status", ["Married", "Not Married"])
59
+ self_employed = st.selectbox("Self-Employed", ["No", "Yes"])
60
+
 
61
  with col3:
62
+ education = st.selectbox("Education Level", ["Graduate", "Under Graduate"])
 
 
63
  st.markdown('</div>', unsafe_allow_html=True)
64
+
 
65
  st.markdown('<div class="section-card"><h3>💰 Financial Details</h3>', unsafe_allow_html=True)
 
66
  col1, col2, col3 = st.columns(3)
67
  with col1:
68
+ applicant_income = st.number_input("Monthly Income ($)", min_value=0, value=5000)
 
69
  credit_history = st.selectbox("Credit History Status", [1, 0],
70
+ format_func=lambda x: "Good Credit History (1)" if x == 1 else "Poor Credit History (0)")
71
+
 
72
  with col2:
73
+ coapplicant_income = st.number_input("Co-Applicant's Income ($)", min_value=0)
74
+ location = st.selectbox("Property Location", ["Urban", "Semiurban", "Rural"])
75
+
 
 
76
  with col3:
77
+ loan_amount = st.number_input("Loan Amount ($)", min_value=0, value=100000)
78
+ loan_term = st.slider("Loan Term (months)", min_value=12, max_value=360, value=180, step=12)
 
 
 
79
  st.markdown('</div>', unsafe_allow_html=True)
80
+
81
+ # Summary Section
 
 
82
  total_income = applicant_income + coapplicant_income
83
+ interest_rate = 0.05
 
 
84
  monthly_interest = interest_rate / 12
85
  num_payments = loan_term
86
+
 
87
  if monthly_interest == 0 or num_payments == 0:
88
  monthly_payment = 0
89
  else:
90
  monthly_payment = loan_amount * (monthly_interest * (1 + monthly_interest) ** num_payments) / \
91
+ ((1 + monthly_interest) ** num_payments - 1)
92
+
93
+ debt_to_income_ratio = (monthly_payment / total_income) if total_income > 0 else 0
94
+ debt_to_income_percent = debt_to_income_ratio * 100
95
+
96
+ # Metrics
97
+ st.markdown('<div class="section-card"><h3>📊 Application Summary</h3>', unsafe_allow_html=True)
98
  col1, col2, col3, col4 = st.columns(4)
99
+ col1.metric("Total Monthly Income", f"${total_income:,.2f}")
100
+ col2.metric("Estimated Monthly Payment", f"${monthly_payment:,.2f}")
101
+ col3.metric("Loan Term", f"{loan_term // 12} years")
102
+ col4.metric("Debt-to-Income Ratio", f"{debt_to_income_percent:.1f}%",
103
+ delta=f"{debt_to_income_percent - 36:.1f}%" if debt_to_income_percent > 36 else "-",
104
+ delta_color="normal" if debt_to_income_percent < 36 else "inverse")
105
+
106
+ st.caption("Note: Estimated payment assumes a 5.0% annual interest rate. A healthy debt-to-income ratio is usually under 36%.")
 
 
 
 
 
 
 
107
  st.markdown('</div>', unsafe_allow_html=True)
108
+
109
+
110
+ # Action Buttons
111
+ col_main, col_reset = st.columns([3, 1])
112
+ with col_main:
113
+ check_button = st.button("🔍 Check Loan Approval Status", use_container_width=True)
114
+ with col_reset:
115
+ reset_button = st.button("🔄 Reset Form", use_container_width=True, help="Clear the form and start over")
116
+
117
+ if reset_button:
 
 
 
118
  st.session_state.restart_clicked = True
119
  st.rerun()
120
+
121
+ # Function to preprocess inputs
122
+ def prepare_input():
123
  gender_num = 0 if gender == "Male" else 1
124
+ marital_status_num = 1 if marital_status == "Married" else 0
125
+ education_num = 1 if education == "Graduate" else 0
126
+ self_employed_num = 1 if self_employed == "Yes" else 0
127
+ credit_history_num = credit_history
 
 
128
  location_semiurban = 1 if location == "Semiurban" else 0
129
  location_urban = 1 if location == "Urban" else 0
 
 
130
  term_years = loan_term / 12
131
+ credit_interaction = loan_amount * credit_history_num
132
+ income_term_ratio = total_income / term_years if term_years > 0 else 0
133
+
 
 
 
 
134
  return np.array([[
135
  gender_num, marital_status_num, number_of_dependents, education_num, self_employed_num,
136
  applicant_income, coapplicant_income, loan_amount, credit_history_num,
137
+ total_income, debt_to_income_ratio, location_semiurban, location_urban, term_years,
138
+ credit_interaction, income_term_ratio
139
  ]])
140
+
141
+ # Make prediction and display results
142
+ if check_button:
143
+ st.toast(" Prediction complete! Scroll down to view your result.")
144
+ with st.spinner("Analyzing your application..."):
145
+ features = prepare_input()
146
+ model_result = model.predict(features)[0]
147
+
148
+ # Manual override rules
149
+ manually_rejected = False
150
+ reason = ""
 
 
 
 
 
 
 
151
  if total_income < 1500:
152
+ manually_rejected = True
153
+ reason = "Total income is below the required minimum of $1,500."
154
+ elif debt_to_income_percent > 50:
155
+ manually_rejected = True
156
+ reason = "Debt-to-income ratio is too high (above 50%)."
157
+ elif credit_history == 0 and debt_to_income_percent > 35:
158
+ manually_rejected = True
159
+ reason = "Poor credit history and high debt-to-income ratio."
160
+
161
+ approved = (model_result == 1) and not manually_rejected
162
+
163
+ # Display outcome
164
+ if approved:
 
165
  st.markdown("""
166
  <div class="result-approved">
167
+ <h3 style="color: #2E7D32;">✅ Loan Approved</h3>
168
+ <p>Congratulations! Based on your information, you meet the eligibility criteria.</p>
 
169
  <ol>
170
+ <li>Verification of documents</li>
171
+ <li>Loan term negotiation</li>
172
+ <li>Disbursement of funds</li>
173
  </ol>
174
+ <p><em>This result is part of an educational simulation, not a real financial offer.</em></p>
175
  </div>
176
  """, unsafe_allow_html=True)
177
  else:
178
  st.markdown(f"""
179
  <div class="result-rejected">
180
+ <h3 style="color: #C62828;">❌ Loan Not Approved</h3>
181
+ <p>Unfortunately, we cannot approve your application at this time.</p>
182
+ <p><strong>Reason:</strong> {reason if reason else "Application does not meet current criteria."}</p>
183
+ <p><strong>Suggestions:</strong></p>
 
 
 
 
 
184
  <ul>
185
+ <li>Improve your credit history</li>
186
+ <li>Lower your requested loan amount</li>
187
+ <li>Add a co-applicant with income</li>
188
+ <li>Reduce existing debt</li>
189
  </ul>
190
  </div>
191
  """, unsafe_allow_html=True)
 
 
 
 
 
 
192
 
193
+
194
+ with tab_about:
195
+ # Custom styling (already applied globally)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ # Container start
198
  st.markdown('<div class="about-container">', unsafe_allow_html=True)
199
+
200
+ # System Overview
201
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
202
  st.markdown('<h2 class="section-header">About the Loan Approval System</h2>', unsafe_allow_html=True)
203
+ st.markdown("""
204
+ <p class="about-text">
205
+ This prototype demonstrates a machine learning model deployed as an interactive loan approval simulation.
206
+ It evaluates user inputs — such as income, credit history, and loan amount — to assess eligibility.
207
+ </p>
208
+ """, unsafe_allow_html=True)
209
  st.markdown('</div>', unsafe_allow_html=True)
210
+
211
+ # Model Info
212
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
213
+ st.markdown('<h2 class="section-header">Machine Learning Model Details</h2>', unsafe_allow_html=True)
214
+ st.markdown("""
215
+ <p class="about-text">
216
+ The system uses a Decision Tree classifier. After evaluating multiple models (Logistic Regression,
217
+ Random Forest, Support Vector Machine), the Decision Tree performed best with the selected features.
218
+ Cost Complexity Pruning was used to fine-tune and avoid overfitting.
219
+ </p>
220
+ """, unsafe_allow_html=True)
221
  st.markdown('</div>', unsafe_allow_html=True)
222
+
223
+ # Performance Metrics
224
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
225
+ st.markdown('<h2 class="section-header">Model Performance</h2>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  st.markdown("""
227
+ <div class="metrics-container">
228
+ <div class="metric-card">
229
+ <div class="metric-value">83.61%</div>
230
+ <div class="metric-label">Accuracy</div>
231
+ </div>
232
+ <div class="metric-card">
233
+ <div class="metric-value">80.77%</div>
234
+ <div class="metric-label">Precision</div>
235
+ </div>
236
+ <div class="metric-card">
237
+ <div class="metric-value">100.00%</div>
238
+ <div class="metric-label">Recall</div>
239
+ </div>
240
+ <div class="metric-card">
241
+ <div class="metric-value">89.36%</div>
242
+ <div class="metric-label">F1 Score</div>
243
+ </div>
244
+ </div>
245
  """, unsafe_allow_html=True)
246
+
247
+ # Resource Link
248
+ st.markdown("""
249
+ <p class="about-text">
250
+ View the full modeling process, including feature engineering and model comparisons, at
251
+ <a href="https://github.com/ifiecas/bankloan2" target="_blank" style="color: #2563eb;">this GitHub repository</a>.
252
+ </p>
253
+ """, unsafe_allow_html=True)
254
+
255
+ # Video Embed
256
+ st.markdown('<h2 class="section-header">Video Overview</h2>', unsafe_allow_html=True)
257
+ st.markdown("""
258
+ <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
259
+ <iframe
260
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
261
+ src="https://www.youtube.com/embed/y88GidhkAE8?si=iesfB084u4qrtPB_"
262
+ title="Assessment Explanation"
263
+ frameborder="0"
264
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
265
+ allowfullscreen>
266
+ </iframe>
267
+ </div>
268
+ """, unsafe_allow_html=True)
269
+
270
+ # Author Credit
271
  st.markdown('<div class="about-section">', unsafe_allow_html=True)
272
+ st.markdown('<h2 class="section-header">About the Creator</h2>', unsafe_allow_html=True)
273
+ st.markdown("""
274
+ <div class="author-bio">
275
+ <div class="author-image">IF</div>
276
+ <div>
277
+ <p style="margin: 0; font-weight: 600; color: #1f2937;">Ivy Fiecas-Borjal</p>
278
+ <p style="margin: 0; font-size: 14px; color: #6b7280;">Builder of AI/ML Experiences | Business Dev in Tech</p>
279
+ <p style="margin-top: 5px; font-size: 14px;">
280
+ <a href="https://ifiecas.com/" target="_blank" style="color: #2563eb;">Visit Portfolio</a>
281
+ </p>
282
+ </div>
283
+ </div>
284
+ """, unsafe_allow_html=True)
 
 
 
285
 
286
+ # Personal note
287
+ st.markdown("""
288
+ <p class="about-text">
289
+ This app was created as part of a predictive analytics class at Victoria University (Melbourne) under Dr. Omid Ameri Sianaki.
290
+ It was a fun and insightful learning experience!
291
+ </p>
292
+ """, unsafe_allow_html=True)
293
 
294
+ # Final disclaimer
295
+ st.markdown("""<div class="footer-disclaimer">
296
+ <p><strong>Educational Project Disclaimer:</strong> This application is a prototype created for demonstration purposes only.
297
+ It is not affiliated with any real bank or financial institution.</p>
298
+ <p>© 2025 SmartLoanAI – A Machine Learning Showcase Project</p>
299
+ </div>""", unsafe_allow_html=True)
300
 
 
301
  st.markdown('</div>', unsafe_allow_html=True)