Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -7,7 +7,7 @@ from huggingface_hub import hf_hub_download
|
|
7 |
st.set_page_config(
|
8 |
page_title="Loan Approval System",
|
9 |
page_icon="🏦",
|
10 |
-
layout="centered"
|
11 |
initial_sidebar_state="collapsed"
|
12 |
)
|
13 |
|
@@ -31,6 +31,8 @@ st.markdown("""
|
|
31 |
border-radius: 10px;
|
32 |
background-color: white;
|
33 |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
|
|
|
34 |
}
|
35 |
|
36 |
/* Font family - applied globally */
|
@@ -57,6 +59,12 @@ st.markdown("""
|
|
57 |
padding: 1.5rem;
|
58 |
margin-bottom: 1.5rem;
|
59 |
border-left: 4px solid var(--primary-purple);
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
61 |
|
62 |
/* Remove purple left border from the first section card */
|
@@ -85,16 +93,20 @@ st.markdown("""
|
|
85 |
.result-approved {
|
86 |
background-color: #E8F5E9;
|
87 |
border-left: 4px solid #4CAF50;
|
88 |
-
padding:
|
89 |
border-radius: 5px;
|
90 |
-
margin-top:
|
|
|
|
|
91 |
}
|
92 |
.result-rejected {
|
93 |
background-color: #FFEBEE;
|
94 |
border-left: 4px solid #F44336;
|
95 |
-
padding:
|
96 |
border-radius: 5px;
|
97 |
-
margin-top:
|
|
|
|
|
98 |
}
|
99 |
|
100 |
/* Input widgets */
|
@@ -135,19 +147,92 @@ st.markdown("""
|
|
135 |
margin-bottom: 1.5rem;
|
136 |
border-radius: 10px;
|
137 |
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
|
138 |
}
|
139 |
|
140 |
/* Footer disclaimer */
|
141 |
.footer-disclaimer {
|
142 |
text-align: center;
|
143 |
margin-top: 2rem;
|
144 |
-
padding:
|
145 |
border-top: 1px solid #EEEEEE;
|
146 |
font-size: 0.9rem;
|
147 |
color: #666666;
|
148 |
line-height: 1.5;
|
149 |
background-color: var(--light-gray);
|
150 |
border-radius: 5px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
}
|
152 |
</style>
|
153 |
""", unsafe_allow_html=True)
|
@@ -167,48 +252,76 @@ model = load_model()
|
|
167 |
if 'restart_clicked' not in st.session_state:
|
168 |
st.session_state.restart_clicked = False
|
169 |
|
|
|
|
|
|
|
|
|
|
|
170 |
# Create tabs for better organization
|
171 |
-
tab1, tab2 = st.tabs(["Loan Application", "About the System"])
|
172 |
|
173 |
with tab1:
|
174 |
# Reset all form values if restart was clicked
|
175 |
if st.session_state.restart_clicked:
|
176 |
st.session_state.restart_clicked = False # Reset flag
|
177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
# Personal Information Section
|
179 |
-
st.markdown('<div class="section-card"><h3
|
180 |
|
181 |
-
col1, col2 = st.columns(
|
182 |
with col1:
|
183 |
gender = st.selectbox("Gender", ["Male", "Female"])
|
184 |
-
|
|
|
185 |
|
186 |
with col2:
|
187 |
marital_status = st.selectbox("Marital Status", ["Married", "Not Married"])
|
188 |
-
|
|
|
|
|
|
|
|
|
|
|
189 |
|
190 |
-
self_employed = st.selectbox("Self-Employed", ["No", "Yes"])
|
191 |
st.markdown('</div>', unsafe_allow_html=True)
|
192 |
|
193 |
# Financial Details Section
|
194 |
-
st.markdown('<div class="section-card"><h3
|
195 |
|
196 |
-
col1, col2 = st.columns(
|
197 |
with col1:
|
198 |
-
applicant_income = st.number_input("Monthly Income ($)", min_value=0, value=5000
|
199 |
-
|
200 |
credit_history = st.selectbox("Credit History Status", [1, 0],
|
201 |
-
format_func=lambda x: "
|
|
|
202 |
|
203 |
with col2:
|
204 |
-
coapplicant_income = st.number_input("Co-Applicant's Income ($)", min_value=0
|
205 |
-
|
206 |
-
location = st.selectbox("Property Location", ["Urban", "Semiurban", "Rural"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
|
208 |
st.markdown('</div>', unsafe_allow_html=True)
|
209 |
|
210 |
-
# Summary section
|
211 |
-
st.markdown('<div class="section-card"><h3
|
212 |
|
213 |
total_income = applicant_income + coapplicant_income
|
214 |
|
@@ -224,20 +337,24 @@ with tab1:
|
|
224 |
monthly_payment = loan_amount * (monthly_interest * (1 + monthly_interest) ** num_payments) / \
|
225 |
((1 + monthly_interest) ** num_payments - 1)
|
226 |
|
227 |
-
# Calculate DTI for backend use only (not displayed)
|
228 |
dti = (monthly_payment / total_income) if total_income > 0 else 0
|
229 |
dti_percent = dti * 100
|
230 |
|
231 |
# Display summary metrics
|
232 |
-
col1, col2, col3 = st.columns(
|
233 |
col1.metric("Total Monthly Income", f"${total_income:,}")
|
234 |
col2.metric("Estimated Monthly Payment", f"${monthly_payment:.2f}")
|
235 |
col3.metric("Loan Term", f"{loan_term//12} years")
|
|
|
|
|
|
|
236 |
|
237 |
# Add interest rate disclaimer
|
238 |
st.markdown(f"""
|
239 |
-
<div style="font-size: 0.
|
240 |
-
|
|
|
241 |
</div>
|
242 |
""", unsafe_allow_html=True)
|
243 |
|
@@ -247,16 +364,16 @@ with tab1:
|
|
247 |
col1, col2 = st.columns([3, 1])
|
248 |
|
249 |
with col1:
|
250 |
-
predict_button = st.button("Check Loan Approval Status", use_container_width=True)
|
251 |
|
252 |
with col2:
|
253 |
-
restart_button = st.button("🔄
|
254 |
-
help="
|
255 |
|
256 |
# Handle restart button click
|
257 |
if restart_button:
|
258 |
st.session_state.restart_clicked = True
|
259 |
-
st.rerun()
|
260 |
|
261 |
def preprocess_input():
|
262 |
# Convert categorical inputs to numerical format based on encoding reference
|
@@ -286,22 +403,32 @@ with tab1:
|
|
286 |
credit_amount_interaction, income_term_ratio
|
287 |
]])
|
288 |
|
289 |
-
# Display prediction
|
290 |
if predict_button:
|
291 |
with st.spinner("Processing your application..."):
|
|
|
|
|
|
|
|
|
|
|
|
|
292 |
input_data = preprocess_input()
|
293 |
prediction = model.predict(input_data)
|
294 |
|
295 |
# Apply additional rules to override the model in certain cases (backend only)
|
296 |
manual_rejection = False
|
|
|
297 |
|
298 |
-
# Rule-based rejections that override the model
|
299 |
if total_income < 1500:
|
300 |
manual_rejection = True
|
|
|
301 |
elif dti_percent > 50:
|
302 |
manual_rejection = True
|
|
|
303 |
elif credit_history == 0 and dti_percent > 35:
|
304 |
manual_rejection = True
|
|
|
305 |
|
306 |
# Final decision combines model prediction and manual eligibility checks
|
307 |
final_approval = (prediction[0] == 1) and not manual_rejection
|
@@ -310,23 +437,55 @@ with tab1:
|
|
310 |
if final_approval:
|
311 |
st.markdown("""
|
312 |
<div class="result-approved">
|
313 |
-
<h3 style="color: #2E7D32;">✅ Loan Approved</h3>
|
314 |
-
<p>Congratulations! Based on your information, you're eligible for this loan.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
315 |
</div>
|
316 |
""", unsafe_allow_html=True)
|
317 |
else:
|
318 |
-
st.markdown("""
|
319 |
<div class="result-rejected">
|
320 |
-
<h3 style="color: #C62828;">❌ Loan Not Approved</h3>
|
321 |
-
<p>Unfortunately, based on your current information, we cannot approve your loan application.</p>
|
322 |
-
<p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
</div>
|
324 |
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
325 |
|
326 |
with tab2:
|
327 |
-
# Add custom CSS
|
328 |
st.markdown("""
|
329 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
330 |
/* Main container styling */
|
331 |
.about-container {
|
332 |
background-color: #f8f9fa;
|
@@ -484,7 +643,7 @@ with tab2:
|
|
484 |
# Link to documentation/more info
|
485 |
st.markdown(
|
486 |
'<p class="about-text">For more information about the modeling process (from loading the dataset to fine-tuning '
|
487 |
-
'the model), check here: <a href="https://github.com/ifiecas/bankloan2" target="_blank" style="color: #2563eb;">
|
488 |
unsafe_allow_html=True
|
489 |
)
|
490 |
|
@@ -528,13 +687,4 @@ with tab2:
|
|
528 |
|
529 |
st.markdown(
|
530 |
'<p class="about-text">Inspired by an assessment in BCO6008 Predictive Analytics class in Victoria University '
|
531 |
-
'(Melbourne) with Dr. Omid Ameri
|
532 |
-
unsafe_allow_html=True
|
533 |
-
)
|
534 |
-
st.markdown('</div>', unsafe_allow_html=True)
|
535 |
-
|
536 |
-
# Disclaimer footer
|
537 |
-
st.markdown("""<div class="footer-disclaimer">
|
538 |
-
<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>
|
539 |
-
<p>© 2025 SmartLoanAI - Machine Learning Showcase Project</p>
|
540 |
-
</div>""", unsafe_allow_html=True)
|
|
|
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 |
|
|
|
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 */
|
|
|
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 */
|
|
|
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 */
|
|
|
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)
|
|
|
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 |
|
|
|
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 |
|
|
|
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
|
|
|
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
|
|
|
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;
|
|
|
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 |
|
|
|
687 |
|
688 |
st.markdown(
|
689 |
'<p class="about-text">Inspired by an assessment in BCO6008 Predictive Analytics class in Victoria University '
|
690 |
+
'(Melbourne) with Dr. Omid Ameri
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|