Kousik-1504 commited on
Commit ·
e80d1c2
0
Parent(s):
Version 1.0 of the application has been developed
Browse files- .gitignore +2 -0
- Dockerfile +7 -0
- app.py +146 -0
- models/ohe.pkl +0 -0
- models/scaler.pkl +0 -0
- models/xgboost_model.pkl +0 -0
- notebooks/analysis.ipynb +0 -0
- notebooks/feature_transformed_telecom.csv +0 -0
- notebooks/model_training.ipynb +0 -0
- notebooks/telecom_customer_churn.csv +0 -0
- requirements.ipynb +71 -0
- requirements.txt +6 -0
- templates/documentation.html +613 -0
- templates/documentation2.html +613 -0
- templates/home.html +510 -0
- templates/home2.html +510 -0
- templates/predict.html +855 -0
- templates/predict2.html +856 -0
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
Dockerfile
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.13.9
|
| 2 |
+
WORKDIR /app
|
| 3 |
+
COPY requirements.txt .
|
| 4 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 5 |
+
COPY . .
|
| 6 |
+
EXPOSE 7860
|
| 7 |
+
CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
|
app.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, render_template, jsonify
|
| 2 |
+
import joblib
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
app = Flask(__name__)
|
| 6 |
+
|
| 7 |
+
# Load pre-trained pipeline and XGBoost model at startup
|
| 8 |
+
try:
|
| 9 |
+
ohe = joblib.load('models/ohe.pkl')
|
| 10 |
+
scaler = joblib.load('models/scaler.pkl')
|
| 11 |
+
model = joblib.load('models/xgboost_model.pkl')
|
| 12 |
+
expected_features = list(model.feature_names_in_)
|
| 13 |
+
print("Models and pipelines loaded successfully.")
|
| 14 |
+
except Exception as e:
|
| 15 |
+
print(f"Error loading models or encoder: {e}")
|
| 16 |
+
ohe, scaler, model, expected_features = None, None, None, []
|
| 17 |
+
|
| 18 |
+
@app.route('/')
|
| 19 |
+
def home():
|
| 20 |
+
return render_template('home2.html')
|
| 21 |
+
|
| 22 |
+
@app.route('/documentation')
|
| 23 |
+
def documentation():
|
| 24 |
+
return render_template('documentation2.html')
|
| 25 |
+
|
| 26 |
+
@app.route('/predict_model', methods=['GET', 'POST'])
|
| 27 |
+
def predict_model():
|
| 28 |
+
if request.method == 'POST':
|
| 29 |
+
try:
|
| 30 |
+
# 1. Extract raw inputs from form
|
| 31 |
+
gender = int(request.form.get('gender', 0))
|
| 32 |
+
senior = int(request.form.get('SeniorCitizen', 0))
|
| 33 |
+
partner_str = request.form.get('Partner', 'No')
|
| 34 |
+
dependents_str = request.form.get('Dependents', 'No')
|
| 35 |
+
|
| 36 |
+
phone_str = request.form.get('PhoneService', 'No')
|
| 37 |
+
multiple_str = request.form.get('MultipleLines', 'No')
|
| 38 |
+
internet_str = request.form.get('InternetService', 'No')
|
| 39 |
+
|
| 40 |
+
security_str = request.form.get('OnlineSecurity', 'No')
|
| 41 |
+
backup_str = request.form.get('OnlineBackup', 'No')
|
| 42 |
+
protection_str = request.form.get('DeviceProtection', 'No')
|
| 43 |
+
tech_str = request.form.get('TechSupport', 'No')
|
| 44 |
+
tv_str = request.form.get('StreamingTV', 'No')
|
| 45 |
+
movies_str = request.form.get('StreamingMovies', 'No')
|
| 46 |
+
|
| 47 |
+
contract = request.form.get('Contract', 'Month-to-month')
|
| 48 |
+
paperless = request.form.get('PaperlessBilling', 'No')
|
| 49 |
+
payment = request.form.get('PaymentMethod', 'Electronic check')
|
| 50 |
+
|
| 51 |
+
tenure = int(request.form.get('tenure', 0))
|
| 52 |
+
monthly = float(request.form.get('MonthlyCharges', 0.0))
|
| 53 |
+
total = float(request.form.get('TotalCharges', 0.0))
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
map_addon = lambda val: 1 if val == 'Yes' else (-1 if val == 'No' else 0)
|
| 57 |
+
|
| 58 |
+
security = map_addon(security_str)
|
| 59 |
+
backup = map_addon(backup_str)
|
| 60 |
+
protection = map_addon(protection_str)
|
| 61 |
+
tech = map_addon(tech_str)
|
| 62 |
+
tv = map_addon(tv_str)
|
| 63 |
+
movies = map_addon(movies_str)
|
| 64 |
+
|
| 65 |
+
has_partner = 1 if partner_str == 'Yes' else 0
|
| 66 |
+
has_dependents = 1 if dependents_str == 'Yes' else 0
|
| 67 |
+
has_phoneservice = 1 if phone_str == 'Yes' else 0
|
| 68 |
+
has_multiplelines = 1 if multiple_str == 'Yes' else 0
|
| 69 |
+
|
| 70 |
+
internet_map = {'No': 0, 'DSL': 1, 'Fiber optic': 2}
|
| 71 |
+
internet_encoded = internet_map.get(internet_str, 0)
|
| 72 |
+
|
| 73 |
+
is_automatic = 1 if 'automatic' in payment else 0
|
| 74 |
+
|
| 75 |
+
addons_list = [security_str, backup_str, protection_str, tech_str, tv_str, movies_str]
|
| 76 |
+
product_count = sum(1 for add in addons_list if add == 'Yes')
|
| 77 |
+
|
| 78 |
+
high_risk = 1 if product_count in [1, 2, 3] else 0
|
| 79 |
+
fully_integrated = 1 if product_count >= 5 else 0
|
| 80 |
+
mod_security = 1 if security_str == 'Yes' else 0
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
user_data = {
|
| 84 |
+
'gender': gender,
|
| 85 |
+
'SeniorCitizen': senior,
|
| 86 |
+
'tenure': tenure,
|
| 87 |
+
'OnlineSecurity': security,
|
| 88 |
+
'OnlineBackup': backup,
|
| 89 |
+
'DeviceProtection': protection,
|
| 90 |
+
'TechSupport': tech,
|
| 91 |
+
'StreamingTV': tv,
|
| 92 |
+
'StreamingMovies': movies,
|
| 93 |
+
'Contract': contract,
|
| 94 |
+
'PaperlessBilling': paperless,
|
| 95 |
+
'PaymentMethod': payment,
|
| 96 |
+
'MonthlyCharges': monthly,
|
| 97 |
+
'TotalCharges': total,
|
| 98 |
+
'has_partner': has_partner,
|
| 99 |
+
'has_dependents': has_dependents,
|
| 100 |
+
'has_phoneservice': has_phoneservice,
|
| 101 |
+
'has_multiplelines': has_multiplelines,
|
| 102 |
+
'internet_service_encoded': internet_encoded,
|
| 103 |
+
'is_automatic': is_automatic,
|
| 104 |
+
'Product_Count': product_count,
|
| 105 |
+
'Is_High_Risk_Integration': high_risk,
|
| 106 |
+
'Is_Fully_Integrated': fully_integrated,
|
| 107 |
+
'Mod_Security': mod_security
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
df_raw = pd.DataFrame([user_data])
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
categorical_cols = ['Contract', 'PaperlessBilling', 'PaymentMethod']
|
| 114 |
+
encoded_data = ohe.transform(df_raw[categorical_cols]).toarray()
|
| 115 |
+
encoded_df = pd.DataFrame(encoded_data, columns=ohe.get_feature_names_out(categorical_cols))
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
df_processed = pd.concat([df_raw.drop(columns=categorical_cols), encoded_df], axis=1)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
numerical_cols = ['MonthlyCharges', 'TotalCharges']
|
| 122 |
+
df_processed[numerical_cols] = scaler.transform(df_processed[numerical_cols])
|
| 123 |
+
|
| 124 |
+
df_final = df_processed[expected_features]
|
| 125 |
+
|
| 126 |
+
churn_prob = float(model.predict_proba(df_final)[0][1])
|
| 127 |
+
prediction = int(model.predict(df_final)[0])
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
return jsonify({
|
| 131 |
+
'success': True,
|
| 132 |
+
'prediction': prediction,
|
| 133 |
+
'churn_prob': churn_prob
|
| 134 |
+
})
|
| 135 |
+
|
| 136 |
+
except Exception as ex:
|
| 137 |
+
print(f"Error during prediction: {ex}")
|
| 138 |
+
return jsonify({
|
| 139 |
+
'success': False,
|
| 140 |
+
'error': str(ex)
|
| 141 |
+
}), 400
|
| 142 |
+
|
| 143 |
+
return render_template('predict2.html')
|
| 144 |
+
|
| 145 |
+
if __name__ == "__main__":
|
| 146 |
+
app.run(port=7860, host='0.0.0.0')
|
models/ohe.pkl
ADDED
|
Binary file (868 Bytes). View file
|
|
|
models/scaler.pkl
ADDED
|
Binary file (583 Bytes). View file
|
|
|
models/xgboost_model.pkl
ADDED
|
Binary file (63 kB). View file
|
|
|
notebooks/analysis.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
notebooks/feature_transformed_telecom.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
notebooks/model_training.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
notebooks/telecom_customer_churn.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
requirements.ipynb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 7,
|
| 6 |
+
"id": "f72144fd",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [
|
| 9 |
+
{
|
| 10 |
+
"name": "stdout",
|
| 11 |
+
"output_type": "stream",
|
| 12 |
+
"text": [
|
| 13 |
+
"Flask==3.1.3\n",
|
| 14 |
+
"pandas==3.0.2\n",
|
| 15 |
+
"xgboost==3.2.0\n",
|
| 16 |
+
"joblib==1.5.3\n",
|
| 17 |
+
"scikit-learn==1.8.0\n",
|
| 18 |
+
"gunicorn==23.0.0\n"
|
| 19 |
+
]
|
| 20 |
+
}
|
| 21 |
+
],
|
| 22 |
+
"source": [
|
| 23 |
+
"import importlib.metadata\n",
|
| 24 |
+
"\n",
|
| 25 |
+
"name = ['flask', 'pandas', 'xgboost', 'joblib', 'scikit-learn']\n",
|
| 26 |
+
"\n",
|
| 27 |
+
"for p_name in name:\n",
|
| 28 |
+
" try:\n",
|
| 29 |
+
" version = importlib.metadata.version(p_name)\n",
|
| 30 |
+
" if p_name == 'flask':\n",
|
| 31 |
+
" p_name = 'Flask'\n",
|
| 32 |
+
" elif p_name == 'scikit-learn':\n",
|
| 33 |
+
" p_name = 'scikit-learn'\n",
|
| 34 |
+
" print(f\"{p_name}=={version}\")\n",
|
| 35 |
+
" except importlib.metadata.PackageNotFoundError:\n",
|
| 36 |
+
" pass\n",
|
| 37 |
+
"\n",
|
| 38 |
+
"print(\"gunicorn==23.0.0\")"
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"cell_type": "code",
|
| 43 |
+
"execution_count": null,
|
| 44 |
+
"id": "d0798960",
|
| 45 |
+
"metadata": {},
|
| 46 |
+
"outputs": [],
|
| 47 |
+
"source": []
|
| 48 |
+
}
|
| 49 |
+
],
|
| 50 |
+
"metadata": {
|
| 51 |
+
"kernelspec": {
|
| 52 |
+
"display_name": "ml (3.13.9)",
|
| 53 |
+
"language": "python",
|
| 54 |
+
"name": "python3"
|
| 55 |
+
},
|
| 56 |
+
"language_info": {
|
| 57 |
+
"codemirror_mode": {
|
| 58 |
+
"name": "ipython",
|
| 59 |
+
"version": 3
|
| 60 |
+
},
|
| 61 |
+
"file_extension": ".py",
|
| 62 |
+
"mimetype": "text/x-python",
|
| 63 |
+
"name": "python",
|
| 64 |
+
"nbconvert_exporter": "python",
|
| 65 |
+
"pygments_lexer": "ipython3",
|
| 66 |
+
"version": "3.13.9"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"nbformat": 4,
|
| 70 |
+
"nbformat_minor": 5
|
| 71 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask==3.1.3
|
| 2 |
+
pandas==3.0.2
|
| 3 |
+
xgboost==3.2.0
|
| 4 |
+
joblib==1.5.3
|
| 5 |
+
scikit-learn==1.8.0
|
| 6 |
+
gunicorn==23.0.0
|
templates/documentation.html
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Analyzer | Documentation</title>
|
| 7 |
+
|
| 8 |
+
<!-- Google Font -->
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 10 |
+
<!-- Font Awesome Icons -->
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<style>
|
| 14 |
+
/* CSS Variable Theme mirroring predict.html */
|
| 15 |
+
:root {
|
| 16 |
+
--bg-color: #f8fafc; /* Slate 50 - Light, clean background */
|
| 17 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 18 |
+
--text-main: #0f172a; /* Slate 900 - Deep, readable text */
|
| 19 |
+
--text-muted: #64748b; /* Slate 500 - For labels and placeholders */
|
| 20 |
+
--border-color: #cbd5e1; /* Slate 300 - Subtle input borders */
|
| 21 |
+
--primary-accent: #2563eb; /* Blue 600 - Focus state & primary buttons */
|
| 22 |
+
--primary-hover: #1d4ed8; /* Blue 700 */
|
| 23 |
+
--font-family: 'Inter', sans-serif;
|
| 24 |
+
--card-shadow: 0 15px 40px rgba(15, 23, 42, 0.06);
|
| 25 |
+
--card-border-radius: 28px;
|
| 26 |
+
--inner-border-radius: 18px;
|
| 27 |
+
--gradient-start: #1e3a8a;
|
| 28 |
+
--gradient-end: #3b82f6;
|
| 29 |
+
|
| 30 |
+
--success: #10b981;
|
| 31 |
+
--warning: #f59e0b;
|
| 32 |
+
--danger: #ef4444;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* Dark Mode variables */
|
| 36 |
+
[data-theme="dark"] {
|
| 37 |
+
--bg-color: #0f172a;
|
| 38 |
+
--card-bg: #1e293b;
|
| 39 |
+
--text-main: #f8fafc;
|
| 40 |
+
--text-muted: #94a3b8;
|
| 41 |
+
--border-color: #334155;
|
| 42 |
+
--primary-accent: #3b82f6;
|
| 43 |
+
--primary-hover: #60a5fa;
|
| 44 |
+
--card-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
|
| 45 |
+
--gradient-start: #0f172a;
|
| 46 |
+
--gradient-end: #1e3a8a;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
* {
|
| 50 |
+
margin: 0;
|
| 51 |
+
padding: 0;
|
| 52 |
+
box-sizing: border-box;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
body {
|
| 56 |
+
font-family: var(--font-family);
|
| 57 |
+
background: linear-gradient(to bottom right, var(--bg-color), var(--bg-color));
|
| 58 |
+
min-height: 100vh;
|
| 59 |
+
padding: 40px 20px;
|
| 60 |
+
color: var(--text-main);
|
| 61 |
+
transition: background 0.3s, color 0.3s;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.container {
|
| 65 |
+
max-width: 1250px;
|
| 66 |
+
margin: auto;
|
| 67 |
+
position: relative;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/* Dark Mode Switch Button */
|
| 71 |
+
.theme-toggle-btn {
|
| 72 |
+
position: absolute;
|
| 73 |
+
top: 20px;
|
| 74 |
+
right: 20px;
|
| 75 |
+
background: rgba(37, 99, 235, 0.08);
|
| 76 |
+
border: 1px solid var(--border-color);
|
| 77 |
+
color: var(--primary-accent);
|
| 78 |
+
padding: 8px 12px;
|
| 79 |
+
border-radius: 20px;
|
| 80 |
+
cursor: pointer;
|
| 81 |
+
font-size: 0.85rem;
|
| 82 |
+
font-weight: 600;
|
| 83 |
+
display: flex;
|
| 84 |
+
align-items: center;
|
| 85 |
+
gap: 6px;
|
| 86 |
+
transition: all 0.2s;
|
| 87 |
+
z-index: 100;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.theme-toggle-btn:hover {
|
| 91 |
+
background: var(--primary-accent);
|
| 92 |
+
color: #ffffff;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/* HERO SECTION */
|
| 96 |
+
.hero-section {
|
| 97 |
+
text-align: center;
|
| 98 |
+
margin-bottom: 50px;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.hero-badge {
|
| 102 |
+
display: inline-block;
|
| 103 |
+
padding: 10px 18px;
|
| 104 |
+
border-radius: 999px;
|
| 105 |
+
background: rgba(37, 99, 235, 0.08);
|
| 106 |
+
color: var(--primary-accent);
|
| 107 |
+
font-size: 0.85rem;
|
| 108 |
+
font-weight: 700;
|
| 109 |
+
letter-spacing: 0.4px;
|
| 110 |
+
margin-bottom: 22px;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.hero-section h1 {
|
| 114 |
+
font-size: 3rem;
|
| 115 |
+
font-weight: 800;
|
| 116 |
+
color: var(--text-main);
|
| 117 |
+
margin-bottom: 18px;
|
| 118 |
+
letter-spacing: -0.02em;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.hero-section p {
|
| 122 |
+
max-width: 850px;
|
| 123 |
+
margin: auto;
|
| 124 |
+
color: var(--text-muted);
|
| 125 |
+
line-height: 1.9;
|
| 126 |
+
font-size: 1.05rem;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/* MAIN GRID */
|
| 130 |
+
.documentation-grid {
|
| 131 |
+
display: grid;
|
| 132 |
+
grid-template-columns: 1.2fr 0.8fr;
|
| 133 |
+
gap: 28px;
|
| 134 |
+
align-items: start;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/* GENERAL CARD */
|
| 138 |
+
.card {
|
| 139 |
+
background: var(--card-bg);
|
| 140 |
+
backdrop-filter: blur(14px);
|
| 141 |
+
border-radius: var(--card-border-radius);
|
| 142 |
+
padding: 34px;
|
| 143 |
+
border: 1px solid var(--border-color);
|
| 144 |
+
box-shadow: var(--card-shadow);
|
| 145 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* LARGE CARD */
|
| 149 |
+
.large-card {
|
| 150 |
+
min-height: 100%;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/* STICKY COLUMN */
|
| 154 |
+
.sticky-column {
|
| 155 |
+
position: sticky;
|
| 156 |
+
top: 30px;
|
| 157 |
+
display: flex;
|
| 158 |
+
flex-direction: column;
|
| 159 |
+
gap: 24px;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/* SMALL CARD */
|
| 163 |
+
.small-card {
|
| 164 |
+
padding: 28px;
|
| 165 |
+
transition: transform 0.25s ease, background-color 0.3s, border-color 0.3s;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.small-card:hover {
|
| 169 |
+
transform: translateY(-2px);
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
/* CARD TITLE */
|
| 173 |
+
.card-title {
|
| 174 |
+
font-size: 1.45rem;
|
| 175 |
+
font-weight: 700;
|
| 176 |
+
color: var(--text-main);
|
| 177 |
+
margin-bottom: 22px;
|
| 178 |
+
display: flex;
|
| 179 |
+
align-items: center;
|
| 180 |
+
gap: 8px;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
/* PARAGRAPH */
|
| 184 |
+
.card p {
|
| 185 |
+
color: var(--text-muted);
|
| 186 |
+
line-height: 1.9;
|
| 187 |
+
font-size: 0.97rem;
|
| 188 |
+
margin-bottom: 18px;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
/* FEATURE LIST */
|
| 192 |
+
.feature-list {
|
| 193 |
+
display: flex;
|
| 194 |
+
flex-direction: column;
|
| 195 |
+
gap: 18px;
|
| 196 |
+
margin-top: 22px;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.feature-item {
|
| 200 |
+
background: var(--bg-color);
|
| 201 |
+
border: 1px solid var(--border-color);
|
| 202 |
+
border-radius: var(--inner-border-radius);
|
| 203 |
+
padding: 18px 20px;
|
| 204 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.feature-item strong {
|
| 208 |
+
display: block;
|
| 209 |
+
color: var(--text-main);
|
| 210 |
+
margin-bottom: 7px;
|
| 211 |
+
font-size: 0.96rem;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.feature-item span {
|
| 215 |
+
color: var(--text-muted);
|
| 216 |
+
line-height: 1.7;
|
| 217 |
+
font-size: 0.92rem;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
/* HIGHLIGHT BOX */
|
| 221 |
+
.highlight-box {
|
| 222 |
+
background: linear-gradient(135deg, rgba(37, 99, 235, 0.05), var(--bg-color));
|
| 223 |
+
border: 1px solid var(--border-color);
|
| 224 |
+
border-radius: 22px;
|
| 225 |
+
padding: 24px;
|
| 226 |
+
margin-top: 28px;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.highlight-title {
|
| 230 |
+
font-size: 1rem;
|
| 231 |
+
font-weight: 700;
|
| 232 |
+
color: var(--primary-accent);
|
| 233 |
+
margin-bottom: 10px;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
.highlight-box p {
|
| 237 |
+
margin-bottom: 0;
|
| 238 |
+
color: var(--text-main);
|
| 239 |
+
opacity: 0.95;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
/* PIPELINE SECTION */
|
| 243 |
+
.pipeline-card {
|
| 244 |
+
margin-top: 32px;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.pipeline {
|
| 248 |
+
margin-top: 24px;
|
| 249 |
+
display: flex;
|
| 250 |
+
flex-direction: column;
|
| 251 |
+
gap: 18px;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.pipeline-step {
|
| 255 |
+
display: flex;
|
| 256 |
+
align-items: flex-start;
|
| 257 |
+
gap: 16px;
|
| 258 |
+
background: var(--bg-color);
|
| 259 |
+
border: 1px solid var(--border-color);
|
| 260 |
+
border-radius: var(--inner-border-radius);
|
| 261 |
+
padding: 18px;
|
| 262 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.step-number {
|
| 266 |
+
min-width: 38px;
|
| 267 |
+
height: 38px;
|
| 268 |
+
border-radius: 50%;
|
| 269 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 270 |
+
color: white;
|
| 271 |
+
display: flex;
|
| 272 |
+
align-items: center;
|
| 273 |
+
justify-content: center;
|
| 274 |
+
font-weight: 700;
|
| 275 |
+
font-size: 0.92rem;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
.step-content strong {
|
| 279 |
+
display: block;
|
| 280 |
+
margin-bottom: 6px;
|
| 281 |
+
color: var(--text-main);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
.step-content span {
|
| 285 |
+
color: var(--text-muted);
|
| 286 |
+
line-height: 1.7;
|
| 287 |
+
font-size: 0.92rem;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
/* DISCLAIMER */
|
| 291 |
+
.disclaimer {
|
| 292 |
+
margin-top: 35px;
|
| 293 |
+
background: rgba(245, 158, 11, 0.08);
|
| 294 |
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
| 295 |
+
color: #d97706;
|
| 296 |
+
padding: 22px;
|
| 297 |
+
border-radius: 22px;
|
| 298 |
+
line-height: 1.8;
|
| 299 |
+
font-size: 0.93rem;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/* BUTTONS */
|
| 303 |
+
.btn-group {
|
| 304 |
+
display: flex;
|
| 305 |
+
gap: 20px;
|
| 306 |
+
justify-content: center;
|
| 307 |
+
margin-top: 42px;
|
| 308 |
+
flex-wrap: wrap;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
.btn {
|
| 312 |
+
text-decoration: none;
|
| 313 |
+
padding: 16px 30px;
|
| 314 |
+
border-radius: 16px;
|
| 315 |
+
font-weight: 700;
|
| 316 |
+
transition: all 0.25s ease;
|
| 317 |
+
min-width: 220px;
|
| 318 |
+
text-align: center;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.btn-primary {
|
| 322 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 323 |
+
color: white;
|
| 324 |
+
box-shadow: 0 12px 25px rgba(37, 99, 235, 0.2);
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
.btn-primary:hover {
|
| 328 |
+
transform: translateY(-2px);
|
| 329 |
+
box-shadow: 0 18px 30px rgba(37, 99, 235, 0.24);
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
.btn-secondary {
|
| 333 |
+
background: var(--card-bg);
|
| 334 |
+
border: 1px solid var(--border-color);
|
| 335 |
+
color: var(--text-main);
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.btn-secondary:hover {
|
| 339 |
+
transform: translateY(-2px);
|
| 340 |
+
background: var(--bg-color);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
/* FOOTER */
|
| 344 |
+
.footer {
|
| 345 |
+
text-align: center;
|
| 346 |
+
margin-top: 45px;
|
| 347 |
+
color: var(--text-muted);
|
| 348 |
+
font-size: 0.9rem;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
/* RESPONSIVE */
|
| 352 |
+
@media (max-width: 900px) {
|
| 353 |
+
.documentation-grid {
|
| 354 |
+
grid-template-columns: 1fr;
|
| 355 |
+
}
|
| 356 |
+
.sticky-column {
|
| 357 |
+
position: static;
|
| 358 |
+
}
|
| 359 |
+
.hero-section h1 {
|
| 360 |
+
font-size: 2.3rem;
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
@media (max-width: 600px) {
|
| 365 |
+
.card {
|
| 366 |
+
padding: 26px;
|
| 367 |
+
}
|
| 368 |
+
.btn {
|
| 369 |
+
width: 100%;
|
| 370 |
+
}
|
| 371 |
+
.hero-section h1 {
|
| 372 |
+
font-size: 2rem;
|
| 373 |
+
}
|
| 374 |
+
}
|
| 375 |
+
</style>
|
| 376 |
+
</head>
|
| 377 |
+
<body>
|
| 378 |
+
|
| 379 |
+
<div class="container">
|
| 380 |
+
|
| 381 |
+
<!-- Theme Toggle Button -->
|
| 382 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 383 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 384 |
+
</button>
|
| 385 |
+
|
| 386 |
+
<!-- HERO -->
|
| 387 |
+
<div class="hero-section">
|
| 388 |
+
<div class="hero-badge">
|
| 389 |
+
Model Documentation & Architecture
|
| 390 |
+
</div>
|
| 391 |
+
<h1>
|
| 392 |
+
Understanding the Prediction Pipeline
|
| 393 |
+
</h1>
|
| 394 |
+
<p>
|
| 395 |
+
This documentation explains the machine learning architecture, preprocessing pipeline, optimization strategy, and the reasoning behind simplifying telecom features for both predictive performance and end-user usability.
|
| 396 |
+
</p>
|
| 397 |
+
</div>
|
| 398 |
+
|
| 399 |
+
<!-- MAIN GRID -->
|
| 400 |
+
<div class="documentation-grid">
|
| 401 |
+
|
| 402 |
+
<!-- LEFT SIDE -->
|
| 403 |
+
<div class="card large-card">
|
| 404 |
+
<div class="card-title">
|
| 405 |
+
<i class="fa-solid fa-brain"></i> Why XGBoost Classifier?
|
| 406 |
+
</div>
|
| 407 |
+
<p>
|
| 408 |
+
The prediction system uses the <strong>XGBoost (Extreme Gradient Boosting) Classifier</strong> because the target task involves predicting a binary customer state (Churn vs. No Churn) using a combination of numerical billing patterns (charges, tenure) and categorical features (contract type, payment method).
|
| 409 |
+
</p>
|
| 410 |
+
<p>
|
| 411 |
+
XGBoost was selected because it represents the state-of-the-art in gradient boosted decision trees for structured tabular datasets, providing high training efficiency, built-in regularization, and superior classification capabilities.
|
| 412 |
+
</p>
|
| 413 |
+
|
| 414 |
+
<div class="feature-list">
|
| 415 |
+
<div class="feature-item">
|
| 416 |
+
<strong>Regularized Gradient Boosting</strong>
|
| 417 |
+
<span>
|
| 418 |
+
XGBoost incorporates L1 (Lasso) and L2 (Ridge) regularization constraints to control tree complexity and prevent overfitting during split evaluations.
|
| 419 |
+
</span>
|
| 420 |
+
</div>
|
| 421 |
+
|
| 422 |
+
<div class="feature-item">
|
| 423 |
+
<strong>Imbalance Mitigation</strong>
|
| 424 |
+
<span>
|
| 425 |
+
Configures dynamic target weighting via the `scale_pos_weight` hyperparameter, which balances training weights based on the positive-to-negative sample ratio to handle class skewness.
|
| 426 |
+
</span>
|
| 427 |
+
</div>
|
| 428 |
+
|
| 429 |
+
<div class="feature-item">
|
| 430 |
+
<strong>Non-linear Separation</strong>
|
| 431 |
+
<span>
|
| 432 |
+
Enables decision split thresholds that naturally map complex, non-linear interactions between service tenures, product counts, and monthly rates.
|
| 433 |
+
</span>
|
| 434 |
+
</div>
|
| 435 |
+
</div>
|
| 436 |
+
|
| 437 |
+
<div class="highlight-box">
|
| 438 |
+
<div class="highlight-title">Why Not Linear / Basic Classifiers?</div>
|
| 439 |
+
<p>
|
| 440 |
+
Traditional linear models assume independent, linear interactions. Customer churn data containing high multi-collinearity and multi-service usage thresholds (where churn peaks at low tenure and moderate charges) is better modeled by decision-tree systems. XGBoost significantly outperformed traditional baseline classifiers during cross-validation.
|
| 441 |
+
</p>
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
+
|
| 445 |
+
<!-- RIGHT SIDE -->
|
| 446 |
+
<div class="sticky-column">
|
| 447 |
+
|
| 448 |
+
<!-- ONE-HOT ENCODING -->
|
| 449 |
+
<div class="card small-card">
|
| 450 |
+
<div class="card-title">
|
| 451 |
+
<i class="fa-solid fa-cubes"></i> One-Hot Encoding
|
| 452 |
+
</div>
|
| 453 |
+
<p>
|
| 454 |
+
One-Hot Encoding (OHE) was utilized to transform raw categorical columns (such as Contract, PaperlessBilling, and PaymentMethod) into numeric formats.
|
| 455 |
+
</p>
|
| 456 |
+
<div class="feature-list">
|
| 457 |
+
<div class="feature-item">
|
| 458 |
+
<strong>Dummy Variable Trap Avoidance</strong>
|
| 459 |
+
<span>
|
| 460 |
+
Configures `drop='first'` to drop the baseline column for each category, preventing collinearity issues in parameter calculations.
|
| 461 |
+
</span>
|
| 462 |
+
</div>
|
| 463 |
+
<div class="feature-item">
|
| 464 |
+
<strong>Algorithmic Compatibility</strong>
|
| 465 |
+
<span>
|
| 466 |
+
Ensures raw strings are converted into mathematical array formats required by gradient boosted tree algorithms.
|
| 467 |
+
</span>
|
| 468 |
+
</div>
|
| 469 |
+
</div>
|
| 470 |
+
</div>
|
| 471 |
+
|
| 472 |
+
<!-- GRID SEARCH -->
|
| 473 |
+
<div class="card small-card">
|
| 474 |
+
<div class="card-title">
|
| 475 |
+
<i class="fa-solid fa-magnifying-glass-chart"></i> GridSearchCV
|
| 476 |
+
</div>
|
| 477 |
+
<p>
|
| 478 |
+
GridSearchCV was deployed to run exhaustive hyperparameter tuning over cross-validation folds, identifying optimal parameters for tree depth, estimators, and learning rates.
|
| 479 |
+
</p>
|
| 480 |
+
<div class="feature-list">
|
| 481 |
+
<div class="feature-item">
|
| 482 |
+
<strong>Exhaustive Optimization</strong>
|
| 483 |
+
<span>
|
| 484 |
+
Evaluates every parameter configuration across a defined parameter grid to prevent manual tuning bias.
|
| 485 |
+
</span>
|
| 486 |
+
</div>
|
| 487 |
+
<div class="feature-item">
|
| 488 |
+
<strong>Maximized ROC AUC</strong>
|
| 489 |
+
<span>
|
| 490 |
+
Optimizes model evaluation based on the Area Under the ROC Curve, balancing true positive and false positive rates.
|
| 491 |
+
</span>
|
| 492 |
+
</div>
|
| 493 |
+
</div>
|
| 494 |
+
</div>
|
| 495 |
+
|
| 496 |
+
</div>
|
| 497 |
+
|
| 498 |
+
</div>
|
| 499 |
+
|
| 500 |
+
<!-- FEATURE ENGINEERING -->
|
| 501 |
+
<div class="card pipeline-card">
|
| 502 |
+
<div class="card-title">
|
| 503 |
+
<i class="fa-solid fa-code-fork"></i> Simplifying Features for the Model and the End User
|
| 504 |
+
</div>
|
| 505 |
+
<p>
|
| 506 |
+
One important design decision in this project was preprocessing the raw customer usage and service variables into structured, derived feature groups to improve learning quality and simplify client-side entry.
|
| 507 |
+
</p>
|
| 508 |
+
<p>
|
| 509 |
+
Tabular telecom data often contains detailed, correlated medical and account records. Direct usage of raw data fields can increase dimensionality, cause noise, and complicate user interaction.
|
| 510 |
+
</p>
|
| 511 |
+
|
| 512 |
+
<div class="pipeline">
|
| 513 |
+
<div class="pipeline-step">
|
| 514 |
+
<div class="step-number">1</div>
|
| 515 |
+
<div class="step-content">
|
| 516 |
+
<strong>Service Add-On Mapping</strong>
|
| 517 |
+
<span>
|
| 518 |
+
Individual premium add-ons (such as OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTV, and StreamingMovies) are mapped to numeric indicators (-1 for No, 0 for No Internet, and 1 for Yes) to construct consistent ordinal inputs.
|
| 519 |
+
</span>
|
| 520 |
+
</div>
|
| 521 |
+
</div>
|
| 522 |
+
|
| 523 |
+
<div class="pipeline-step">
|
| 524 |
+
<div class="step-number">2</div>
|
| 525 |
+
<div class="step-content">
|
| 526 |
+
<strong>Automatic Billing Consolidation</strong>
|
| 527 |
+
<span>
|
| 528 |
+
Aggregates specific payment channels containing "(automatic)" to compile a single, binary `is_automatic` feature, capturing customer financial convenience.
|
| 529 |
+
</span>
|
| 530 |
+
</div>
|
| 531 |
+
</div>
|
| 532 |
+
|
| 533 |
+
<div class="pipeline-step">
|
| 534 |
+
<div class="step-number">3</div>
|
| 535 |
+
<div class="step-content">
|
| 536 |
+
<strong>Standardization & Scaling</strong>
|
| 537 |
+
<span>
|
| 538 |
+
Applies `StandardScaler` to `MonthlyCharges` and `TotalCharges` dynamically on the server using training population stats to prevent scale bias from dominating prediction thresholds.
|
| 539 |
+
</span>
|
| 540 |
+
</div>
|
| 541 |
+
</div>
|
| 542 |
+
|
| 543 |
+
<div class="pipeline-step">
|
| 544 |
+
<div class="step-number">4</div>
|
| 545 |
+
<div class="step-content">
|
| 546 |
+
<strong>Ecosystem Integration Metrics</strong>
|
| 547 |
+
<span>
|
| 548 |
+
Derives `Product_Count` and flags `Is_High_Risk_Integration` (1 to 3 products) and `Is_Fully_Integrated` (5 or 6 products) variables to model the protective effect of customer service bundling on user loyalty.
|
| 549 |
+
</span>
|
| 550 |
+
</div>
|
| 551 |
+
</div>
|
| 552 |
+
</div>
|
| 553 |
+
|
| 554 |
+
<div class="highlight-box">
|
| 555 |
+
<div class="highlight-title">Design Philosophy</div>
|
| 556 |
+
<p>
|
| 557 |
+
The objective was not only maximizing model accuracy and ROC AUC scores, but also creating a customer-centric analytics system that remains understandable, structured, and highly interactive for real-world business decisions.
|
| 558 |
+
</p>
|
| 559 |
+
</div>
|
| 560 |
+
</div>
|
| 561 |
+
|
| 562 |
+
<!-- DISCLAIMER -->
|
| 563 |
+
<div class="disclaimer">
|
| 564 |
+
<strong>Analytical Disclaimer:</strong>
|
| 565 |
+
This platform is intended for analytical, educational, and demonstration purposes only. Predictions generated by the system should be interpreted as probability weights and not direct statements of customer action.
|
| 566 |
+
</div>
|
| 567 |
+
|
| 568 |
+
<!-- BUTTONS -->
|
| 569 |
+
<div class="btn-group">
|
| 570 |
+
<a href="{{ url_for('predict_model') }}" class="btn btn-primary">
|
| 571 |
+
<i class="fa-solid fa-gauge-high"></i> Open Prediction Tool
|
| 572 |
+
</a>
|
| 573 |
+
<a href="/" class="btn btn-secondary">
|
| 574 |
+
<i class="fa-solid fa-house"></i> Return to Home
|
| 575 |
+
</a>
|
| 576 |
+
</div>
|
| 577 |
+
|
| 578 |
+
<!-- FOOTER -->
|
| 579 |
+
<div class="footer">
|
| 580 |
+
Customer Churn Predictor • XGBoost Classifier • One-Hot Encoding • GridSearchCV • Feature Engineering
|
| 581 |
+
</div>
|
| 582 |
+
|
| 583 |
+
</div>
|
| 584 |
+
|
| 585 |
+
<script>
|
| 586 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 587 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 588 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 589 |
+
updateThemeUI(savedTheme);
|
| 590 |
+
});
|
| 591 |
+
|
| 592 |
+
function toggleTheme() {
|
| 593 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 594 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 595 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 596 |
+
localStorage.setItem('theme', newTheme);
|
| 597 |
+
updateThemeUI(newTheme);
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
function updateThemeUI(theme) {
|
| 601 |
+
const btnText = document.getElementById('themeToggleText');
|
| 602 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 603 |
+
if (theme === 'dark') {
|
| 604 |
+
btnText.textContent = 'Light Mode';
|
| 605 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 606 |
+
} else {
|
| 607 |
+
btnText.textContent = 'Dark Mode';
|
| 608 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 609 |
+
}
|
| 610 |
+
}
|
| 611 |
+
</script>
|
| 612 |
+
</body>
|
| 613 |
+
</html>
|
templates/documentation2.html
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Analyzer | Documentation</title>
|
| 7 |
+
|
| 8 |
+
<!-- Google Font -->
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 10 |
+
<!-- Font Awesome Icons -->
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<style>
|
| 14 |
+
/* CSS Variable Theme mirroring predict.html */
|
| 15 |
+
:root {
|
| 16 |
+
--bg-color: #f5f3ff; /* soft lilac-tinted white */
|
| 17 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 18 |
+
--text-main: #1e1b4b; /* Deep indigo - highly readable */
|
| 19 |
+
--text-muted: #6b7280; /* Slate/gray 500 */
|
| 20 |
+
--border-color: #ddd6fe; /* Soft lavender border */
|
| 21 |
+
--primary-accent: #7c3aed; /* Royal Amethyst violet */
|
| 22 |
+
--primary-hover: #6d28d9; /* Darker violet */
|
| 23 |
+
--font-family: 'Inter', sans-serif;
|
| 24 |
+
--card-shadow: 0 15px 40px rgba(124, 58, 237, 0.05);
|
| 25 |
+
--card-border-radius: 28px;
|
| 26 |
+
--inner-border-radius: 18px;
|
| 27 |
+
--gradient-start: #4f46e5; /* Indigo 600 */
|
| 28 |
+
--gradient-end: #7c3aed; /* Amethyst violet 600 */
|
| 29 |
+
|
| 30 |
+
--success: #10b981;
|
| 31 |
+
--warning: #f59e0b;
|
| 32 |
+
--danger: #ef4444;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* Dark Mode variables */
|
| 36 |
+
[data-theme="dark"] {
|
| 37 |
+
--bg-color: #0b0a19; /* deep space-violet background */
|
| 38 |
+
--card-bg: #161427; /* deep violet-gray card container background */
|
| 39 |
+
--text-main: #f5f3ff; /* crisp white/violet text */
|
| 40 |
+
--text-muted: #94a3b8; /* soft slate-gray for muted text */
|
| 41 |
+
--border-color: #2c2848; /* dark violet-gray border */
|
| 42 |
+
--primary-accent: #a78bfa; /* light violet */
|
| 43 |
+
--primary-hover: #c4b5fd; /* bright violet */
|
| 44 |
+
--card-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
|
| 45 |
+
--gradient-start: #1e1b4b; /* dark indigo gradient start */
|
| 46 |
+
--gradient-end: #4c1d95; /* dark violet gradient end */
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
* {
|
| 50 |
+
margin: 0;
|
| 51 |
+
padding: 0;
|
| 52 |
+
box-sizing: border-box;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
body {
|
| 56 |
+
font-family: var(--font-family);
|
| 57 |
+
background: linear-gradient(to bottom right, var(--bg-color), var(--bg-color));
|
| 58 |
+
min-height: 100vh;
|
| 59 |
+
padding: 40px 20px;
|
| 60 |
+
color: var(--text-main);
|
| 61 |
+
transition: background 0.3s, color 0.3s;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.container {
|
| 65 |
+
max-width: 1250px;
|
| 66 |
+
margin: auto;
|
| 67 |
+
position: relative;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/* Dark Mode Switch Button */
|
| 71 |
+
.theme-toggle-btn {
|
| 72 |
+
position: absolute;
|
| 73 |
+
top: 20px;
|
| 74 |
+
right: 20px;
|
| 75 |
+
background: rgba(124, 58, 237, 0.08);
|
| 76 |
+
border: 1px solid var(--border-color);
|
| 77 |
+
color: var(--primary-accent);
|
| 78 |
+
padding: 8px 12px;
|
| 79 |
+
border-radius: 20px;
|
| 80 |
+
cursor: pointer;
|
| 81 |
+
font-size: 0.85rem;
|
| 82 |
+
font-weight: 600;
|
| 83 |
+
display: flex;
|
| 84 |
+
align-items: center;
|
| 85 |
+
gap: 6px;
|
| 86 |
+
transition: all 0.2s;
|
| 87 |
+
z-index: 100;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.theme-toggle-btn:hover {
|
| 91 |
+
background: var(--primary-accent);
|
| 92 |
+
color: #ffffff;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/* HERO SECTION */
|
| 96 |
+
.hero-section {
|
| 97 |
+
text-align: center;
|
| 98 |
+
margin-bottom: 50px;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.hero-badge {
|
| 102 |
+
display: inline-block;
|
| 103 |
+
padding: 10px 18px;
|
| 104 |
+
border-radius: 999px;
|
| 105 |
+
background: rgba(124, 58, 237, 0.08);
|
| 106 |
+
color: var(--primary-accent);
|
| 107 |
+
font-size: 0.85rem;
|
| 108 |
+
font-weight: 700;
|
| 109 |
+
letter-spacing: 0.4px;
|
| 110 |
+
margin-bottom: 22px;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.hero-section h1 {
|
| 114 |
+
font-size: 3rem;
|
| 115 |
+
font-weight: 800;
|
| 116 |
+
color: var(--text-main);
|
| 117 |
+
margin-bottom: 18px;
|
| 118 |
+
letter-spacing: -0.02em;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.hero-section p {
|
| 122 |
+
max-width: 850px;
|
| 123 |
+
margin: auto;
|
| 124 |
+
color: var(--text-muted);
|
| 125 |
+
line-height: 1.9;
|
| 126 |
+
font-size: 1.05rem;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/* MAIN GRID */
|
| 130 |
+
.documentation-grid {
|
| 131 |
+
display: grid;
|
| 132 |
+
grid-template-columns: 1.2fr 0.8fr;
|
| 133 |
+
gap: 28px;
|
| 134 |
+
align-items: start;
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/* GENERAL CARD */
|
| 138 |
+
.card {
|
| 139 |
+
background: var(--card-bg);
|
| 140 |
+
backdrop-filter: blur(14px);
|
| 141 |
+
border-radius: var(--card-border-radius);
|
| 142 |
+
padding: 34px;
|
| 143 |
+
border: 1px solid var(--border-color);
|
| 144 |
+
box-shadow: var(--card-shadow);
|
| 145 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* LARGE CARD */
|
| 149 |
+
.large-card {
|
| 150 |
+
min-height: 100%;
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/* STICKY COLUMN */
|
| 154 |
+
.sticky-column {
|
| 155 |
+
position: sticky;
|
| 156 |
+
top: 30px;
|
| 157 |
+
display: flex;
|
| 158 |
+
flex-direction: column;
|
| 159 |
+
gap: 24px;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
/* SMALL CARD */
|
| 163 |
+
.small-card {
|
| 164 |
+
padding: 28px;
|
| 165 |
+
transition: transform 0.25s ease, background-color 0.3s, border-color 0.3s;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.small-card:hover {
|
| 169 |
+
transform: translateY(-2px);
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
/* CARD TITLE */
|
| 173 |
+
.card-title {
|
| 174 |
+
font-size: 1.45rem;
|
| 175 |
+
font-weight: 700;
|
| 176 |
+
color: var(--text-main);
|
| 177 |
+
margin-bottom: 22px;
|
| 178 |
+
display: flex;
|
| 179 |
+
align-items: center;
|
| 180 |
+
gap: 8px;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
/* PARAGRAPH */
|
| 184 |
+
.card p {
|
| 185 |
+
color: var(--text-muted);
|
| 186 |
+
line-height: 1.9;
|
| 187 |
+
font-size: 0.97rem;
|
| 188 |
+
margin-bottom: 18px;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
/* FEATURE LIST */
|
| 192 |
+
.feature-list {
|
| 193 |
+
display: flex;
|
| 194 |
+
flex-direction: column;
|
| 195 |
+
gap: 18px;
|
| 196 |
+
margin-top: 22px;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.feature-item {
|
| 200 |
+
background: var(--bg-color);
|
| 201 |
+
border: 1px solid var(--border-color);
|
| 202 |
+
border-radius: var(--inner-border-radius);
|
| 203 |
+
padding: 18px 20px;
|
| 204 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.feature-item strong {
|
| 208 |
+
display: block;
|
| 209 |
+
color: var(--text-main);
|
| 210 |
+
margin-bottom: 7px;
|
| 211 |
+
font-size: 0.96rem;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.feature-item span {
|
| 215 |
+
color: var(--text-muted);
|
| 216 |
+
line-height: 1.7;
|
| 217 |
+
font-size: 0.92rem;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
/* HIGHLIGHT BOX */
|
| 221 |
+
.highlight-box {
|
| 222 |
+
background: linear-gradient(135deg, rgba(124, 58, 237, 0.05), var(--bg-color));
|
| 223 |
+
border: 1px solid var(--border-color);
|
| 224 |
+
border-radius: 22px;
|
| 225 |
+
padding: 24px;
|
| 226 |
+
margin-top: 28px;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.highlight-title {
|
| 230 |
+
font-size: 1rem;
|
| 231 |
+
font-weight: 700;
|
| 232 |
+
color: var(--primary-accent);
|
| 233 |
+
margin-bottom: 10px;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
.highlight-box p {
|
| 237 |
+
margin-bottom: 0;
|
| 238 |
+
color: var(--text-main);
|
| 239 |
+
opacity: 0.95;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
/* PIPELINE SECTION */
|
| 243 |
+
.pipeline-card {
|
| 244 |
+
margin-top: 32px;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.pipeline {
|
| 248 |
+
margin-top: 24px;
|
| 249 |
+
display: flex;
|
| 250 |
+
flex-direction: column;
|
| 251 |
+
gap: 18px;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.pipeline-step {
|
| 255 |
+
display: flex;
|
| 256 |
+
align-items: flex-start;
|
| 257 |
+
gap: 16px;
|
| 258 |
+
background: var(--bg-color);
|
| 259 |
+
border: 1px solid var(--border-color);
|
| 260 |
+
border-radius: var(--inner-border-radius);
|
| 261 |
+
padding: 18px;
|
| 262 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.step-number {
|
| 266 |
+
min-width: 38px;
|
| 267 |
+
height: 38px;
|
| 268 |
+
border-radius: 50%;
|
| 269 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 270 |
+
color: white;
|
| 271 |
+
display: flex;
|
| 272 |
+
align-items: center;
|
| 273 |
+
justify-content: center;
|
| 274 |
+
font-weight: 700;
|
| 275 |
+
font-size: 0.92rem;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
.step-content strong {
|
| 279 |
+
display: block;
|
| 280 |
+
margin-bottom: 6px;
|
| 281 |
+
color: var(--text-main);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
.step-content span {
|
| 285 |
+
color: var(--text-muted);
|
| 286 |
+
line-height: 1.7;
|
| 287 |
+
font-size: 0.92rem;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
/* DISCLAIMER */
|
| 291 |
+
.disclaimer {
|
| 292 |
+
margin-top: 35px;
|
| 293 |
+
background: rgba(245, 158, 11, 0.08);
|
| 294 |
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
| 295 |
+
color: #d97706;
|
| 296 |
+
padding: 22px;
|
| 297 |
+
border-radius: 22px;
|
| 298 |
+
line-height: 1.8;
|
| 299 |
+
font-size: 0.93rem;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/* BUTTONS */
|
| 303 |
+
.btn-group {
|
| 304 |
+
display: flex;
|
| 305 |
+
gap: 20px;
|
| 306 |
+
justify-content: center;
|
| 307 |
+
margin-top: 42px;
|
| 308 |
+
flex-wrap: wrap;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
.btn {
|
| 312 |
+
text-decoration: none;
|
| 313 |
+
padding: 16px 30px;
|
| 314 |
+
border-radius: 16px;
|
| 315 |
+
font-weight: 700;
|
| 316 |
+
transition: all 0.25s ease;
|
| 317 |
+
min-width: 220px;
|
| 318 |
+
text-align: center;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.btn-primary {
|
| 322 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 323 |
+
color: white;
|
| 324 |
+
box-shadow: 0 12px 25px rgba(124, 58, 237, 0.2);
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
.btn-primary:hover {
|
| 328 |
+
transform: translateY(-2px);
|
| 329 |
+
box-shadow: 0 18px 30px rgba(124, 58, 237, 0.24);
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
.btn-secondary {
|
| 333 |
+
background: var(--card-bg);
|
| 334 |
+
border: 1px solid var(--border-color);
|
| 335 |
+
color: var(--text-main);
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.btn-secondary:hover {
|
| 339 |
+
transform: translateY(-2px);
|
| 340 |
+
background: var(--bg-color);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
/* FOOTER */
|
| 344 |
+
.footer {
|
| 345 |
+
text-align: center;
|
| 346 |
+
margin-top: 45px;
|
| 347 |
+
color: var(--text-muted);
|
| 348 |
+
font-size: 0.9rem;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
/* RESPONSIVE */
|
| 352 |
+
@media (max-width: 900px) {
|
| 353 |
+
.documentation-grid {
|
| 354 |
+
grid-template-columns: 1fr;
|
| 355 |
+
}
|
| 356 |
+
.sticky-column {
|
| 357 |
+
position: static;
|
| 358 |
+
}
|
| 359 |
+
.hero-section h1 {
|
| 360 |
+
font-size: 2.3rem;
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
@media (max-width: 600px) {
|
| 365 |
+
.card {
|
| 366 |
+
padding: 26px;
|
| 367 |
+
}
|
| 368 |
+
.btn {
|
| 369 |
+
width: 100%;
|
| 370 |
+
}
|
| 371 |
+
.hero-section h1 {
|
| 372 |
+
font-size: 2rem;
|
| 373 |
+
}
|
| 374 |
+
}
|
| 375 |
+
</style>
|
| 376 |
+
</head>
|
| 377 |
+
<body>
|
| 378 |
+
|
| 379 |
+
<div class="container">
|
| 380 |
+
|
| 381 |
+
<!-- Theme Toggle Button -->
|
| 382 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 383 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 384 |
+
</button>
|
| 385 |
+
|
| 386 |
+
<!-- HERO -->
|
| 387 |
+
<div class="hero-section">
|
| 388 |
+
<div class="hero-badge">
|
| 389 |
+
Model Documentation & Architecture
|
| 390 |
+
</div>
|
| 391 |
+
<h1>
|
| 392 |
+
Understanding the Prediction Pipeline
|
| 393 |
+
</h1>
|
| 394 |
+
<p>
|
| 395 |
+
This documentation explains the machine learning architecture, preprocessing pipeline, optimization strategy, and the reasoning behind simplifying telecom features for both predictive performance and end-user usability.
|
| 396 |
+
</p>
|
| 397 |
+
</div>
|
| 398 |
+
|
| 399 |
+
<!-- MAIN GRID -->
|
| 400 |
+
<div class="documentation-grid">
|
| 401 |
+
|
| 402 |
+
<!-- LEFT SIDE -->
|
| 403 |
+
<div class="card large-card">
|
| 404 |
+
<div class="card-title">
|
| 405 |
+
<i class="fa-solid fa-brain"></i> Why XGBoost Classifier?
|
| 406 |
+
</div>
|
| 407 |
+
<p>
|
| 408 |
+
The prediction system uses the <strong>XGBoost (Extreme Gradient Boosting) Classifier</strong> because the target task involves predicting a binary customer state (Churn vs. No Churn) using a combination of numerical billing patterns (charges, tenure) and categorical features (contract type, payment method).
|
| 409 |
+
</p>
|
| 410 |
+
<p>
|
| 411 |
+
XGBoost was selected because it represents the state-of-the-art in gradient boosted decision trees for structured tabular datasets, providing high training efficiency, built-in regularization, and superior classification capabilities.
|
| 412 |
+
</p>
|
| 413 |
+
|
| 414 |
+
<div class="feature-list">
|
| 415 |
+
<div class="feature-item">
|
| 416 |
+
<strong>Regularized Gradient Boosting</strong>
|
| 417 |
+
<span>
|
| 418 |
+
XGBoost incorporates L1 (Lasso) and L2 (Ridge) regularization constraints to control tree complexity and prevent overfitting during split evaluations.
|
| 419 |
+
</span>
|
| 420 |
+
</div>
|
| 421 |
+
|
| 422 |
+
<div class="feature-item">
|
| 423 |
+
<strong>Imbalance Mitigation</strong>
|
| 424 |
+
<span>
|
| 425 |
+
Configures dynamic target weighting via the `scale_pos_weight` hyperparameter, which balances training weights based on the positive-to-negative sample ratio to handle class skewness.
|
| 426 |
+
</span>
|
| 427 |
+
</div>
|
| 428 |
+
|
| 429 |
+
<div class="feature-item">
|
| 430 |
+
<strong>Non-linear Separation</strong>
|
| 431 |
+
<span>
|
| 432 |
+
Enables decision split thresholds that naturally map complex, non-linear interactions between service tenures, product counts, and monthly rates.
|
| 433 |
+
</span>
|
| 434 |
+
</div>
|
| 435 |
+
</div>
|
| 436 |
+
|
| 437 |
+
<div class="highlight-box">
|
| 438 |
+
<div class="highlight-title">Why Not Linear / Basic Classifiers?</div>
|
| 439 |
+
<p>
|
| 440 |
+
Traditional linear models assume independent, linear interactions. Customer churn data containing high multi-collinearity and multi-service usage thresholds (where churn peaks at low tenure and moderate charges) is better modeled by decision-tree systems. XGBoost significantly outperformed traditional baseline classifiers during cross-validation.
|
| 441 |
+
</p>
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
+
|
| 445 |
+
<!-- RIGHT SIDE -->
|
| 446 |
+
<div class="sticky-column">
|
| 447 |
+
|
| 448 |
+
<!-- ONE-HOT ENCODING -->
|
| 449 |
+
<div class="card small-card">
|
| 450 |
+
<div class="card-title">
|
| 451 |
+
<i class="fa-solid fa-cubes"></i> One-Hot Encoding
|
| 452 |
+
</div>
|
| 453 |
+
<p>
|
| 454 |
+
One-Hot Encoding (OHE) was utilized to transform raw categorical columns (such as Contract, PaperlessBilling, and PaymentMethod) into numeric formats.
|
| 455 |
+
</p>
|
| 456 |
+
<div class="feature-list">
|
| 457 |
+
<div class="feature-item">
|
| 458 |
+
<strong>Dummy Variable Trap Avoidance</strong>
|
| 459 |
+
<span>
|
| 460 |
+
Configures `drop='first'` to drop the baseline column for each category, preventing collinearity issues in parameter calculations.
|
| 461 |
+
</span>
|
| 462 |
+
</div>
|
| 463 |
+
<div class="feature-item">
|
| 464 |
+
<strong>Algorithmic Compatibility</strong>
|
| 465 |
+
<span>
|
| 466 |
+
Ensures raw strings are converted into mathematical array formats required by gradient boosted tree algorithms.
|
| 467 |
+
</span>
|
| 468 |
+
</div>
|
| 469 |
+
</div>
|
| 470 |
+
</div>
|
| 471 |
+
|
| 472 |
+
<!-- GRID SEARCH -->
|
| 473 |
+
<div class="card small-card">
|
| 474 |
+
<div class="card-title">
|
| 475 |
+
<i class="fa-solid fa-magnifying-glass-chart"></i> GridSearchCV
|
| 476 |
+
</div>
|
| 477 |
+
<p>
|
| 478 |
+
GridSearchCV was deployed to run exhaustive hyperparameter tuning over cross-validation folds, identifying optimal parameters for tree depth, estimators, and learning rates.
|
| 479 |
+
</p>
|
| 480 |
+
<div class="feature-list">
|
| 481 |
+
<div class="feature-item">
|
| 482 |
+
<strong>Exhaustive Optimization</strong>
|
| 483 |
+
<span>
|
| 484 |
+
Evaluates every parameter configuration across a defined parameter grid to prevent manual tuning bias.
|
| 485 |
+
</span>
|
| 486 |
+
</div>
|
| 487 |
+
<div class="feature-item">
|
| 488 |
+
<strong>Maximized ROC AUC</strong>
|
| 489 |
+
<span>
|
| 490 |
+
Optimizes model evaluation based on the Area Under the ROC Curve, balancing true positive and false positive rates.
|
| 491 |
+
</span>
|
| 492 |
+
</div>
|
| 493 |
+
</div>
|
| 494 |
+
</div>
|
| 495 |
+
|
| 496 |
+
</div>
|
| 497 |
+
|
| 498 |
+
</div>
|
| 499 |
+
|
| 500 |
+
<!-- FEATURE ENGINEERING -->
|
| 501 |
+
<div class="card pipeline-card">
|
| 502 |
+
<div class="card-title">
|
| 503 |
+
<i class="fa-solid fa-code-fork"></i> Simplifying Features for the Model and the End User
|
| 504 |
+
</div>
|
| 505 |
+
<p>
|
| 506 |
+
One important design decision in this project was preprocessing the raw customer usage and service variables into structured, derived feature groups to improve learning quality and simplify client-side entry.
|
| 507 |
+
</p>
|
| 508 |
+
<p>
|
| 509 |
+
Tabular telecom data often contains detailed, correlated medical and account records. Direct usage of raw data fields can increase dimensionality, cause noise, and complicate user interaction.
|
| 510 |
+
</p>
|
| 511 |
+
|
| 512 |
+
<div class="pipeline">
|
| 513 |
+
<div class="pipeline-step">
|
| 514 |
+
<div class="step-number">1</div>
|
| 515 |
+
<div class="step-content">
|
| 516 |
+
<strong>Service Add-On Mapping</strong>
|
| 517 |
+
<span>
|
| 518 |
+
Individual premium add-ons (such as OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTV, and StreamingMovies) are mapped to numeric indicators (-1 for No, 0 for No Internet, and 1 for Yes) to construct consistent ordinal inputs.
|
| 519 |
+
</span>
|
| 520 |
+
</div>
|
| 521 |
+
</div>
|
| 522 |
+
|
| 523 |
+
<div class="pipeline-step">
|
| 524 |
+
<div class="step-number">2</div>
|
| 525 |
+
<div class="step-content">
|
| 526 |
+
<strong>Automatic Billing Consolidation</strong>
|
| 527 |
+
<span>
|
| 528 |
+
Aggregates specific payment channels containing "(automatic)" to compile a single, binary `is_automatic` feature, capturing customer financial convenience.
|
| 529 |
+
</span>
|
| 530 |
+
</div>
|
| 531 |
+
</div>
|
| 532 |
+
|
| 533 |
+
<div class="pipeline-step">
|
| 534 |
+
<div class="step-number">3</div>
|
| 535 |
+
<div class="step-content">
|
| 536 |
+
<strong>Standardization & Scaling</strong>
|
| 537 |
+
<span>
|
| 538 |
+
Applies `StandardScaler` to `MonthlyCharges` and `TotalCharges` dynamically on the server using training population stats to prevent scale bias from dominating prediction thresholds.
|
| 539 |
+
</span>
|
| 540 |
+
</div>
|
| 541 |
+
</div>
|
| 542 |
+
|
| 543 |
+
<div class="pipeline-step">
|
| 544 |
+
<div class="step-number">4</div>
|
| 545 |
+
<div class="step-content">
|
| 546 |
+
<strong>Ecosystem Integration Metrics</strong>
|
| 547 |
+
<span>
|
| 548 |
+
Derives `Product_Count` and flags `Is_High_Risk_Integration` (1 to 3 products) and `Is_Fully_Integrated` (5 or 6 products) variables to model the protective effect of customer service bundling on user loyalty.
|
| 549 |
+
</span>
|
| 550 |
+
</div>
|
| 551 |
+
</div>
|
| 552 |
+
</div>
|
| 553 |
+
|
| 554 |
+
<div class="highlight-box">
|
| 555 |
+
<div class="highlight-title">Design Philosophy</div>
|
| 556 |
+
<p>
|
| 557 |
+
The objective was not only maximizing model accuracy and ROC AUC scores, but also creating a customer-centric analytics system that remains understandable, structured, and highly interactive for real-world business decisions.
|
| 558 |
+
</p>
|
| 559 |
+
</div>
|
| 560 |
+
</div>
|
| 561 |
+
|
| 562 |
+
<!-- DISCLAIMER -->
|
| 563 |
+
<div class="disclaimer">
|
| 564 |
+
<strong>Analytical Disclaimer:</strong>
|
| 565 |
+
This platform is intended for analytical, educational, and demonstration purposes only. Predictions generated by the system should be interpreted as probability weights and not direct statements of customer action.
|
| 566 |
+
</div>
|
| 567 |
+
|
| 568 |
+
<!-- BUTTONS -->
|
| 569 |
+
<div class="btn-group">
|
| 570 |
+
<a href="{{ url_for('predict_model') }}" class="btn btn-primary">
|
| 571 |
+
<i class="fa-solid fa-gauge-high"></i> Open Prediction Tool
|
| 572 |
+
</a>
|
| 573 |
+
<a href="{{ url_for('home') }}" class="btn btn-secondary">
|
| 574 |
+
<i class="fa-solid fa-house"></i> Return to Home
|
| 575 |
+
</a>
|
| 576 |
+
</div>
|
| 577 |
+
|
| 578 |
+
<!-- FOOTER -->
|
| 579 |
+
<div class="footer">
|
| 580 |
+
Customer Churn Predictor • XGBoost Classifier • One-Hot Encoding • GridSearchCV • Feature Engineering
|
| 581 |
+
</div>
|
| 582 |
+
|
| 583 |
+
</div>
|
| 584 |
+
|
| 585 |
+
<script>
|
| 586 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 587 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 588 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 589 |
+
updateThemeUI(savedTheme);
|
| 590 |
+
});
|
| 591 |
+
|
| 592 |
+
function toggleTheme() {
|
| 593 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 594 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 595 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 596 |
+
localStorage.setItem('theme', newTheme);
|
| 597 |
+
updateThemeUI(newTheme);
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
function updateThemeUI(theme) {
|
| 601 |
+
const btnText = document.getElementById('themeToggleText');
|
| 602 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 603 |
+
if (theme === 'dark') {
|
| 604 |
+
btnText.textContent = 'Light Mode';
|
| 605 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 606 |
+
} else {
|
| 607 |
+
btnText.textContent = 'Dark Mode';
|
| 608 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 609 |
+
}
|
| 610 |
+
}
|
| 611 |
+
</script>
|
| 612 |
+
</body>
|
| 613 |
+
</html>
|
templates/home.html
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Analyzer | Home</title>
|
| 7 |
+
|
| 8 |
+
<!-- Google Font -->
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 10 |
+
<!-- Font Awesome Icons -->
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<style>
|
| 14 |
+
/* CSS Variable Theme mirroring predict.html */
|
| 15 |
+
:root {
|
| 16 |
+
--bg-color: #f8fafc; /* Slate 50 - Light, clean background */
|
| 17 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 18 |
+
--text-main: #0f172a; /* Slate 900 - Deep, readable text */
|
| 19 |
+
--text-muted: #64748b; /* Slate 500 - For labels and placeholders */
|
| 20 |
+
--border-color: #cbd5e1; /* Slate 300 - Subtle input borders */
|
| 21 |
+
--primary-accent: #2563eb; /* Blue 600 - Focus state & primary buttons */
|
| 22 |
+
--primary-hover: #1d4ed8; /* Blue 700 */
|
| 23 |
+
--font-family: 'Inter', sans-serif;
|
| 24 |
+
--card-shadow: 0 20px 50px rgba(15, 23, 42, 0.06);
|
| 25 |
+
--card-border-radius: 32px;
|
| 26 |
+
--inner-border-radius: 18px;
|
| 27 |
+
--gradient-start: #1e3a8a;
|
| 28 |
+
--gradient-end: #3b82f6;
|
| 29 |
+
|
| 30 |
+
--success: #10b981;
|
| 31 |
+
--warning: #f59e0b;
|
| 32 |
+
--danger: #ef4444;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* Dark Mode variables */
|
| 36 |
+
[data-theme="dark"] {
|
| 37 |
+
--bg-color: #0f172a;
|
| 38 |
+
--card-bg: #1e293b;
|
| 39 |
+
--text-main: #f8fafc;
|
| 40 |
+
--text-muted: #94a3b8;
|
| 41 |
+
--border-color: #334155;
|
| 42 |
+
--primary-accent: #3b82f6;
|
| 43 |
+
--primary-hover: #60a5fa;
|
| 44 |
+
--card-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
|
| 45 |
+
--gradient-start: #0f172a;
|
| 46 |
+
--gradient-end: #1e3a8a;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
* {
|
| 50 |
+
margin: 0;
|
| 51 |
+
padding: 0;
|
| 52 |
+
box-sizing: border-box;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
body {
|
| 56 |
+
font-family: var(--font-family);
|
| 57 |
+
min-height: 100vh;
|
| 58 |
+
background: linear-gradient(to bottom right, var(--bg-color), var(--bg-color));
|
| 59 |
+
padding: 40px 20px;
|
| 60 |
+
display: flex;
|
| 61 |
+
align-items: center;
|
| 62 |
+
justify-content: center;
|
| 63 |
+
color: var(--text-main);
|
| 64 |
+
transition: background 0.3s, color 0.3s;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.hero-container {
|
| 68 |
+
width: 100%;
|
| 69 |
+
max-width: 1050px;
|
| 70 |
+
background: var(--card-bg);
|
| 71 |
+
backdrop-filter: blur(14px);
|
| 72 |
+
border-radius: var(--card-border-radius);
|
| 73 |
+
padding: 60px;
|
| 74 |
+
border: 1px solid var(--border-color);
|
| 75 |
+
box-shadow: var(--card-shadow);
|
| 76 |
+
animation: fadeIn 0.5s ease;
|
| 77 |
+
position: relative;
|
| 78 |
+
transition: background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
/* Dark Mode Switch Button */
|
| 82 |
+
.theme-toggle-btn {
|
| 83 |
+
position: absolute;
|
| 84 |
+
top: 20px;
|
| 85 |
+
right: 20px;
|
| 86 |
+
background: rgba(37, 99, 235, 0.08);
|
| 87 |
+
border: 1px solid var(--border-color);
|
| 88 |
+
color: var(--primary-accent);
|
| 89 |
+
padding: 8px 12px;
|
| 90 |
+
border-radius: 20px;
|
| 91 |
+
cursor: pointer;
|
| 92 |
+
font-size: 0.85rem;
|
| 93 |
+
font-weight: 600;
|
| 94 |
+
display: flex;
|
| 95 |
+
align-items: center;
|
| 96 |
+
gap: 6px;
|
| 97 |
+
transition: all 0.2s;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.theme-toggle-btn:hover {
|
| 101 |
+
background: var(--primary-accent);
|
| 102 |
+
color: #ffffff;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
/* HERO Section */
|
| 106 |
+
.hero-top {
|
| 107 |
+
text-align: center;
|
| 108 |
+
margin-bottom: 50px;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.hero-badge {
|
| 112 |
+
display: inline-block;
|
| 113 |
+
padding: 10px 18px;
|
| 114 |
+
border-radius: 999px;
|
| 115 |
+
background: rgba(37, 99, 235, 0.08);
|
| 116 |
+
color: var(--primary-accent);
|
| 117 |
+
font-size: 0.85rem;
|
| 118 |
+
font-weight: 700;
|
| 119 |
+
letter-spacing: 0.4px;
|
| 120 |
+
margin-bottom: 22px;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
h1 {
|
| 124 |
+
font-size: 3.2rem;
|
| 125 |
+
line-height: 1.1;
|
| 126 |
+
color: var(--text-main);
|
| 127 |
+
margin-bottom: 20px;
|
| 128 |
+
font-weight: 800;
|
| 129 |
+
letter-spacing: -0.02em;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.hero-description {
|
| 133 |
+
max-width: 760px;
|
| 134 |
+
margin: auto;
|
| 135 |
+
color: var(--text-muted);
|
| 136 |
+
line-height: 1.9;
|
| 137 |
+
font-size: 1.05rem;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/* GRID */
|
| 141 |
+
.content-grid {
|
| 142 |
+
display: grid;
|
| 143 |
+
grid-template-columns: 1.1fr 0.9fr;
|
| 144 |
+
gap: 28px;
|
| 145 |
+
margin-bottom: 40px;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* CARDS */
|
| 149 |
+
.card {
|
| 150 |
+
background: var(--card-bg);
|
| 151 |
+
border-radius: 24px;
|
| 152 |
+
padding: 30px;
|
| 153 |
+
border: 1px solid var(--border-color);
|
| 154 |
+
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.03);
|
| 155 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
.card-title {
|
| 159 |
+
font-size: 1.35rem;
|
| 160 |
+
font-weight: 700;
|
| 161 |
+
margin-bottom: 18px;
|
| 162 |
+
color: var(--text-main);
|
| 163 |
+
display: flex;
|
| 164 |
+
align-items: center;
|
| 165 |
+
gap: 8px;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.card p {
|
| 169 |
+
color: var(--text-muted);
|
| 170 |
+
line-height: 1.8;
|
| 171 |
+
font-size: 0.97rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/* FEATURE LIST */
|
| 175 |
+
.feature-list {
|
| 176 |
+
margin-top: 22px;
|
| 177 |
+
display: flex;
|
| 178 |
+
flex-direction: column;
|
| 179 |
+
gap: 16px;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
.feature-item {
|
| 183 |
+
background: var(--bg-color);
|
| 184 |
+
border-radius: 16px;
|
| 185 |
+
padding: 16px 18px;
|
| 186 |
+
border: 1px solid var(--border-color);
|
| 187 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.feature-item strong {
|
| 191 |
+
display: block;
|
| 192 |
+
margin-bottom: 6px;
|
| 193 |
+
color: var(--text-main);
|
| 194 |
+
font-size: 0.95rem;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
.feature-item span {
|
| 198 |
+
color: var(--text-muted);
|
| 199 |
+
font-size: 0.92rem;
|
| 200 |
+
line-height: 1.6;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
/* HIGHLIGHT */
|
| 204 |
+
.highlight-box {
|
| 205 |
+
background: linear-gradient(135deg, rgba(37, 99, 235, 0.05), var(--bg-color));
|
| 206 |
+
border: 1px solid var(--border-color);
|
| 207 |
+
border-radius: 22px;
|
| 208 |
+
padding: 28px;
|
| 209 |
+
margin-top: 24px;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
.highlight-title {
|
| 213 |
+
color: var(--primary-accent);
|
| 214 |
+
font-size: 1rem;
|
| 215 |
+
font-weight: 700;
|
| 216 |
+
margin-bottom: 12px;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
.highlight-box p {
|
| 220 |
+
color: var(--text-main);
|
| 221 |
+
line-height: 1.8;
|
| 222 |
+
font-size: 0.95rem;
|
| 223 |
+
opacity: 0.95;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
/* METRICS */
|
| 227 |
+
.metrics-grid {
|
| 228 |
+
display: grid;
|
| 229 |
+
grid-template-columns: 1fr 1fr;
|
| 230 |
+
gap: 18px;
|
| 231 |
+
margin-top: 22px;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
.metric-card {
|
| 235 |
+
background: var(--bg-color);
|
| 236 |
+
border-radius: var(--inner-border-radius);
|
| 237 |
+
padding: 20px;
|
| 238 |
+
border: 1px solid var(--border-color);
|
| 239 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
.metric-number {
|
| 243 |
+
font-size: 1.8rem;
|
| 244 |
+
font-weight: 800;
|
| 245 |
+
color: var(--primary-accent);
|
| 246 |
+
margin-bottom: 6px;
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
.metric-label {
|
| 250 |
+
color: var(--text-muted);
|
| 251 |
+
font-size: 0.9rem;
|
| 252 |
+
line-height: 1.5;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/* DISCLAIMER */
|
| 256 |
+
.disclaimer {
|
| 257 |
+
margin-top: 28px;
|
| 258 |
+
background: rgba(245, 158, 11, 0.08);
|
| 259 |
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
| 260 |
+
color: #d97706;
|
| 261 |
+
padding: 18px 20px;
|
| 262 |
+
border-radius: var(--inner-border-radius);
|
| 263 |
+
line-height: 1.8;
|
| 264 |
+
font-size: 0.92rem;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
/* BUTTONS */
|
| 268 |
+
.btn-group {
|
| 269 |
+
display: flex;
|
| 270 |
+
gap: 20px;
|
| 271 |
+
justify-content: center;
|
| 272 |
+
margin-top: 42px;
|
| 273 |
+
flex-wrap: wrap;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
.btn {
|
| 277 |
+
text-decoration: none;
|
| 278 |
+
padding: 16px 30px;
|
| 279 |
+
border-radius: 16px;
|
| 280 |
+
font-weight: 700;
|
| 281 |
+
font-size: 0.97rem;
|
| 282 |
+
transition: all 0.25s ease;
|
| 283 |
+
display: inline-flex;
|
| 284 |
+
align-items: center;
|
| 285 |
+
justify-content: center;
|
| 286 |
+
min-width: 230px;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
.btn-primary {
|
| 290 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 291 |
+
color: white;
|
| 292 |
+
box-shadow: 0 12px 25px rgba(37, 99, 235, 0.2);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
.btn-primary:hover {
|
| 296 |
+
transform: translateY(-2px);
|
| 297 |
+
box-shadow: 0 18px 30px rgba(37, 99, 235, 0.25);
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.btn-secondary {
|
| 301 |
+
background: var(--card-bg);
|
| 302 |
+
color: var(--text-main);
|
| 303 |
+
border: 1px solid var(--border-color);
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
.btn-secondary:hover {
|
| 307 |
+
transform: translateY(-2px);
|
| 308 |
+
background: var(--bg-color);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
/* FOOTER */
|
| 312 |
+
.footer {
|
| 313 |
+
text-align: center;
|
| 314 |
+
margin-top: 45px;
|
| 315 |
+
color: var(--text-muted);
|
| 316 |
+
font-size: 0.9rem;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
/* ANIMATION */
|
| 320 |
+
@keyframes fadeIn {
|
| 321 |
+
from {
|
| 322 |
+
opacity: 0;
|
| 323 |
+
transform: translateY(12px);
|
| 324 |
+
}
|
| 325 |
+
to {
|
| 326 |
+
opacity: 1;
|
| 327 |
+
transform: translateY(0);
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
/* RESPONSIVE */
|
| 332 |
+
@media (max-width: 900px) {
|
| 333 |
+
.content-grid {
|
| 334 |
+
grid-template-columns: 1fr;
|
| 335 |
+
}
|
| 336 |
+
h1 {
|
| 337 |
+
font-size: 2.3rem;
|
| 338 |
+
}
|
| 339 |
+
.hero-container {
|
| 340 |
+
padding: 35px;
|
| 341 |
+
}
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
@media (max-width: 600px) {
|
| 345 |
+
.metrics-grid {
|
| 346 |
+
grid-template-columns: 1fr;
|
| 347 |
+
}
|
| 348 |
+
.btn {
|
| 349 |
+
width: 100%;
|
| 350 |
+
}
|
| 351 |
+
.hero-container {
|
| 352 |
+
padding: 28px;
|
| 353 |
+
}
|
| 354 |
+
h1 {
|
| 355 |
+
font-size: 2rem;
|
| 356 |
+
}
|
| 357 |
+
}
|
| 358 |
+
</style>
|
| 359 |
+
</head>
|
| 360 |
+
<body>
|
| 361 |
+
|
| 362 |
+
<div class="hero-container">
|
| 363 |
+
|
| 364 |
+
<!-- Theme Toggle Button -->
|
| 365 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 366 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 367 |
+
</button>
|
| 368 |
+
|
| 369 |
+
<!-- HERO -->
|
| 370 |
+
<div class="hero-top">
|
| 371 |
+
<div class="hero-badge">
|
| 372 |
+
AI-Driven Telecom Intelligence Platform
|
| 373 |
+
</div>
|
| 374 |
+
<h1>
|
| 375 |
+
Customer Churn Predictor
|
| 376 |
+
</h1>
|
| 377 |
+
<p class="hero-description">
|
| 378 |
+
Advanced machine learning system designed to estimate customer churn risk patterns using customer metadata, active service subscriptions, contract types, and billing history.
|
| 379 |
+
</p>
|
| 380 |
+
</div>
|
| 381 |
+
|
| 382 |
+
<!-- CONTENT GRID -->
|
| 383 |
+
<div class="content-grid">
|
| 384 |
+
|
| 385 |
+
<!-- LEFT CARD -->
|
| 386 |
+
<div class="card">
|
| 387 |
+
<div class="card-title">
|
| 388 |
+
<i class="fa-solid fa-chart-line"></i> Platform Overview
|
| 389 |
+
</div>
|
| 390 |
+
<p>
|
| 391 |
+
This platform was developed to analyze customer profiles and identify patterns associated with churn behavior. The prediction pipeline combines statistical learning, feature preprocessing, and subscription activity indicators to estimate customer churn probability.
|
| 392 |
+
</p>
|
| 393 |
+
|
| 394 |
+
<div class="feature-list">
|
| 395 |
+
<div class="feature-item">
|
| 396 |
+
<strong>XGBoost Classifier</strong>
|
| 397 |
+
<span>
|
| 398 |
+
Gradient boosted decision trees optimized for high classification performance, class imbalance, and stable prediction behavior.
|
| 399 |
+
</span>
|
| 400 |
+
</div>
|
| 401 |
+
|
| 402 |
+
<div class="feature-item">
|
| 403 |
+
<strong>GridSearchCV Optimization</strong>
|
| 404 |
+
<span>
|
| 405 |
+
Exhaustive hyperparameter tuning performed to maximize model ROC AUC score and ensure robust generalization across demographic segments.
|
| 406 |
+
</span>
|
| 407 |
+
</div>
|
| 408 |
+
|
| 409 |
+
<div class="feature-item">
|
| 410 |
+
<strong>Telecom Feature Engineering</strong>
|
| 411 |
+
<span>
|
| 412 |
+
One-hot categorical encodings, standardized numerical billing features, product usage counts, and integration risk levels built into the pipeline.
|
| 413 |
+
</span>
|
| 414 |
+
</div>
|
| 415 |
+
</div>
|
| 416 |
+
|
| 417 |
+
<div class="highlight-box">
|
| 418 |
+
<div class="highlight-title">Optimization Overview</div>
|
| 419 |
+
<p>
|
| 420 |
+
This version utilizes <strong>XGBoost</strong> to achieve high classification power on structured customer data. Hyperparameters (learning rate, max depth, estimators) were tuned using <strong>GridSearchCV</strong> to maximize predictive reliability and precision-recall trade-offs.
|
| 421 |
+
</p>
|
| 422 |
+
</div>
|
| 423 |
+
</div>
|
| 424 |
+
|
| 425 |
+
<!-- RIGHT CARD -->
|
| 426 |
+
<div class="card">
|
| 427 |
+
<div class="card-title">
|
| 428 |
+
<i class="fa-solid fa-gears"></i> System Capabilities
|
| 429 |
+
</div>
|
| 430 |
+
<p>
|
| 431 |
+
The model evaluates customer profiles using a combination of customer tenure, service subscriptions, charges, payment history, and contract parameters.
|
| 432 |
+
</p>
|
| 433 |
+
|
| 434 |
+
<div class="metrics-grid">
|
| 435 |
+
<div class="metric-card" style="grid-row: span 2; display: flex; flex-direction: column; justify-content: center;">
|
| 436 |
+
<div class="metric-number" style="font-size: 1.45rem; line-height: 1.35; margin-bottom: 8px;">82% Overall Accuracy<br>& 75% Churn Recall</div>
|
| 437 |
+
<div class="metric-label">
|
| 438 |
+
Optimized XGBoost model performance showcasing overall accuracy and recall for churn detection
|
| 439 |
+
</div>
|
| 440 |
+
</div>
|
| 441 |
+
|
| 442 |
+
<div class="metric-card">
|
| 443 |
+
<div class="metric-number">XGB</div>
|
| 444 |
+
<div class="metric-label">
|
| 445 |
+
Extreme Gradient Boosting used for predictions
|
| 446 |
+
</div>
|
| 447 |
+
</div>
|
| 448 |
+
|
| 449 |
+
<div class="metric-card">
|
| 450 |
+
<div class="metric-number">ML</div>
|
| 451 |
+
<div class="metric-label">
|
| 452 |
+
Machine learning pipeline with preprocessing integration
|
| 453 |
+
</div>
|
| 454 |
+
</div>
|
| 455 |
+
</div>
|
| 456 |
+
|
| 457 |
+
<div class="disclaimer">
|
| 458 |
+
<strong>Analytical Disclaimer:</strong>
|
| 459 |
+
This platform is intended for analytical and research demonstration purposes. Predictions generated by the system should be used in combination with qualitative customer insights and business policy.
|
| 460 |
+
</div>
|
| 461 |
+
</div>
|
| 462 |
+
|
| 463 |
+
</div>
|
| 464 |
+
|
| 465 |
+
<!-- BUTTONS -->
|
| 466 |
+
<div class="btn-group">
|
| 467 |
+
<a href="{{ url_for('predict_model') }}" class="btn btn-primary">
|
| 468 |
+
<i class="fa-solid fa-gauge-high"></i> Launch Prediction Tool
|
| 469 |
+
</a>
|
| 470 |
+
<a href="{{ url_for('documentation') }}" class="btn btn-secondary">
|
| 471 |
+
<i class="fa-solid fa-book"></i> View Model Documentation
|
| 472 |
+
</a>
|
| 473 |
+
</div>
|
| 474 |
+
|
| 475 |
+
<!-- FOOTER -->
|
| 476 |
+
<div class="footer">
|
| 477 |
+
Customer Churn Predictor • Powered by XGBoost Classifier, Feature Engineering, and GridSearchCV
|
| 478 |
+
</div>
|
| 479 |
+
|
| 480 |
+
</div>
|
| 481 |
+
|
| 482 |
+
<script>
|
| 483 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 484 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 485 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 486 |
+
updateThemeUI(savedTheme);
|
| 487 |
+
});
|
| 488 |
+
|
| 489 |
+
function toggleTheme() {
|
| 490 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 491 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 492 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 493 |
+
localStorage.setItem('theme', newTheme);
|
| 494 |
+
updateThemeUI(newTheme);
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
function updateThemeUI(theme) {
|
| 498 |
+
const btnText = document.getElementById('themeToggleText');
|
| 499 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 500 |
+
if (theme === 'dark') {
|
| 501 |
+
btnText.textContent = 'Light Mode';
|
| 502 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 503 |
+
} else {
|
| 504 |
+
btnText.textContent = 'Dark Mode';
|
| 505 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 506 |
+
}
|
| 507 |
+
}
|
| 508 |
+
</script>
|
| 509 |
+
</body>
|
| 510 |
+
</html>
|
templates/home2.html
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Analyzer | Home</title>
|
| 7 |
+
|
| 8 |
+
<!-- Google Font -->
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
| 10 |
+
<!-- Font Awesome Icons -->
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<style>
|
| 14 |
+
/* CSS Variable Theme mirroring predict.html */
|
| 15 |
+
:root {
|
| 16 |
+
--bg-color: #f5f3ff; /* soft lilac-tinted white */
|
| 17 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 18 |
+
--text-main: #1e1b4b; /* Deep indigo - highly readable */
|
| 19 |
+
--text-muted: #6b7280; /* Slate/gray 500 */
|
| 20 |
+
--border-color: #ddd6fe; /* Soft lavender border */
|
| 21 |
+
--primary-accent: #7c3aed; /* Royal Amethyst violet */
|
| 22 |
+
--primary-hover: #6d28d9; /* Darker violet */
|
| 23 |
+
--font-family: 'Inter', sans-serif;
|
| 24 |
+
--card-shadow: 0 20px 50px rgba(124, 58, 237, 0.05);
|
| 25 |
+
--card-border-radius: 32px;
|
| 26 |
+
--inner-border-radius: 18px;
|
| 27 |
+
--gradient-start: #4f46e5; /* Indigo 600 */
|
| 28 |
+
--gradient-end: #7c3aed; /* Amethyst violet 600 */
|
| 29 |
+
|
| 30 |
+
--success: #10b981;
|
| 31 |
+
--warning: #f59e0b;
|
| 32 |
+
--danger: #ef4444;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* Dark Mode variables */
|
| 36 |
+
[data-theme="dark"] {
|
| 37 |
+
--bg-color: #0b0a19; /* deep space-violet background */
|
| 38 |
+
--card-bg: #161427; /* deep violet-gray card container background */
|
| 39 |
+
--text-main: #f5f3ff; /* crisp white/violet text */
|
| 40 |
+
--text-muted: #94a3b8; /* soft slate-gray for muted text */
|
| 41 |
+
--border-color: #2c2848; /* dark violet-gray border */
|
| 42 |
+
--primary-accent: #a78bfa; /* light violet */
|
| 43 |
+
--primary-hover: #c4b5fd; /* bright violet */
|
| 44 |
+
--card-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
|
| 45 |
+
--gradient-start: #1e1b4b; /* dark indigo gradient start */
|
| 46 |
+
--gradient-end: #4c1d95; /* dark violet gradient end */
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
* {
|
| 50 |
+
margin: 0;
|
| 51 |
+
padding: 0;
|
| 52 |
+
box-sizing: border-box;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
body {
|
| 56 |
+
font-family: var(--font-family);
|
| 57 |
+
min-height: 100vh;
|
| 58 |
+
background: linear-gradient(to bottom right, var(--bg-color), var(--bg-color));
|
| 59 |
+
padding: 40px 20px;
|
| 60 |
+
display: flex;
|
| 61 |
+
align-items: center;
|
| 62 |
+
justify-content: center;
|
| 63 |
+
color: var(--text-main);
|
| 64 |
+
transition: background 0.3s, color 0.3s;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.hero-container {
|
| 68 |
+
width: 100%;
|
| 69 |
+
max-width: 1050px;
|
| 70 |
+
background: var(--card-bg);
|
| 71 |
+
backdrop-filter: blur(14px);
|
| 72 |
+
border-radius: var(--card-border-radius);
|
| 73 |
+
padding: 60px;
|
| 74 |
+
border: 1px solid var(--border-color);
|
| 75 |
+
box-shadow: var(--card-shadow);
|
| 76 |
+
animation: fadeIn 0.5s ease;
|
| 77 |
+
position: relative;
|
| 78 |
+
transition: background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
/* Dark Mode Switch Button */
|
| 82 |
+
.theme-toggle-btn {
|
| 83 |
+
position: absolute;
|
| 84 |
+
top: 20px;
|
| 85 |
+
right: 20px;
|
| 86 |
+
background: rgba(124, 58, 237, 0.08);
|
| 87 |
+
border: 1px solid var(--border-color);
|
| 88 |
+
color: var(--primary-accent);
|
| 89 |
+
padding: 8px 12px;
|
| 90 |
+
border-radius: 20px;
|
| 91 |
+
cursor: pointer;
|
| 92 |
+
font-size: 0.85rem;
|
| 93 |
+
font-weight: 600;
|
| 94 |
+
display: flex;
|
| 95 |
+
align-items: center;
|
| 96 |
+
gap: 6px;
|
| 97 |
+
transition: all 0.2s;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.theme-toggle-btn:hover {
|
| 101 |
+
background: var(--primary-accent);
|
| 102 |
+
color: #ffffff;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
/* HERO Section */
|
| 106 |
+
.hero-top {
|
| 107 |
+
text-align: center;
|
| 108 |
+
margin-bottom: 50px;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
.hero-badge {
|
| 112 |
+
display: inline-block;
|
| 113 |
+
padding: 10px 18px;
|
| 114 |
+
border-radius: 999px;
|
| 115 |
+
background: rgba(124, 58, 237, 0.08);
|
| 116 |
+
color: var(--primary-accent);
|
| 117 |
+
font-size: 0.85rem;
|
| 118 |
+
font-weight: 700;
|
| 119 |
+
letter-spacing: 0.4px;
|
| 120 |
+
margin-bottom: 22px;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
h1 {
|
| 124 |
+
font-size: 3.2rem;
|
| 125 |
+
line-height: 1.1;
|
| 126 |
+
color: var(--text-main);
|
| 127 |
+
margin-bottom: 20px;
|
| 128 |
+
font-weight: 800;
|
| 129 |
+
letter-spacing: -0.02em;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.hero-description {
|
| 133 |
+
max-width: 760px;
|
| 134 |
+
margin: auto;
|
| 135 |
+
color: var(--text-muted);
|
| 136 |
+
line-height: 1.9;
|
| 137 |
+
font-size: 1.05rem;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/* GRID */
|
| 141 |
+
.content-grid {
|
| 142 |
+
display: grid;
|
| 143 |
+
grid-template-columns: 1.1fr 0.9fr;
|
| 144 |
+
gap: 28px;
|
| 145 |
+
margin-bottom: 40px;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* CARDS */
|
| 149 |
+
.card {
|
| 150 |
+
background: var(--card-bg);
|
| 151 |
+
border-radius: 24px;
|
| 152 |
+
padding: 30px;
|
| 153 |
+
border: 1px solid var(--border-color);
|
| 154 |
+
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.03);
|
| 155 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
.card-title {
|
| 159 |
+
font-size: 1.35rem;
|
| 160 |
+
font-weight: 700;
|
| 161 |
+
margin-bottom: 18px;
|
| 162 |
+
color: var(--text-main);
|
| 163 |
+
display: flex;
|
| 164 |
+
align-items: center;
|
| 165 |
+
gap: 8px;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.card p {
|
| 169 |
+
color: var(--text-muted);
|
| 170 |
+
line-height: 1.8;
|
| 171 |
+
font-size: 0.97rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
/* FEATURE LIST */
|
| 175 |
+
.feature-list {
|
| 176 |
+
margin-top: 22px;
|
| 177 |
+
display: flex;
|
| 178 |
+
flex-direction: column;
|
| 179 |
+
gap: 16px;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
.feature-item {
|
| 183 |
+
background: var(--bg-color);
|
| 184 |
+
border-radius: 16px;
|
| 185 |
+
padding: 16px 18px;
|
| 186 |
+
border: 1px solid var(--border-color);
|
| 187 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.feature-item strong {
|
| 191 |
+
display: block;
|
| 192 |
+
margin-bottom: 6px;
|
| 193 |
+
color: var(--text-main);
|
| 194 |
+
font-size: 0.95rem;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
.feature-item span {
|
| 198 |
+
color: var(--text-muted);
|
| 199 |
+
font-size: 0.92rem;
|
| 200 |
+
line-height: 1.6;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
/* HIGHLIGHT */
|
| 204 |
+
.highlight-box {
|
| 205 |
+
background: linear-gradient(135deg, rgba(124, 58, 237, 0.05), var(--bg-color));
|
| 206 |
+
border: 1px solid var(--border-color);
|
| 207 |
+
border-radius: 22px;
|
| 208 |
+
padding: 28px;
|
| 209 |
+
margin-top: 24px;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
.highlight-title {
|
| 213 |
+
color: var(--primary-accent);
|
| 214 |
+
font-size: 1rem;
|
| 215 |
+
font-weight: 700;
|
| 216 |
+
margin-bottom: 12px;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
.highlight-box p {
|
| 220 |
+
color: var(--text-main);
|
| 221 |
+
line-height: 1.8;
|
| 222 |
+
font-size: 0.95rem;
|
| 223 |
+
opacity: 0.95;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
/* METRICS */
|
| 227 |
+
.metrics-grid {
|
| 228 |
+
display: grid;
|
| 229 |
+
grid-template-columns: 1fr 1fr;
|
| 230 |
+
gap: 18px;
|
| 231 |
+
margin-top: 22px;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
.metric-card {
|
| 235 |
+
background: var(--bg-color);
|
| 236 |
+
border-radius: var(--inner-border-radius);
|
| 237 |
+
padding: 20px;
|
| 238 |
+
border: 1px solid var(--border-color);
|
| 239 |
+
transition: background-color 0.3s, border-color 0.3s;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
.metric-number {
|
| 243 |
+
font-size: 1.8rem;
|
| 244 |
+
font-weight: 800;
|
| 245 |
+
color: var(--primary-accent);
|
| 246 |
+
margin-bottom: 6px;
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
.metric-label {
|
| 250 |
+
color: var(--text-muted);
|
| 251 |
+
font-size: 0.9rem;
|
| 252 |
+
line-height: 1.5;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/* DISCLAIMER */
|
| 256 |
+
.disclaimer {
|
| 257 |
+
margin-top: 28px;
|
| 258 |
+
background: rgba(245, 158, 11, 0.08);
|
| 259 |
+
border: 1px solid rgba(245, 158, 11, 0.3);
|
| 260 |
+
color: #d97706;
|
| 261 |
+
padding: 18px 20px;
|
| 262 |
+
border-radius: var(--inner-border-radius);
|
| 263 |
+
line-height: 1.8;
|
| 264 |
+
font-size: 0.92rem;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
/* BUTTONS */
|
| 268 |
+
.btn-group {
|
| 269 |
+
display: flex;
|
| 270 |
+
gap: 20px;
|
| 271 |
+
justify-content: center;
|
| 272 |
+
margin-top: 42px;
|
| 273 |
+
flex-wrap: wrap;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
.btn {
|
| 277 |
+
text-decoration: none;
|
| 278 |
+
padding: 16px 30px;
|
| 279 |
+
border-radius: 16px;
|
| 280 |
+
font-weight: 700;
|
| 281 |
+
font-size: 0.97rem;
|
| 282 |
+
transition: all 0.25s ease;
|
| 283 |
+
display: inline-flex;
|
| 284 |
+
align-items: center;
|
| 285 |
+
justify-content: center;
|
| 286 |
+
min-width: 230px;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
.btn-primary {
|
| 290 |
+
background: linear-gradient(to right, var(--primary-accent), var(--primary-hover));
|
| 291 |
+
color: white;
|
| 292 |
+
box-shadow: 0 12px 25px rgba(124, 58, 237, 0.2);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
.btn-primary:hover {
|
| 296 |
+
transform: translateY(-2px);
|
| 297 |
+
box-shadow: 0 18px 30px rgba(124, 58, 237, 0.25);
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.btn-secondary {
|
| 301 |
+
background: var(--card-bg);
|
| 302 |
+
color: var(--text-main);
|
| 303 |
+
border: 1px solid var(--border-color);
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
.btn-secondary:hover {
|
| 307 |
+
transform: translateY(-2px);
|
| 308 |
+
background: var(--bg-color);
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
/* FOOTER */
|
| 312 |
+
.footer {
|
| 313 |
+
text-align: center;
|
| 314 |
+
margin-top: 45px;
|
| 315 |
+
color: var(--text-muted);
|
| 316 |
+
font-size: 0.9rem;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
/* ANIMATION */
|
| 320 |
+
@keyframes fadeIn {
|
| 321 |
+
from {
|
| 322 |
+
opacity: 0;
|
| 323 |
+
transform: translateY(12px);
|
| 324 |
+
}
|
| 325 |
+
to {
|
| 326 |
+
opacity: 1;
|
| 327 |
+
transform: translateY(0);
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
/* RESPONSIVE */
|
| 332 |
+
@media (max-width: 900px) {
|
| 333 |
+
.content-grid {
|
| 334 |
+
grid-template-columns: 1fr;
|
| 335 |
+
}
|
| 336 |
+
h1 {
|
| 337 |
+
font-size: 2.3rem;
|
| 338 |
+
}
|
| 339 |
+
.hero-container {
|
| 340 |
+
padding: 35px;
|
| 341 |
+
}
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
@media (max-width: 600px) {
|
| 345 |
+
.metrics-grid {
|
| 346 |
+
grid-template-columns: 1fr;
|
| 347 |
+
}
|
| 348 |
+
.btn {
|
| 349 |
+
width: 100%;
|
| 350 |
+
}
|
| 351 |
+
.hero-container {
|
| 352 |
+
padding: 28px;
|
| 353 |
+
}
|
| 354 |
+
h1 {
|
| 355 |
+
font-size: 2rem;
|
| 356 |
+
}
|
| 357 |
+
}
|
| 358 |
+
</style>
|
| 359 |
+
</head>
|
| 360 |
+
<body>
|
| 361 |
+
|
| 362 |
+
<div class="hero-container">
|
| 363 |
+
|
| 364 |
+
<!-- Theme Toggle Button -->
|
| 365 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 366 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 367 |
+
</button>
|
| 368 |
+
|
| 369 |
+
<!-- HERO -->
|
| 370 |
+
<div class="hero-top">
|
| 371 |
+
<div class="hero-badge">
|
| 372 |
+
AI-Driven Telecom Intelligence Platform
|
| 373 |
+
</div>
|
| 374 |
+
<h1>
|
| 375 |
+
Customer Churn Predictor
|
| 376 |
+
</h1>
|
| 377 |
+
<p class="hero-description">
|
| 378 |
+
Advanced machine learning system designed to estimate customer churn risk patterns using customer metadata, active service subscriptions, contract types, and billing history.
|
| 379 |
+
</p>
|
| 380 |
+
</div>
|
| 381 |
+
|
| 382 |
+
<!-- CONTENT GRID -->
|
| 383 |
+
<div class="content-grid">
|
| 384 |
+
|
| 385 |
+
<!-- LEFT CARD -->
|
| 386 |
+
<div class="card">
|
| 387 |
+
<div class="card-title">
|
| 388 |
+
<i class="fa-solid fa-chart-line"></i> Platform Overview
|
| 389 |
+
</div>
|
| 390 |
+
<p>
|
| 391 |
+
This platform was developed to analyze customer profiles and identify patterns associated with churn behavior. The prediction pipeline combines statistical learning, feature preprocessing, and subscription activity indicators to estimate customer churn probability.
|
| 392 |
+
</p>
|
| 393 |
+
|
| 394 |
+
<div class="feature-list">
|
| 395 |
+
<div class="feature-item">
|
| 396 |
+
<strong>XGBoost Classifier</strong>
|
| 397 |
+
<span>
|
| 398 |
+
Gradient boosted decision trees optimized for high classification performance, class imbalance, and stable prediction behavior.
|
| 399 |
+
</span>
|
| 400 |
+
</div>
|
| 401 |
+
|
| 402 |
+
<div class="feature-item">
|
| 403 |
+
<strong>GridSearchCV Optimization</strong>
|
| 404 |
+
<span>
|
| 405 |
+
Exhaustive hyperparameter tuning performed to maximize model ROC AUC score and ensure robust generalization across demographic segments.
|
| 406 |
+
</span>
|
| 407 |
+
</div>
|
| 408 |
+
|
| 409 |
+
<div class="feature-item">
|
| 410 |
+
<strong>Telecom Feature Engineering</strong>
|
| 411 |
+
<span>
|
| 412 |
+
One-hot categorical encodings, standardized numerical billing features, product usage counts, and integration risk levels built into the pipeline.
|
| 413 |
+
</span>
|
| 414 |
+
</div>
|
| 415 |
+
</div>
|
| 416 |
+
|
| 417 |
+
<div class="highlight-box">
|
| 418 |
+
<div class="highlight-title">Optimization Overview</div>
|
| 419 |
+
<p>
|
| 420 |
+
This version utilizes <strong>XGBoost</strong> to achieve high classification power on structured customer data. Hyperparameters (learning rate, max depth, estimators) were tuned using <strong>GridSearchCV</strong> to maximize predictive reliability and precision-recall trade-offs.
|
| 421 |
+
</p>
|
| 422 |
+
</div>
|
| 423 |
+
</div>
|
| 424 |
+
|
| 425 |
+
<!-- RIGHT CARD -->
|
| 426 |
+
<div class="card">
|
| 427 |
+
<div class="card-title">
|
| 428 |
+
<i class="fa-solid fa-gears"></i> System Capabilities
|
| 429 |
+
</div>
|
| 430 |
+
<p>
|
| 431 |
+
The model evaluates customer profiles using a combination of customer tenure, service subscriptions, charges, payment history, and contract parameters.
|
| 432 |
+
</p>
|
| 433 |
+
|
| 434 |
+
<div class="metrics-grid">
|
| 435 |
+
<div class="metric-card" style="grid-row: span 2; display: flex; flex-direction: column; justify-content: center;">
|
| 436 |
+
<div class="metric-number" style="font-size: 1.45rem; line-height: 1.35; margin-bottom: 8px;">82% Overall Accuracy<br>& 75% Churn Recall</div>
|
| 437 |
+
<div class="metric-label">
|
| 438 |
+
Optimized XGBoost model performance showcasing overall accuracy and recall for churn detection
|
| 439 |
+
</div>
|
| 440 |
+
</div>
|
| 441 |
+
|
| 442 |
+
<div class="metric-card">
|
| 443 |
+
<div class="metric-number">XGB</div>
|
| 444 |
+
<div class="metric-label">
|
| 445 |
+
Extreme Gradient Boosting used for predictions
|
| 446 |
+
</div>
|
| 447 |
+
</div>
|
| 448 |
+
|
| 449 |
+
<div class="metric-card">
|
| 450 |
+
<div class="metric-number">ML</div>
|
| 451 |
+
<div class="metric-label">
|
| 452 |
+
Machine learning pipeline with preprocessing integration
|
| 453 |
+
</div>
|
| 454 |
+
</div>
|
| 455 |
+
</div>
|
| 456 |
+
|
| 457 |
+
<div class="disclaimer">
|
| 458 |
+
<strong>Analytical Disclaimer:</strong>
|
| 459 |
+
This platform is intended for analytical and research demonstration purposes. Predictions generated by the system should be used in combination with qualitative customer insights and business policy.
|
| 460 |
+
</div>
|
| 461 |
+
</div>
|
| 462 |
+
|
| 463 |
+
</div>
|
| 464 |
+
|
| 465 |
+
<!-- BUTTONS -->
|
| 466 |
+
<div class="btn-group">
|
| 467 |
+
<a href="{{ url_for('predict_model') }}" class="btn btn-primary">
|
| 468 |
+
<i class="fa-solid fa-gauge-high"></i> Launch Prediction Tool
|
| 469 |
+
</a>
|
| 470 |
+
<a href="{{ url_for('documentation') }}" class="btn btn-secondary">
|
| 471 |
+
<i class="fa-solid fa-book"></i> View Model Documentation
|
| 472 |
+
</a>
|
| 473 |
+
</div>
|
| 474 |
+
|
| 475 |
+
<!-- FOOTER -->
|
| 476 |
+
<div class="footer">
|
| 477 |
+
Customer Churn Predictor • Powered by XGBoost Classifier, Feature Engineering, and GridSearchCV
|
| 478 |
+
</div>
|
| 479 |
+
|
| 480 |
+
</div>
|
| 481 |
+
|
| 482 |
+
<script>
|
| 483 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 484 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 485 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 486 |
+
updateThemeUI(savedTheme);
|
| 487 |
+
});
|
| 488 |
+
|
| 489 |
+
function toggleTheme() {
|
| 490 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 491 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 492 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 493 |
+
localStorage.setItem('theme', newTheme);
|
| 494 |
+
updateThemeUI(newTheme);
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
function updateThemeUI(theme) {
|
| 498 |
+
const btnText = document.getElementById('themeToggleText');
|
| 499 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 500 |
+
if (theme === 'dark') {
|
| 501 |
+
btnText.textContent = 'Light Mode';
|
| 502 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 503 |
+
} else {
|
| 504 |
+
btnText.textContent = 'Dark Mode';
|
| 505 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 506 |
+
}
|
| 507 |
+
}
|
| 508 |
+
</script>
|
| 509 |
+
</body>
|
| 510 |
+
</html>
|
templates/predict.html
ADDED
|
@@ -0,0 +1,855 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Prediction Portal</title>
|
| 7 |
+
<meta name="description" content="Predict customer churn probability using advanced machine learning models based on telecom profile characteristics.">
|
| 8 |
+
|
| 9 |
+
<!-- Modern Google Fonts -->
|
| 10 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 11 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 12 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 13 |
+
|
| 14 |
+
<!-- Font Awesome Icons for premium look -->
|
| 15 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 16 |
+
|
| 17 |
+
<style>
|
| 18 |
+
/* Styling variables and custom design tokens */
|
| 19 |
+
:root {
|
| 20 |
+
--bg-color: #f8fafc; /* Slate 50 - Light, clean background */
|
| 21 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 22 |
+
--text-main: #0f172a; /* Slate 900 - Deep, readable text */
|
| 23 |
+
--text-muted: #64748b; /* Slate 500 - For labels and placeholders */
|
| 24 |
+
--border-color: #cbd5e1; /* Slate 300 - Subtle input borders */
|
| 25 |
+
--primary-accent: #2563eb; /* Blue 600 - Focus state & primary buttons */
|
| 26 |
+
--primary-hover: #1d4ed8; /* Blue 700 */
|
| 27 |
+
--font-family: 'Inter', sans-serif;
|
| 28 |
+
--card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05), 0 20px 25px -5px rgb(0 0 0 / 0.03);
|
| 29 |
+
--card-border-radius: 12px;
|
| 30 |
+
|
| 31 |
+
/* Auxiliary colors for status and categories */
|
| 32 |
+
--success: #10b981;
|
| 33 |
+
--warning: #f59e0b;
|
| 34 |
+
--danger: #ef4444;
|
| 35 |
+
--gradient-start: #1e3a8a;
|
| 36 |
+
--gradient-end: #3b82f6;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/* Dark Mode variables */
|
| 40 |
+
[data-theme="dark"] {
|
| 41 |
+
--bg-color: #0f172a;
|
| 42 |
+
--card-bg: #1e293b;
|
| 43 |
+
--text-main: #f8fafc;
|
| 44 |
+
--text-muted: #94a3b8;
|
| 45 |
+
--border-color: #334155;
|
| 46 |
+
--primary-accent: #3b82f6;
|
| 47 |
+
--primary-hover: #60a5fa;
|
| 48 |
+
--card-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3);
|
| 49 |
+
--gradient-start: #0f172a;
|
| 50 |
+
--gradient-end: #1e3a8a;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
* {
|
| 54 |
+
box-sizing: border-box;
|
| 55 |
+
margin: 0;
|
| 56 |
+
padding: 0;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
body {
|
| 60 |
+
font-family: var(--font-family);
|
| 61 |
+
background-color: var(--bg-color);
|
| 62 |
+
color: var(--text-main);
|
| 63 |
+
min-height: 100vh;
|
| 64 |
+
display: flex;
|
| 65 |
+
flex-direction: column;
|
| 66 |
+
transition: background-color 0.3s, color 0.3s;
|
| 67 |
+
line-height: 1.5;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/* Header styling with smooth gradient */
|
| 71 |
+
header {
|
| 72 |
+
background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%);
|
| 73 |
+
color: #ffffff;
|
| 74 |
+
padding: 2.5rem 1.5rem;
|
| 75 |
+
text-align: center;
|
| 76 |
+
position: relative;
|
| 77 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.header-content {
|
| 81 |
+
max-width: 1000px;
|
| 82 |
+
margin: 0 auto;
|
| 83 |
+
position: relative;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
header h1 {
|
| 87 |
+
font-size: 2.25rem;
|
| 88 |
+
font-weight: 700;
|
| 89 |
+
margin-bottom: 0.5rem;
|
| 90 |
+
letter-spacing: -0.025em;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
header p {
|
| 94 |
+
font-size: 1rem;
|
| 95 |
+
opacity: 0.9;
|
| 96 |
+
max-width: 600px;
|
| 97 |
+
margin: 0 auto;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.theme-toggle-btn {
|
| 101 |
+
position: absolute;
|
| 102 |
+
top: 10px;
|
| 103 |
+
right: 20px;
|
| 104 |
+
background: rgba(255, 255, 255, 0.15);
|
| 105 |
+
border: none;
|
| 106 |
+
color: #ffffff;
|
| 107 |
+
padding: 8px 12px;
|
| 108 |
+
border-radius: 20px;
|
| 109 |
+
cursor: pointer;
|
| 110 |
+
font-size: 0.9rem;
|
| 111 |
+
font-weight: 500;
|
| 112 |
+
display: flex;
|
| 113 |
+
align-items: center;
|
| 114 |
+
gap: 6px;
|
| 115 |
+
transition: background 0.2s;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.theme-toggle-btn:hover {
|
| 119 |
+
background: rgba(255, 255, 255, 0.25);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
/* Main layout container */
|
| 123 |
+
main {
|
| 124 |
+
flex: 1;
|
| 125 |
+
max-width: 1200px;
|
| 126 |
+
width: 100%;
|
| 127 |
+
margin: 0 auto;
|
| 128 |
+
padding: 2rem 1.5rem;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
/* Prediction Result Banner if present */
|
| 132 |
+
.result-card {
|
| 133 |
+
background-color: var(--card-bg);
|
| 134 |
+
border-radius: var(--card-border-radius);
|
| 135 |
+
box-shadow: var(--card-shadow);
|
| 136 |
+
border-left: 6px solid var(--primary-accent);
|
| 137 |
+
padding: 1.5rem;
|
| 138 |
+
margin-top: 2rem;
|
| 139 |
+
display: flex;
|
| 140 |
+
align-items: center;
|
| 141 |
+
gap: 1.5rem;
|
| 142 |
+
animation: slideDown 0.4s ease-out;
|
| 143 |
+
border-top: 1px solid var(--border-color);
|
| 144 |
+
border-right: 1px solid var(--border-color);
|
| 145 |
+
border-bottom: 1px solid var(--border-color);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.result-card.churn-high {
|
| 149 |
+
border-left-color: var(--danger);
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
.result-card.churn-low {
|
| 153 |
+
border-left-color: var(--success);
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.result-icon {
|
| 157 |
+
font-size: 2.5rem;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.churn-high .result-icon { color: var(--danger); }
|
| 161 |
+
.churn-low .result-icon { color: var(--success); }
|
| 162 |
+
|
| 163 |
+
.result-info h3 {
|
| 164 |
+
font-size: 1.25rem;
|
| 165 |
+
font-weight: 600;
|
| 166 |
+
margin-bottom: 0.25rem;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.result-info p {
|
| 170 |
+
color: var(--text-muted);
|
| 171 |
+
font-size: 0.95rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.result-score {
|
| 175 |
+
margin-left: auto;
|
| 176 |
+
text-align: right;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.result-score .percentage {
|
| 180 |
+
font-size: 2rem;
|
| 181 |
+
font-weight: 700;
|
| 182 |
+
color: var(--text-main);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.result-score .label {
|
| 186 |
+
font-size: 0.75rem;
|
| 187 |
+
color: var(--text-muted);
|
| 188 |
+
text-transform: uppercase;
|
| 189 |
+
letter-spacing: 0.05em;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/* Grid Layout for Form Card and Controls */
|
| 193 |
+
.form-layout {
|
| 194 |
+
display: flex;
|
| 195 |
+
flex-direction: column;
|
| 196 |
+
gap: 2rem;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
/* Card Panels representing form sections */
|
| 200 |
+
.section-card {
|
| 201 |
+
background-color: var(--card-bg);
|
| 202 |
+
border-radius: var(--card-border-radius);
|
| 203 |
+
box-shadow: var(--card-shadow);
|
| 204 |
+
padding: 2rem;
|
| 205 |
+
border: 1px solid var(--border-color);
|
| 206 |
+
transition: background-color 0.3s, border-color 0.3s, transform 0.2s;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.section-card:hover {
|
| 210 |
+
transform: translateY(-2px);
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.section-title {
|
| 214 |
+
font-size: 1.25rem;
|
| 215 |
+
font-weight: 600;
|
| 216 |
+
margin-bottom: 1.5rem;
|
| 217 |
+
display: flex;
|
| 218 |
+
align-items: center;
|
| 219 |
+
gap: 10px;
|
| 220 |
+
border-bottom: 2px solid var(--bg-color);
|
| 221 |
+
padding-bottom: 0.75rem;
|
| 222 |
+
color: var(--primary-accent);
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.grid-container {
|
| 226 |
+
display: grid;
|
| 227 |
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
| 228 |
+
gap: 1.5rem;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
/* Form Fields Styling */
|
| 232 |
+
.form-group {
|
| 233 |
+
display: flex;
|
| 234 |
+
flex-direction: column;
|
| 235 |
+
gap: 0.5rem;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
label {
|
| 239 |
+
font-weight: 500;
|
| 240 |
+
font-size: 0.9rem;
|
| 241 |
+
color: var(--text-main);
|
| 242 |
+
display: flex;
|
| 243 |
+
align-items: center;
|
| 244 |
+
gap: 6px;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.field-desc {
|
| 248 |
+
font-size: 0.75rem;
|
| 249 |
+
color: var(--text-muted);
|
| 250 |
+
margin-top: -0.25rem;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
input[type="text"],
|
| 254 |
+
input[type="number"],
|
| 255 |
+
select {
|
| 256 |
+
font-family: var(--font-family);
|
| 257 |
+
color: var(--text-main);
|
| 258 |
+
background-color: var(--card-bg);
|
| 259 |
+
border: 1px solid var(--border-color);
|
| 260 |
+
border-radius: 6px;
|
| 261 |
+
padding: 10px 14px;
|
| 262 |
+
font-size: 0.95rem;
|
| 263 |
+
width: 100%;
|
| 264 |
+
transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
input[type="text"]:focus,
|
| 268 |
+
input[type="number"]:focus,
|
| 269 |
+
select:focus {
|
| 270 |
+
border-color: var(--primary-accent);
|
| 271 |
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
|
| 272 |
+
outline: none;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
/* Form action buttons */
|
| 276 |
+
.form-actions {
|
| 277 |
+
display: flex;
|
| 278 |
+
justify-content: space-between;
|
| 279 |
+
align-items: center;
|
| 280 |
+
width: 100%;
|
| 281 |
+
gap: 1rem;
|
| 282 |
+
margin-top: 1.5rem;
|
| 283 |
+
flex-wrap: wrap;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
.form-actions .nav-group,
|
| 287 |
+
.form-actions .action-group {
|
| 288 |
+
display: flex;
|
| 289 |
+
gap: 1rem;
|
| 290 |
+
flex-wrap: wrap;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.btn {
|
| 294 |
+
font-family: var(--font-family);
|
| 295 |
+
font-size: 0.95rem;
|
| 296 |
+
font-weight: 600;
|
| 297 |
+
padding: 12px 24px;
|
| 298 |
+
border-radius: 6px;
|
| 299 |
+
cursor: pointer;
|
| 300 |
+
border: none;
|
| 301 |
+
transition: all 0.2s;
|
| 302 |
+
display: inline-flex;
|
| 303 |
+
align-items: center;
|
| 304 |
+
gap: 8px;
|
| 305 |
+
text-decoration: none;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.btn-secondary {
|
| 309 |
+
background-color: var(--card-bg);
|
| 310 |
+
color: var(--text-main);
|
| 311 |
+
border: 1px solid var(--border-color);
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
.btn-secondary:hover {
|
| 315 |
+
background-color: var(--bg-color);
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.btn-primary {
|
| 319 |
+
background-color: var(--primary-accent);
|
| 320 |
+
color: #ffffff;
|
| 321 |
+
box-shadow: 0 2px 4px rgba(37, 99, 235, 0.2);
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.btn-primary:hover {
|
| 325 |
+
background-color: var(--primary-hover);
|
| 326 |
+
transform: translateY(-1px);
|
| 327 |
+
box-shadow: 0 4px 6px rgba(37, 99, 235, 0.3);
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
.btn-primary:active {
|
| 331 |
+
transform: translateY(0);
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
.btn-warning {
|
| 335 |
+
background-color: var(--warning);
|
| 336 |
+
color: #ffffff;
|
| 337 |
+
box-shadow: 0 2px 4px rgba(245, 158, 11, 0.2);
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
.btn-warning:hover {
|
| 341 |
+
background-color: #d97706;
|
| 342 |
+
transform: translateY(-1px);
|
| 343 |
+
box-shadow: 0 4px 6px rgba(245, 158, 11, 0.3);
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
footer {
|
| 347 |
+
text-align: center;
|
| 348 |
+
padding: 2rem 1.5rem;
|
| 349 |
+
color: var(--text-muted);
|
| 350 |
+
font-size: 0.85rem;
|
| 351 |
+
border-top: 1px solid var(--border-color);
|
| 352 |
+
margin-top: auto;
|
| 353 |
+
background-color: var(--card-bg);
|
| 354 |
+
transition: background-color 0.3s, border-top 0.3s;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
/* Animations */
|
| 358 |
+
@keyframes slideDown {
|
| 359 |
+
from {
|
| 360 |
+
opacity: 0;
|
| 361 |
+
transform: translateY(-15px);
|
| 362 |
+
}
|
| 363 |
+
to {
|
| 364 |
+
opacity: 1;
|
| 365 |
+
transform: translateY(0);
|
| 366 |
+
}
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
/* Responsive adjustments */
|
| 370 |
+
@media (max-width: 768px) {
|
| 371 |
+
.grid-container {
|
| 372 |
+
grid-template-columns: 1fr;
|
| 373 |
+
}
|
| 374 |
+
header h1 {
|
| 375 |
+
font-size: 1.75rem;
|
| 376 |
+
}
|
| 377 |
+
main {
|
| 378 |
+
padding: 1rem;
|
| 379 |
+
}
|
| 380 |
+
.form-actions {
|
| 381 |
+
flex-direction: column-reverse;
|
| 382 |
+
align-items: stretch;
|
| 383 |
+
}
|
| 384 |
+
.form-actions .nav-group,
|
| 385 |
+
.form-actions .action-group {
|
| 386 |
+
flex-direction: column;
|
| 387 |
+
width: 100%;
|
| 388 |
+
}
|
| 389 |
+
.btn {
|
| 390 |
+
width: 100%;
|
| 391 |
+
justify-content: center;
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
</style>
|
| 395 |
+
</head>
|
| 396 |
+
<body>
|
| 397 |
+
|
| 398 |
+
<header>
|
| 399 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 400 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 401 |
+
</button>
|
| 402 |
+
<div class="header-content">
|
| 403 |
+
<h1><i class="fa-solid fa-chart-line"></i> Customer Churn Analyzer</h1>
|
| 404 |
+
<p>Configure a customer profile below to estimate the likelihood of churn using our optimized XGBoost prediction engine.</p>
|
| 405 |
+
</div>
|
| 406 |
+
</header>
|
| 407 |
+
|
| 408 |
+
<main>
|
| 409 |
+
<form id="churnForm">
|
| 410 |
+
<div class="form-layout">
|
| 411 |
+
|
| 412 |
+
<!-- Section 1: Customer Profile -->
|
| 413 |
+
<section class="section-card">
|
| 414 |
+
<h2 class="section-title"><i class="fa-solid fa-user-tie"></i> Customer Profile & Demographics</h2>
|
| 415 |
+
<div class="grid-container">
|
| 416 |
+
|
| 417 |
+
<div class="form-group">
|
| 418 |
+
<label for="gender"><i class="fa-solid fa-venus-mars"></i> Gender</label>
|
| 419 |
+
<select name="gender" id="gender" required>
|
| 420 |
+
<option value="0">Female</option>
|
| 421 |
+
<option value="1">Male</option>
|
| 422 |
+
</select>
|
| 423 |
+
<span class="field-desc">Binary representation (Female=0, Male=1)</span>
|
| 424 |
+
</div>
|
| 425 |
+
|
| 426 |
+
<div class="form-group">
|
| 427 |
+
<label for="SeniorCitizen"><i class="fa-solid fa-person-cane"></i> Senior Citizen</label>
|
| 428 |
+
<select name="SeniorCitizen" id="SeniorCitizen" required>
|
| 429 |
+
<option value="0">No</option>
|
| 430 |
+
<option value="1">Yes</option>
|
| 431 |
+
</select>
|
| 432 |
+
<span class="field-desc">Is the customer 65 years or older?</span>
|
| 433 |
+
</div>
|
| 434 |
+
|
| 435 |
+
<div class="form-group">
|
| 436 |
+
<label for="Partner"><i class="fa-solid fa-ring"></i> Partner Status</label>
|
| 437 |
+
<select name="Partner" id="Partner" required>
|
| 438 |
+
<option value="No">No Partner</option>
|
| 439 |
+
<option value="Yes">Has Partner</option>
|
| 440 |
+
</select>
|
| 441 |
+
<span class="field-desc">Primary demographic status flag</span>
|
| 442 |
+
</div>
|
| 443 |
+
|
| 444 |
+
<div class="form-group">
|
| 445 |
+
<label for="Dependents"><i class="fa-solid fa-children"></i> Dependents Status</label>
|
| 446 |
+
<select name="Dependents" id="Dependents" required>
|
| 447 |
+
<option value="No">No Dependents</option>
|
| 448 |
+
<option value="Yes">Has Dependents</option>
|
| 449 |
+
</select>
|
| 450 |
+
<span class="field-desc">Dependent children or relatives</span>
|
| 451 |
+
</div>
|
| 452 |
+
|
| 453 |
+
</div>
|
| 454 |
+
</section>
|
| 455 |
+
|
| 456 |
+
<!-- Section 2: Core Phone & Internet Services -->
|
| 457 |
+
<section class="section-card">
|
| 458 |
+
<h2 class="section-title"><i class="fa-solid fa-network-wired"></i> Core Telecom Services</h2>
|
| 459 |
+
<div class="grid-container">
|
| 460 |
+
|
| 461 |
+
<div class="form-group">
|
| 462 |
+
<label for="PhoneService"><i class="fa-solid fa-phone"></i> Phone Service</label>
|
| 463 |
+
<select name="PhoneService" id="PhoneService" required>
|
| 464 |
+
<option value="Yes">Yes</option>
|
| 465 |
+
<option value="No">No</option>
|
| 466 |
+
</select>
|
| 467 |
+
<span class="field-desc">Subscribes to standard voice line</span>
|
| 468 |
+
</div>
|
| 469 |
+
|
| 470 |
+
<div class="form-group">
|
| 471 |
+
<label for="MultipleLines"><i class="fa-solid fa-phone-volume"></i> Multiple Lines</label>
|
| 472 |
+
<select name="MultipleLines" id="MultipleLines" required>
|
| 473 |
+
<option value="No">No</option>
|
| 474 |
+
<option value="Yes">Yes</option>
|
| 475 |
+
<option value="No phone service">No phone service</option>
|
| 476 |
+
</select>
|
| 477 |
+
<span class="field-desc">Multiple telephone numbers active</span>
|
| 478 |
+
</div>
|
| 479 |
+
|
| 480 |
+
<div class="form-group">
|
| 481 |
+
<label for="InternetService"><i class="fa-solid fa-wifi"></i> Internet Service Type</label>
|
| 482 |
+
<select name="InternetService" id="InternetService" required>
|
| 483 |
+
<option value="DSL">DSL</option>
|
| 484 |
+
<option value="Fiber optic">Fiber optic</option>
|
| 485 |
+
<option value="No">No Internet Service</option>
|
| 486 |
+
</select>
|
| 487 |
+
<span class="field-desc">Technology type</span>
|
| 488 |
+
</div>
|
| 489 |
+
|
| 490 |
+
</div>
|
| 491 |
+
</section>
|
| 492 |
+
|
| 493 |
+
<!-- Section 3: Premium Add-ons & Streaming -->
|
| 494 |
+
<section class="section-card">
|
| 495 |
+
<h2 class="section-title"><i class="fa-solid fa-cubes"></i> Service Add-Ons & Streaming</h2>
|
| 496 |
+
<div class="grid-container">
|
| 497 |
+
|
| 498 |
+
<div class="form-group">
|
| 499 |
+
<label for="OnlineSecurity"><i class="fa-solid fa-shield-halved"></i> Online Security</label>
|
| 500 |
+
<select name="OnlineSecurity" id="OnlineSecurity" required>
|
| 501 |
+
<option value="No">No</option>
|
| 502 |
+
<option value="Yes">Yes</option>
|
| 503 |
+
<option value="No internet service">No internet service</option>
|
| 504 |
+
</select>
|
| 505 |
+
<span class="field-desc">Antivirus & firewall bundle</span>
|
| 506 |
+
</div>
|
| 507 |
+
|
| 508 |
+
<div class="form-group">
|
| 509 |
+
<label for="OnlineBackup"><i class="fa-solid fa-cloud-arrow-up"></i> Online Backup</label>
|
| 510 |
+
<select name="OnlineBackup" id="OnlineBackup" required>
|
| 511 |
+
<option value="No">No</option>
|
| 512 |
+
<option value="Yes">Yes</option>
|
| 513 |
+
<option value="No internet service">No internet service</option>
|
| 514 |
+
</select>
|
| 515 |
+
<span class="field-desc">Cloud backup storage</span>
|
| 516 |
+
</div>
|
| 517 |
+
|
| 518 |
+
<div class="form-group">
|
| 519 |
+
<label for="DeviceProtection"><i class="fa-solid fa-laptop-medical"></i> Device Protection</label>
|
| 520 |
+
<select name="DeviceProtection" id="DeviceProtection" required>
|
| 521 |
+
<option value="No">No</option>
|
| 522 |
+
<option value="Yes">Yes</option>
|
| 523 |
+
<option value="No internet service">No internet service</option>
|
| 524 |
+
</select>
|
| 525 |
+
<span class="field-desc">Accidental damage coverage</span>
|
| 526 |
+
</div>
|
| 527 |
+
|
| 528 |
+
<div class="form-group">
|
| 529 |
+
<label for="TechSupport"><i class="fa-solid fa-user-gear"></i> Tech Support</label>
|
| 530 |
+
<select name="TechSupport" id="TechSupport" required>
|
| 531 |
+
<option value="No">No</option>
|
| 532 |
+
<option value="Yes">Yes</option>
|
| 533 |
+
<option value="No internet service">No internet service</option>
|
| 534 |
+
</select>
|
| 535 |
+
<span class="field-desc">Priority assistance plan</span>
|
| 536 |
+
</div>
|
| 537 |
+
|
| 538 |
+
<div class="form-group">
|
| 539 |
+
<label for="StreamingTV"><i class="fa-solid fa-tv"></i> Streaming TV</label>
|
| 540 |
+
<select name="StreamingTV" id="StreamingTV" required>
|
| 541 |
+
<option value="No">No</option>
|
| 542 |
+
<option value="Yes">Yes</option>
|
| 543 |
+
<option value="No internet service">No internet service</option>
|
| 544 |
+
</select>
|
| 545 |
+
<span class="field-desc">Live TV channels package</span>
|
| 546 |
+
</div>
|
| 547 |
+
|
| 548 |
+
<div class="form-group">
|
| 549 |
+
<label for="StreamingMovies"><i class="fa-solid fa-film"></i> Streaming Movies</label>
|
| 550 |
+
<select name="StreamingMovies" id="StreamingMovies" required>
|
| 551 |
+
<option value="No">No</option>
|
| 552 |
+
<option value="Yes">Yes</option>
|
| 553 |
+
<option value="No internet service">No internet service</option>
|
| 554 |
+
</select>
|
| 555 |
+
<span class="field-desc">Movie library subscription</span>
|
| 556 |
+
</div>
|
| 557 |
+
|
| 558 |
+
</div>
|
| 559 |
+
</section>
|
| 560 |
+
|
| 561 |
+
<!-- Section 4: Contract & Financials -->
|
| 562 |
+
<section class="section-card">
|
| 563 |
+
<h2 class="section-title"><i class="fa-solid fa-file-invoice-dollar"></i> Contract & Billing Details</h2>
|
| 564 |
+
<div class="grid-container">
|
| 565 |
+
|
| 566 |
+
<div class="form-group">
|
| 567 |
+
<label for="Contract"><i class="fa-solid fa-file-contract"></i> Contract Type</label>
|
| 568 |
+
<select name="Contract" id="Contract" required>
|
| 569 |
+
<option value="Month-to-month">Month-to-month</option>
|
| 570 |
+
<option value="One year">One year</option>
|
| 571 |
+
<option value="Two year">Two year</option>
|
| 572 |
+
</select>
|
| 573 |
+
<span class="field-desc">Billing term structure</span>
|
| 574 |
+
</div>
|
| 575 |
+
|
| 576 |
+
<div class="form-group">
|
| 577 |
+
<label for="PaperlessBilling"><i class="fa-solid fa-envelope-open-text"></i> Paperless Billing</label>
|
| 578 |
+
<select name="PaperlessBilling" id="PaperlessBilling" required>
|
| 579 |
+
<option value="Yes">Yes</option>
|
| 580 |
+
<option value="No">No</option>
|
| 581 |
+
</select>
|
| 582 |
+
<span class="field-desc">Invoice delivery preference</span>
|
| 583 |
+
</div>
|
| 584 |
+
|
| 585 |
+
<div class="form-group">
|
| 586 |
+
<label for="PaymentMethod"><i class="fa-solid fa-credit-card"></i> Payment Method</label>
|
| 587 |
+
<select name="PaymentMethod" id="PaymentMethod" required>
|
| 588 |
+
<option value="Electronic check">Electronic check</option>
|
| 589 |
+
<option value="Mailed check">Mailed check</option>
|
| 590 |
+
<option value="Bank transfer (automatic)">Bank transfer (automatic)</option>
|
| 591 |
+
<option value="Credit card (automatic)">Credit card (automatic)</option>
|
| 592 |
+
</select>
|
| 593 |
+
<span class="field-desc">Billing settlement channel</span>
|
| 594 |
+
</div>
|
| 595 |
+
|
| 596 |
+
<div class="form-group">
|
| 597 |
+
<label for="tenure"><i class="fa-solid fa-hourglass-half"></i> Tenure (Months)</label>
|
| 598 |
+
<input type="number" name="tenure" id="tenure" min="0" max="72" placeholder="e.g. 12" required>
|
| 599 |
+
<span class="field-desc">Months customer has stayed (0 to 72)</span>
|
| 600 |
+
</div>
|
| 601 |
+
|
| 602 |
+
<div class="form-group">
|
| 603 |
+
<label for="MonthlyCharges"><i class="fa-solid fa-sack-dollar"></i> Monthly Charges ($)</label>
|
| 604 |
+
<input type="number" name="MonthlyCharges" id="MonthlyCharges" min="0" max="150" step="0.01" placeholder="e.g. 64.85" required>
|
| 605 |
+
<span class="field-desc">Current monthly invoice price</span>
|
| 606 |
+
</div>
|
| 607 |
+
|
| 608 |
+
<div class="form-group">
|
| 609 |
+
<label for="TotalCharges"><i class="fa-solid fa-money-bill-wave"></i> Total Charges ($)</label>
|
| 610 |
+
<input type="number" name="TotalCharges" id="TotalCharges" min="0" max="10000" step="0.01" placeholder="e.g. 2292.15" required>
|
| 611 |
+
<span class="field-desc">Accrued bill amount across tenure</span>
|
| 612 |
+
</div>
|
| 613 |
+
|
| 614 |
+
</div>
|
| 615 |
+
</section>
|
| 616 |
+
|
| 617 |
+
<!-- Submit & Reset Actions -->
|
| 618 |
+
<div class="form-actions">
|
| 619 |
+
<div class="nav-group">
|
| 620 |
+
<a href="/" class="btn btn-secondary">
|
| 621 |
+
<i class="fa-solid fa-house"></i> Return to Home
|
| 622 |
+
</a>
|
| 623 |
+
<a href="/documentation" class="btn btn-secondary">
|
| 624 |
+
<i class="fa-solid fa-book"></i> View Documentation
|
| 625 |
+
</a>
|
| 626 |
+
</div>
|
| 627 |
+
<div class="action-group">
|
| 628 |
+
<button type="button" class="btn btn-warning" onclick="resetForm()">
|
| 629 |
+
<i class="fa-solid fa-arrow-rotate-left"></i> Reset Fields
|
| 630 |
+
</button>
|
| 631 |
+
<button type="submit" class="btn btn-primary">
|
| 632 |
+
<i class="fa-solid fa-circle-play"></i> Run Prediction Model
|
| 633 |
+
</button>
|
| 634 |
+
</div>
|
| 635 |
+
</div>
|
| 636 |
+
|
| 637 |
+
<!-- Result Container located at the bottom of the page, below the buttons -->
|
| 638 |
+
<div id="resultContainer"></div>
|
| 639 |
+
|
| 640 |
+
</div>
|
| 641 |
+
</form>
|
| 642 |
+
</main>
|
| 643 |
+
|
| 644 |
+
<footer>
|
| 645 |
+
<p>© 2026 Customer Churn Prediction Engine. All rights reserved. Powered by Flask, XGBoost & Scikit-Learn.</p>
|
| 646 |
+
</footer>
|
| 647 |
+
|
| 648 |
+
<script>
|
| 649 |
+
// Initialize Theme
|
| 650 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 651 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 652 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 653 |
+
updateThemeUI(savedTheme);
|
| 654 |
+
|
| 655 |
+
// Initialize service dependencies and constraints
|
| 656 |
+
checkServiceDependencies();
|
| 657 |
+
document.getElementById('PhoneService').addEventListener('change', checkServiceDependencies);
|
| 658 |
+
document.getElementById('InternetService').addEventListener('change', checkServiceDependencies);
|
| 659 |
+
|
| 660 |
+
// Handle asynchronous native form submission
|
| 661 |
+
document.getElementById('churnForm').addEventListener('submit', function(event) {
|
| 662 |
+
event.preventDefault(); // Intercept page navigation
|
| 663 |
+
|
| 664 |
+
const phoneService = document.getElementById('PhoneService').value;
|
| 665 |
+
const internetService = document.getElementById('InternetService').value;
|
| 666 |
+
|
| 667 |
+
// Double check: if both phone and internet are 'No', block submission
|
| 668 |
+
if (phoneService === 'No' && internetService === 'No') {
|
| 669 |
+
return; // Block submission
|
| 670 |
+
}
|
| 671 |
+
|
| 672 |
+
const formData = new FormData(this);
|
| 673 |
+
|
| 674 |
+
// Asynchronously post to prediction endpoint
|
| 675 |
+
fetch('/predict_model', {
|
| 676 |
+
method: 'POST',
|
| 677 |
+
body: formData
|
| 678 |
+
})
|
| 679 |
+
.then(response => {
|
| 680 |
+
if (!response.ok) {
|
| 681 |
+
throw new Error('Network response error');
|
| 682 |
+
}
|
| 683 |
+
return response.json();
|
| 684 |
+
})
|
| 685 |
+
.then(data => {
|
| 686 |
+
if (data.success) {
|
| 687 |
+
displayPredictionResult(data.prediction, data.churn_prob);
|
| 688 |
+
} else {
|
| 689 |
+
console.error('Prediction failed: ' + data.error);
|
| 690 |
+
}
|
| 691 |
+
})
|
| 692 |
+
.catch(error => {
|
| 693 |
+
console.warn('Backend server unavailable. Executing client-side model fallback...', error);
|
| 694 |
+
|
| 695 |
+
// Fallback mechanism if opened directly via filesystem (file:///C:/...)
|
| 696 |
+
if (window.location.protocol === 'file:') {
|
| 697 |
+
runClientSideMockPrediction();
|
| 698 |
+
}
|
| 699 |
+
});
|
| 700 |
+
});
|
| 701 |
+
});
|
| 702 |
+
|
| 703 |
+
// Theme toggle function
|
| 704 |
+
function toggleTheme() {
|
| 705 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 706 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 707 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 708 |
+
localStorage.setItem('theme', newTheme);
|
| 709 |
+
updateThemeUI(newTheme);
|
| 710 |
+
}
|
| 711 |
+
|
| 712 |
+
function updateThemeUI(theme) {
|
| 713 |
+
const btnText = document.getElementById('themeToggleText');
|
| 714 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 715 |
+
if (theme === 'dark') {
|
| 716 |
+
btnText.textContent = 'Light Mode';
|
| 717 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 718 |
+
} else {
|
| 719 |
+
btnText.textContent = 'Dark Mode';
|
| 720 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 721 |
+
}
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
// Dynamic UI constraints for phone and internet services selection
|
| 725 |
+
function checkServiceDependencies() {
|
| 726 |
+
const phoneService = document.getElementById('PhoneService').value;
|
| 727 |
+
const multipleLines = document.getElementById('MultipleLines');
|
| 728 |
+
const internetService = document.getElementById('InternetService').value;
|
| 729 |
+
const submitBtn = document.querySelector('button[type="submit"]');
|
| 730 |
+
const resultContainer = document.getElementById('resultContainer');
|
| 731 |
+
|
| 732 |
+
// 1. Phone Service -> Multiple Lines constraint
|
| 733 |
+
if (phoneService === 'No') {
|
| 734 |
+
multipleLines.value = 'No phone service';
|
| 735 |
+
multipleLines.disabled = true;
|
| 736 |
+
multipleLines.style.opacity = '0.6';
|
| 737 |
+
} else {
|
| 738 |
+
if (multipleLines.value === 'No phone service') {
|
| 739 |
+
multipleLines.value = 'No';
|
| 740 |
+
}
|
| 741 |
+
multipleLines.disabled = false;
|
| 742 |
+
multipleLines.style.opacity = '1';
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
// 2. Internet Service -> Add-ons constraints
|
| 746 |
+
const addons = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies'];
|
| 747 |
+
if (internetService === 'No') {
|
| 748 |
+
addons.forEach(id => {
|
| 749 |
+
const select = document.getElementById(id);
|
| 750 |
+
select.value = 'No internet service';
|
| 751 |
+
select.disabled = true;
|
| 752 |
+
select.style.opacity = '0.6';
|
| 753 |
+
});
|
| 754 |
+
} else {
|
| 755 |
+
addons.forEach(id => {
|
| 756 |
+
const select = document.getElementById(id);
|
| 757 |
+
select.disabled = false;
|
| 758 |
+
select.style.opacity = '1';
|
| 759 |
+
if (select.value === 'No internet service') {
|
| 760 |
+
select.value = 'No';
|
| 761 |
+
}
|
| 762 |
+
});
|
| 763 |
+
}
|
| 764 |
+
|
| 765 |
+
// 3. Dual service deactivation check (Phone Service = No & Internet Service = No)
|
| 766 |
+
if (phoneService === 'No' && internetService === 'No') {
|
| 767 |
+
submitBtn.disabled = true;
|
| 768 |
+
|
| 769 |
+
// Render a warning card below buttons
|
| 770 |
+
resultContainer.innerHTML = `
|
| 771 |
+
<div class="result-card churn-high" style="border-left-color: var(--warning); margin-top: 2rem;">
|
| 772 |
+
<div class="result-icon" style="color: var(--warning);">
|
| 773 |
+
<i class="fa-solid fa-triangle-exclamation"></i>
|
| 774 |
+
</div>
|
| 775 |
+
<div class="result-info">
|
| 776 |
+
<h3>Inactive Profile Status</h3>
|
| 777 |
+
<p>This customer has selected no services from the telecom provider (Phone and Internet are both deactivated). Churn prediction model cannot run on an inactive subscription.</p>
|
| 778 |
+
</div>
|
| 779 |
+
</div>
|
| 780 |
+
`;
|
| 781 |
+
resultContainer.scrollIntoView({ behavior: 'smooth' });
|
| 782 |
+
} else {
|
| 783 |
+
submitBtn.disabled = false;
|
| 784 |
+
// Clear message only if it was the subscription warning
|
| 785 |
+
if (resultContainer.innerHTML.includes('Inactive Profile Status')) {
|
| 786 |
+
resultContainer.innerHTML = '';
|
| 787 |
+
}
|
| 788 |
+
}
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
// Function to calculate a mock prediction if running offline / from file system
|
| 792 |
+
function runClientSideMockPrediction() {
|
| 793 |
+
const tenure = parseFloat(document.getElementById('tenure').value) || 0;
|
| 794 |
+
const monthly = parseFloat(document.getElementById('MonthlyCharges').value) || 0;
|
| 795 |
+
const contract = document.getElementById('Contract').value;
|
| 796 |
+
const security = document.getElementById('OnlineSecurity').value;
|
| 797 |
+
|
| 798 |
+
// Basic churn risk logic matching general trends
|
| 799 |
+
let riskFactor = 0.3;
|
| 800 |
+
|
| 801 |
+
// Monthly charges increase risk
|
| 802 |
+
riskFactor += (monthly / 150) * 0.3;
|
| 803 |
+
// Tenure reduces risk
|
| 804 |
+
riskFactor -= (tenure / 72) * 0.4;
|
| 805 |
+
// Month-to-month contracts have high risk
|
| 806 |
+
if (contract === 'Month-to-month') riskFactor += 0.25;
|
| 807 |
+
if (contract === 'Two year') riskFactor -= 0.2;
|
| 808 |
+
// Security reduces risk
|
| 809 |
+
if (security === 'Yes') riskFactor -= 0.1;
|
| 810 |
+
|
| 811 |
+
// Clamp values between 0.02 and 0.98
|
| 812 |
+
const churnProb = Math.max(0.02, Math.min(0.98, riskFactor));
|
| 813 |
+
const prediction = churnProb > 0.5 ? 1 : 0;
|
| 814 |
+
|
| 815 |
+
displayPredictionResult(prediction, churnProb);
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
// Dynamically insert and display the prediction result below the action buttons
|
| 819 |
+
function displayPredictionResult(prediction, churnProb) {
|
| 820 |
+
const container = document.getElementById('resultContainer');
|
| 821 |
+
const isHighRisk = churnProb > 0.5;
|
| 822 |
+
|
| 823 |
+
container.innerHTML = `
|
| 824 |
+
<div class="result-card ${isHighRisk ? 'churn-high' : 'churn-low'}">
|
| 825 |
+
<div class="result-icon">
|
| 826 |
+
${isHighRisk ? '<i class="fa-solid fa-circle-exclamation"></i>' : '<i class="fa-solid fa-circle-check"></i>'}
|
| 827 |
+
</div>
|
| 828 |
+
<div class="result-info">
|
| 829 |
+
<h3>${isHighRisk ? 'High Churn Risk Predicted' : 'Low Churn Risk Predicted'}</h3>
|
| 830 |
+
<p>${isHighRisk ? 'This customer is likely to terminate service. Immediate retention measures are recommended.' : 'This customer is satisfied and is likely to remain with the service.'}</p>
|
| 831 |
+
</div>
|
| 832 |
+
<div class="result-score">
|
| 833 |
+
<div class="percentage">${(churnProb * 100).toFixed(1)}%</div>
|
| 834 |
+
<div class="label">Churn Probability</div>
|
| 835 |
+
</div>
|
| 836 |
+
</div>
|
| 837 |
+
`;
|
| 838 |
+
|
| 839 |
+
// Smooth scroll to view results natively
|
| 840 |
+
container.scrollIntoView({ behavior: 'smooth' });
|
| 841 |
+
}
|
| 842 |
+
|
| 843 |
+
// Reset Form to initial default state
|
| 844 |
+
function resetForm() {
|
| 845 |
+
document.getElementById('churnForm').reset();
|
| 846 |
+
|
| 847 |
+
// Clear result container
|
| 848 |
+
const container = document.getElementById('resultContainer');
|
| 849 |
+
if (container) {
|
| 850 |
+
container.innerHTML = '';
|
| 851 |
+
}
|
| 852 |
+
}
|
| 853 |
+
</script>
|
| 854 |
+
</body>
|
| 855 |
+
</html>
|
templates/predict2.html
ADDED
|
@@ -0,0 +1,856 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" data-theme="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Customer Churn Prediction Portal</title>
|
| 7 |
+
<meta name="description" content="Predict customer churn probability using advanced machine learning models based on telecom profile characteristics.">
|
| 8 |
+
|
| 9 |
+
<!-- Modern Google Fonts -->
|
| 10 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 11 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 12 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 13 |
+
|
| 14 |
+
<!-- Font Awesome Icons for premium look -->
|
| 15 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 16 |
+
|
| 17 |
+
<style>
|
| 18 |
+
/* Styling variables and custom design tokens */
|
| 19 |
+
:root {
|
| 20 |
+
--bg-color: #f5f3ff; /* soft lilac-tinted white */
|
| 21 |
+
--card-bg: #ffffff; /* Pure white for form containers */
|
| 22 |
+
--text-main: #1e1b4b; /* Deep indigo - highly readable */
|
| 23 |
+
--text-muted: #6b7280; /* Slate/gray 500 */
|
| 24 |
+
--border-color: #ddd6fe; /* Soft lavender border */
|
| 25 |
+
--primary-accent: #7c3aed; /* Royal Amethyst violet */
|
| 26 |
+
--primary-hover: #6d28d9; /* Darker violet */
|
| 27 |
+
--font-family: 'Inter', sans-serif;
|
| 28 |
+
--card-shadow: 0 10px 30px rgba(124, 58, 237, 0.05);
|
| 29 |
+
--card-border-radius: 12px;
|
| 30 |
+
|
| 31 |
+
/* Auxiliary colors for status and categories */
|
| 32 |
+
--success: #10b981;
|
| 33 |
+
--warning: #f59e0b;
|
| 34 |
+
--danger: #ef4444;
|
| 35 |
+
--gradient-start: #4f46e5; /* Indigo 600 */
|
| 36 |
+
--gradient-end: #7c3aed; /* Amethyst violet 600 */
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/* Dark Mode variables */
|
| 40 |
+
[data-theme="dark"] {
|
| 41 |
+
--bg-color: #0b0a19; /* deep space-violet background */
|
| 42 |
+
--card-bg: #161427; /* deep violet-gray card container background */
|
| 43 |
+
--text-main: #f5f3ff; /* crisp white/violet text */
|
| 44 |
+
--text-muted: #94a3b8; /* soft slate-gray for muted text */
|
| 45 |
+
--border-color: #2c2848; /* dark violet-gray border */
|
| 46 |
+
--primary-accent: #a78bfa; /* light violet */
|
| 47 |
+
--primary-hover: #c4b5fd; /* bright violet */
|
| 48 |
+
--card-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
|
| 49 |
+
--gradient-start: #1e1b4b; /* dark indigo gradient start */
|
| 50 |
+
--gradient-end: #4c1d95; /* dark violet gradient end */
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
* {
|
| 54 |
+
box-sizing: border-box;
|
| 55 |
+
margin: 0;
|
| 56 |
+
padding: 0;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
body {
|
| 60 |
+
font-family: var(--font-family);
|
| 61 |
+
background-color: var(--bg-color);
|
| 62 |
+
color: var(--text-main);
|
| 63 |
+
min-height: 100vh;
|
| 64 |
+
display: flex;
|
| 65 |
+
flex-direction: column;
|
| 66 |
+
transition: background-color 0.3s, color 0.3s;
|
| 67 |
+
line-height: 1.5;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
/* Header styling with smooth gradient */
|
| 71 |
+
header {
|
| 72 |
+
background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 100%);
|
| 73 |
+
color: #ffffff;
|
| 74 |
+
padding: 2.5rem 1.5rem;
|
| 75 |
+
text-align: center;
|
| 76 |
+
position: relative;
|
| 77 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.header-content {
|
| 81 |
+
max-width: 1000px;
|
| 82 |
+
margin: 0 auto;
|
| 83 |
+
position: relative;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
header h1 {
|
| 87 |
+
font-size: 2.25rem;
|
| 88 |
+
font-weight: 700;
|
| 89 |
+
margin-bottom: 0.5rem;
|
| 90 |
+
letter-spacing: -0.025em;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
header p {
|
| 94 |
+
font-size: 1rem;
|
| 95 |
+
opacity: 0.9;
|
| 96 |
+
max-width: 600px;
|
| 97 |
+
margin: 0 auto;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.theme-toggle-btn {
|
| 101 |
+
position: absolute;
|
| 102 |
+
top: 10px;
|
| 103 |
+
right: 20px;
|
| 104 |
+
background: rgba(255, 255, 255, 0.15);
|
| 105 |
+
border: none;
|
| 106 |
+
color: #ffffff;
|
| 107 |
+
padding: 8px 12px;
|
| 108 |
+
border-radius: 20px;
|
| 109 |
+
cursor: pointer;
|
| 110 |
+
font-size: 0.9rem;
|
| 111 |
+
font-weight: 500;
|
| 112 |
+
display: flex;
|
| 113 |
+
align-items: center;
|
| 114 |
+
gap: 6px;
|
| 115 |
+
transition: background 0.2s;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.theme-toggle-btn:hover {
|
| 119 |
+
background: rgba(255, 255, 255, 0.25);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
/* Main layout container */
|
| 123 |
+
main {
|
| 124 |
+
flex: 1;
|
| 125 |
+
max-width: 1200px;
|
| 126 |
+
width: 100%;
|
| 127 |
+
margin: 0 auto;
|
| 128 |
+
padding: 2rem 1.5rem;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
/* Prediction Result Banner if present */
|
| 132 |
+
.result-card {
|
| 133 |
+
background-color: var(--card-bg);
|
| 134 |
+
border-radius: var(--card-border-radius);
|
| 135 |
+
box-shadow: var(--card-shadow);
|
| 136 |
+
border-left: 6px solid var(--primary-accent);
|
| 137 |
+
padding: 1.5rem;
|
| 138 |
+
margin-top: 2rem;
|
| 139 |
+
display: flex;
|
| 140 |
+
align-items: center;
|
| 141 |
+
gap: 1.5rem;
|
| 142 |
+
animation: slideDown 0.4s ease-out;
|
| 143 |
+
border-top: 1px solid var(--border-color);
|
| 144 |
+
border-right: 1px solid var(--border-color);
|
| 145 |
+
border-bottom: 1px solid var(--border-color);
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.result-card.churn-high {
|
| 149 |
+
border-left-color: var(--danger);
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
.result-card.churn-low {
|
| 153 |
+
border-left-color: var(--success);
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.result-icon {
|
| 157 |
+
font-size: 2.5rem;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.churn-high .result-icon { color: var(--danger); }
|
| 161 |
+
.churn-low .result-icon { color: var(--success); }
|
| 162 |
+
|
| 163 |
+
.result-info h3 {
|
| 164 |
+
font-size: 1.25rem;
|
| 165 |
+
font-weight: 600;
|
| 166 |
+
margin-bottom: 0.25rem;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.result-info p {
|
| 170 |
+
color: var(--text-muted);
|
| 171 |
+
font-size: 0.95rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.result-score {
|
| 175 |
+
margin-left: auto;
|
| 176 |
+
text-align: right;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.result-score .percentage {
|
| 180 |
+
font-size: 2rem;
|
| 181 |
+
font-weight: 700;
|
| 182 |
+
color: var(--text-main);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.result-score .label {
|
| 186 |
+
font-size: 0.75rem;
|
| 187 |
+
color: var(--text-muted);
|
| 188 |
+
text-transform: uppercase;
|
| 189 |
+
letter-spacing: 0.05em;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/* Grid Layout for Form Card and Controls */
|
| 193 |
+
.form-layout {
|
| 194 |
+
display: flex;
|
| 195 |
+
flex-direction: column;
|
| 196 |
+
gap: 2rem;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
/* Card Panels representing form sections */
|
| 200 |
+
.section-card {
|
| 201 |
+
background-color: var(--card-bg);
|
| 202 |
+
border-radius: var(--card-border-radius);
|
| 203 |
+
box-shadow: var(--card-shadow);
|
| 204 |
+
padding: 2rem;
|
| 205 |
+
border: 1px solid var(--border-color);
|
| 206 |
+
transition: background-color 0.3s, border-color 0.3s, transform 0.2s;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.section-card:hover {
|
| 210 |
+
transform: translateY(-2px);
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.section-title {
|
| 214 |
+
font-size: 1.25rem;
|
| 215 |
+
font-weight: 600;
|
| 216 |
+
margin-bottom: 1.5rem;
|
| 217 |
+
display: flex;
|
| 218 |
+
align-items: center;
|
| 219 |
+
gap: 10px;
|
| 220 |
+
border-bottom: 2px solid var(--bg-color);
|
| 221 |
+
padding-bottom: 0.75rem;
|
| 222 |
+
color: var(--primary-accent);
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.grid-container {
|
| 226 |
+
display: grid;
|
| 227 |
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
| 228 |
+
gap: 1.5rem;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
/* Form Fields Styling */
|
| 232 |
+
.form-group {
|
| 233 |
+
display: flex;
|
| 234 |
+
flex-direction: column;
|
| 235 |
+
gap: 0.5rem;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
label {
|
| 239 |
+
font-weight: 500;
|
| 240 |
+
font-size: 0.9rem;
|
| 241 |
+
color: var(--text-main);
|
| 242 |
+
display: flex;
|
| 243 |
+
align-items: center;
|
| 244 |
+
gap: 6px;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.field-desc {
|
| 248 |
+
font-size: 0.75rem;
|
| 249 |
+
color: var(--text-muted);
|
| 250 |
+
margin-top: -0.25rem;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
input[type="text"],
|
| 254 |
+
input[type="number"],
|
| 255 |
+
select {
|
| 256 |
+
font-family: var(--font-family);
|
| 257 |
+
color: var(--text-main);
|
| 258 |
+
background-color: var(--card-bg);
|
| 259 |
+
border: 1px solid var(--border-color);
|
| 260 |
+
border-radius: 6px;
|
| 261 |
+
padding: 10px 14px;
|
| 262 |
+
font-size: 0.95rem;
|
| 263 |
+
width: 100%;
|
| 264 |
+
transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
input[type="text"]:focus,
|
| 268 |
+
input[type="number"]:focus,
|
| 269 |
+
select:focus {
|
| 270 |
+
border-color: var(--primary-accent);
|
| 271 |
+
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.15);
|
| 272 |
+
outline: none;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
/* Form action buttons */
|
| 276 |
+
.form-actions {
|
| 277 |
+
display: flex;
|
| 278 |
+
justify-content: space-between;
|
| 279 |
+
align-items: center;
|
| 280 |
+
width: 100%;
|
| 281 |
+
gap: 1rem;
|
| 282 |
+
margin-top: 1.5rem;
|
| 283 |
+
flex-wrap: wrap;
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
.form-actions .nav-group,
|
| 287 |
+
.form-actions .action-group {
|
| 288 |
+
display: flex;
|
| 289 |
+
gap: 1rem;
|
| 290 |
+
flex-wrap: wrap;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
.btn {
|
| 294 |
+
font-family: var(--font-family);
|
| 295 |
+
font-size: 0.95rem;
|
| 296 |
+
font-weight: 600;
|
| 297 |
+
padding: 12px 24px;
|
| 298 |
+
border-radius: 6px;
|
| 299 |
+
cursor: pointer;
|
| 300 |
+
border: none;
|
| 301 |
+
transition: all 0.2s;
|
| 302 |
+
display: inline-flex;
|
| 303 |
+
align-items: center;
|
| 304 |
+
gap: 8px;
|
| 305 |
+
text-decoration: none;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.btn-secondary {
|
| 309 |
+
background-color: var(--card-bg);
|
| 310 |
+
color: var(--text-main);
|
| 311 |
+
border: 1px solid var(--border-color);
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
.btn-secondary:hover {
|
| 315 |
+
background-color: var(--bg-color);
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.btn-primary {
|
| 319 |
+
background-color: var(--primary-accent);
|
| 320 |
+
color: #ffffff;
|
| 321 |
+
box-shadow: 0 2px 4px rgba(124, 58, 237, 0.2);
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.btn-primary:hover {
|
| 325 |
+
background-color: var(--primary-hover);
|
| 326 |
+
transform: translateY(-1px);
|
| 327 |
+
box-shadow: 0 4px 6px rgba(124, 58, 237, 0.3);
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
.btn-primary:active {
|
| 331 |
+
transform: translateY(0);
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
.btn-warning {
|
| 335 |
+
background-color: var(--warning);
|
| 336 |
+
color: #ffffff;
|
| 337 |
+
box-shadow: 0 2px 4px rgba(245, 158, 11, 0.2);
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
.btn-warning:hover {
|
| 341 |
+
background-color: #d97706;
|
| 342 |
+
transform: translateY(-1px);
|
| 343 |
+
box-shadow: 0 4px 6px rgba(245, 158, 11, 0.3);
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
footer {
|
| 347 |
+
text-align: center;
|
| 348 |
+
padding: 2rem 1.5rem;
|
| 349 |
+
color: var(--text-muted);
|
| 350 |
+
font-size: 0.85rem;
|
| 351 |
+
border-top: 1px solid var(--border-color);
|
| 352 |
+
margin-top: auto;
|
| 353 |
+
background-color: var(--card-bg);
|
| 354 |
+
transition: background-color 0.3s, border-top 0.3s;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
/* Animations */
|
| 358 |
+
@keyframes slideDown {
|
| 359 |
+
from {
|
| 360 |
+
opacity: 0;
|
| 361 |
+
transform: translateY(-15px);
|
| 362 |
+
}
|
| 363 |
+
to {
|
| 364 |
+
opacity: 1;
|
| 365 |
+
transform: translateY(0);
|
| 366 |
+
}
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
/* Responsive adjustments */
|
| 370 |
+
@media (max-width: 768px) {
|
| 371 |
+
.grid-container {
|
| 372 |
+
grid-template-columns: 1fr;
|
| 373 |
+
}
|
| 374 |
+
header h1 {
|
| 375 |
+
font-size: 1.75rem;
|
| 376 |
+
}
|
| 377 |
+
main {
|
| 378 |
+
padding: 1rem;
|
| 379 |
+
}
|
| 380 |
+
.form-actions {
|
| 381 |
+
flex-direction: column-reverse;
|
| 382 |
+
align-items: stretch;
|
| 383 |
+
}
|
| 384 |
+
.form-actions .nav-group,
|
| 385 |
+
.form-actions .action-group {
|
| 386 |
+
flex-direction: column;
|
| 387 |
+
width: 100%;
|
| 388 |
+
}
|
| 389 |
+
.btn {
|
| 390 |
+
width: 100%;
|
| 391 |
+
justify-content: center;
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
</style>
|
| 395 |
+
</head>
|
| 396 |
+
<body>
|
| 397 |
+
|
| 398 |
+
<header>
|
| 399 |
+
<button class="theme-toggle-btn" id="themeToggle" onclick="toggleTheme()" type="button">
|
| 400 |
+
<i class="fa-solid fa-moon"></i> <span id="themeToggleText">Dark Mode</span>
|
| 401 |
+
</button>
|
| 402 |
+
<div class="header-content">
|
| 403 |
+
<h1><i class="fa-solid fa-chart-line"></i> Customer Churn Analyzer</h1>
|
| 404 |
+
<p>Configure a customer profile below to estimate the likelihood of churn using our optimized XGBoost prediction engine.</p>
|
| 405 |
+
</div>
|
| 406 |
+
</header>
|
| 407 |
+
|
| 408 |
+
<main>
|
| 409 |
+
<form id="churnForm">
|
| 410 |
+
<div class="form-layout">
|
| 411 |
+
|
| 412 |
+
<!-- Section 1: Customer Profile -->
|
| 413 |
+
<section class="section-card">
|
| 414 |
+
<h2 class="section-title"><i class="fa-solid fa-user-tie"></i> Customer Profile & Demographics</h2>
|
| 415 |
+
<div class="grid-container">
|
| 416 |
+
|
| 417 |
+
<div class="form-group">
|
| 418 |
+
<label for="gender"><i class="fa-solid fa-venus-mars"></i> Gender</label>
|
| 419 |
+
<select name="gender" id="gender" required>
|
| 420 |
+
<option value="0">Female</option>
|
| 421 |
+
<option value="1">Male</option>
|
| 422 |
+
</select>
|
| 423 |
+
<span class="field-desc">Binary representation (Female=0, Male=1)</span>
|
| 424 |
+
</div>
|
| 425 |
+
|
| 426 |
+
<div class="form-group">
|
| 427 |
+
<label for="SeniorCitizen"><i class="fa-solid fa-person-cane"></i> Senior Citizen</label>
|
| 428 |
+
<select name="SeniorCitizen" id="SeniorCitizen" required>
|
| 429 |
+
<option value="0">No</option>
|
| 430 |
+
<option value="1">Yes</option>
|
| 431 |
+
</select>
|
| 432 |
+
<span class="field-desc">Is the customer 65 years or older?</span>
|
| 433 |
+
</div>
|
| 434 |
+
|
| 435 |
+
<div class="form-group">
|
| 436 |
+
<label for="Partner"><i class="fa-solid fa-ring"></i> Partner Status</label>
|
| 437 |
+
<select name="Partner" id="Partner" required>
|
| 438 |
+
<option value="No">No Partner</option>
|
| 439 |
+
<option value="Yes">Has Partner</option>
|
| 440 |
+
</select>
|
| 441 |
+
<span class="field-desc">Primary demographic status flag</span>
|
| 442 |
+
</div>
|
| 443 |
+
|
| 444 |
+
<div class="form-group">
|
| 445 |
+
<label for="Dependents"><i class="fa-solid fa-children"></i> Dependents Status</label>
|
| 446 |
+
<select name="Dependents" id="Dependents" required>
|
| 447 |
+
<option value="No">No Dependents</option>
|
| 448 |
+
<option value="Yes">Has Dependents</option>
|
| 449 |
+
</select>
|
| 450 |
+
<span class="field-desc">Dependent children or relatives</span>
|
| 451 |
+
</div>
|
| 452 |
+
|
| 453 |
+
</div>
|
| 454 |
+
</section>
|
| 455 |
+
|
| 456 |
+
<!-- Section 2: Core Phone & Internet Services -->
|
| 457 |
+
<section class="section-card">
|
| 458 |
+
<h2 class="section-title"><i class="fa-solid fa-network-wired"></i> Core Telecom Services</h2>
|
| 459 |
+
<div class="grid-container">
|
| 460 |
+
|
| 461 |
+
<div class="form-group">
|
| 462 |
+
<label for="PhoneService"><i class="fa-solid fa-phone"></i> Phone Service</label>
|
| 463 |
+
<select name="PhoneService" id="PhoneService" required>
|
| 464 |
+
<option value="Yes">Yes</option>
|
| 465 |
+
<option value="No">No</option>
|
| 466 |
+
</select>
|
| 467 |
+
<span class="field-desc">Subscribes to standard voice line</span>
|
| 468 |
+
</div>
|
| 469 |
+
|
| 470 |
+
<div class="form-group">
|
| 471 |
+
<label for="MultipleLines"><i class="fa-solid fa-phone-volume"></i> Multiple Lines</label>
|
| 472 |
+
<select name="MultipleLines" id="MultipleLines" required>
|
| 473 |
+
<option value="No">No</option>
|
| 474 |
+
<option value="Yes">Yes</option>
|
| 475 |
+
<option value="No phone service">No phone service</option>
|
| 476 |
+
</select>
|
| 477 |
+
<span class="field-desc">Multiple telephone numbers active</span>
|
| 478 |
+
</div>
|
| 479 |
+
|
| 480 |
+
<div class="form-group">
|
| 481 |
+
<label for="InternetService"><i class="fa-solid fa-wifi"></i> Internet Service Type</label>
|
| 482 |
+
<select name="InternetService" id="InternetService" required>
|
| 483 |
+
<option value="DSL">DSL</option>
|
| 484 |
+
<option value="Fiber optic">Fiber optic</option>
|
| 485 |
+
<option value="No">No Internet Service</option>
|
| 486 |
+
</select>
|
| 487 |
+
<span class="field-desc">Technology type</span>
|
| 488 |
+
</div>
|
| 489 |
+
|
| 490 |
+
</div>
|
| 491 |
+
</section>
|
| 492 |
+
|
| 493 |
+
<!-- Section 3: Premium Add-ons & Streaming -->
|
| 494 |
+
<section class="section-card">
|
| 495 |
+
<h2 class="section-title"><i class="fa-solid fa-cubes"></i> Service Add-Ons & Streaming</h2>
|
| 496 |
+
<div class="grid-container">
|
| 497 |
+
|
| 498 |
+
<div class="form-group">
|
| 499 |
+
<label for="OnlineSecurity"><i class="fa-solid fa-shield-halved"></i> Online Security</label>
|
| 500 |
+
<select name="OnlineSecurity" id="OnlineSecurity" required>
|
| 501 |
+
<option value="No">No</option>
|
| 502 |
+
<option value="Yes">Yes</option>
|
| 503 |
+
<option value="No internet service">No internet service</option>
|
| 504 |
+
</select>
|
| 505 |
+
<span class="field-desc">Antivirus & firewall bundle</span>
|
| 506 |
+
</div>
|
| 507 |
+
|
| 508 |
+
<div class="form-group">
|
| 509 |
+
<label for="OnlineBackup"><i class="fa-solid fa-cloud-arrow-up"></i> Online Backup</label>
|
| 510 |
+
<select name="OnlineBackup" id="OnlineBackup" required>
|
| 511 |
+
<option value="No">No</option>
|
| 512 |
+
<option value="Yes">Yes</option>
|
| 513 |
+
<option value="No internet service">No internet service</option>
|
| 514 |
+
</select>
|
| 515 |
+
<span class="field-desc">Cloud backup storage</span>
|
| 516 |
+
</div>
|
| 517 |
+
|
| 518 |
+
<div class="form-group">
|
| 519 |
+
<label for="DeviceProtection"><i class="fa-solid fa-laptop-medical"></i> Device Protection</label>
|
| 520 |
+
<select name="DeviceProtection" id="DeviceProtection" required>
|
| 521 |
+
<option value="No">No</option>
|
| 522 |
+
<option value="Yes">Yes</option>
|
| 523 |
+
<option value="No internet service">No internet service</option>
|
| 524 |
+
</select>
|
| 525 |
+
<span class="field-desc">Accidental damage coverage</span>
|
| 526 |
+
</div>
|
| 527 |
+
|
| 528 |
+
<div class="form-group">
|
| 529 |
+
<label for="TechSupport"><i class="fa-solid fa-user-gear"></i> Tech Support</label>
|
| 530 |
+
<select name="TechSupport" id="TechSupport" required>
|
| 531 |
+
<option value="No">No</option>
|
| 532 |
+
<option value="Yes">Yes</option>
|
| 533 |
+
<option value="No internet service">No internet service</option>
|
| 534 |
+
</select>
|
| 535 |
+
<span class="field-desc">Priority assistance plan</span>
|
| 536 |
+
</div>
|
| 537 |
+
|
| 538 |
+
<div class="form-group">
|
| 539 |
+
<label for="StreamingTV"><i class="fa-solid fa-tv"></i> Streaming TV</label>
|
| 540 |
+
<select name="StreamingTV" id="StreamingTV" required>
|
| 541 |
+
<option value="No">No</option>
|
| 542 |
+
<option value="Yes">Yes</option>
|
| 543 |
+
<option value="No internet service">No internet service</option>
|
| 544 |
+
</select>
|
| 545 |
+
<span class="field-desc">Live TV channels package</span>
|
| 546 |
+
</div>
|
| 547 |
+
|
| 548 |
+
<div class="form-group">
|
| 549 |
+
<label for="StreamingMovies"><i class="fa-solid fa-film"></i> Streaming Movies</label>
|
| 550 |
+
<select name="StreamingMovies" id="StreamingMovies" required>
|
| 551 |
+
<option value="No">No</option>
|
| 552 |
+
<option value="Yes">Yes</option>
|
| 553 |
+
<option value="No internet service">No internet service</option>
|
| 554 |
+
</select>
|
| 555 |
+
<span class="field-desc">Movie library subscription</span>
|
| 556 |
+
</div>
|
| 557 |
+
|
| 558 |
+
</div>
|
| 559 |
+
</section>
|
| 560 |
+
|
| 561 |
+
<!-- Section 4: Contract & Financials -->
|
| 562 |
+
<section class="section-card">
|
| 563 |
+
<h2 class="section-title"><i class="fa-solid fa-file-invoice-dollar"></i> Contract & Billing Details</h2>
|
| 564 |
+
<div class="grid-container">
|
| 565 |
+
|
| 566 |
+
<div class="form-group">
|
| 567 |
+
<label for="Contract"><i class="fa-solid fa-file-contract"></i> Contract Type</label>
|
| 568 |
+
<select name="Contract" id="Contract" required>
|
| 569 |
+
<option value="Month-to-month">Month-to-month</option>
|
| 570 |
+
<option value="One year">One year</option>
|
| 571 |
+
<option value="Two year">Two year</option>
|
| 572 |
+
</select>
|
| 573 |
+
<span class="field-desc">Billing term structure</span>
|
| 574 |
+
</div>
|
| 575 |
+
|
| 576 |
+
<div class="form-group">
|
| 577 |
+
<label for="PaperlessBilling"><i class="fa-solid fa-envelope-open-text"></i> Paperless Billing</label>
|
| 578 |
+
<select name="PaperlessBilling" id="PaperlessBilling" required>
|
| 579 |
+
<option value="Yes">Yes</option>
|
| 580 |
+
<option value="No">No</option>
|
| 581 |
+
</select>
|
| 582 |
+
<span class="field-desc">Invoice delivery preference</span>
|
| 583 |
+
</div>
|
| 584 |
+
|
| 585 |
+
<div class="form-group">
|
| 586 |
+
<label for="PaymentMethod"><i class="fa-solid fa-credit-card"></i> Payment Method</label>
|
| 587 |
+
<select name="PaymentMethod" id="PaymentMethod" required>
|
| 588 |
+
<option value="Electronic check">Electronic check</option>
|
| 589 |
+
<option value="Mailed check">Mailed check</option>
|
| 590 |
+
<option value="Bank transfer (automatic)">Bank transfer (automatic)</option>
|
| 591 |
+
<option value="Credit card (automatic)">Credit card (automatic)</option>
|
| 592 |
+
</select>
|
| 593 |
+
<span class="field-desc">Billing settlement channel</span>
|
| 594 |
+
</div>
|
| 595 |
+
|
| 596 |
+
<div class="form-group">
|
| 597 |
+
<label for="tenure"><i class="fa-solid fa-hourglass-half"></i> Tenure (Months)</label>
|
| 598 |
+
<input type="number" name="tenure" id="tenure" min="0" max="72" placeholder="e.g. 12" required>
|
| 599 |
+
<span class="field-desc">Months customer has stayed (0 to 72)</span>
|
| 600 |
+
</div>
|
| 601 |
+
|
| 602 |
+
<div class="form-group">
|
| 603 |
+
<label for="MonthlyCharges"><i class="fa-solid fa-sack-dollar"></i> Monthly Charges ($)</label>
|
| 604 |
+
<input type="number" name="MonthlyCharges" id="MonthlyCharges" min="0" max="150" step="0.01" placeholder="e.g. 64.85" required>
|
| 605 |
+
<span class="field-desc">Current monthly invoice price</span>
|
| 606 |
+
</div>
|
| 607 |
+
|
| 608 |
+
<div class="form-group">
|
| 609 |
+
<label for="TotalCharges"><i class="fa-solid fa-money-bill-wave"></i> Total Charges ($)</label>
|
| 610 |
+
<input type="number" name="TotalCharges" id="TotalCharges" min="0" max="10000" step="0.01" placeholder="e.g. 2292.15" required>
|
| 611 |
+
<span class="field-desc">Accrued bill amount across tenure</span>
|
| 612 |
+
</div>
|
| 613 |
+
|
| 614 |
+
</div>
|
| 615 |
+
</section>
|
| 616 |
+
|
| 617 |
+
<!-- Submit & Reset Actions -->
|
| 618 |
+
<div class="form-actions">
|
| 619 |
+
<div class="nav-group">
|
| 620 |
+
<a href="{{ url_for('home') }}" class="btn btn-secondary">
|
| 621 |
+
<i class="fa-solid fa-house"></i> Return to Home
|
| 622 |
+
</a>
|
| 623 |
+
<a href="{{ url_for('documentation') }}" class="btn btn-secondary">
|
| 624 |
+
<i class="fa-solid fa-book"></i> View Documentation
|
| 625 |
+
</a>
|
| 626 |
+
</div>
|
| 627 |
+
<div class="action-group">
|
| 628 |
+
<button type="button" class="btn btn-warning" onclick="resetForm()">
|
| 629 |
+
<i class="fa-solid fa-arrow-rotate-left"></i> Reset Fields
|
| 630 |
+
</button>
|
| 631 |
+
<button type="submit" class="btn btn-primary">
|
| 632 |
+
<i class="fa-solid fa-circle-play"></i> Run Prediction Model
|
| 633 |
+
</button>
|
| 634 |
+
</div>
|
| 635 |
+
</div>
|
| 636 |
+
|
| 637 |
+
<!-- Result Container located at the bottom of the page, below the buttons -->
|
| 638 |
+
<div id="resultContainer"></div>
|
| 639 |
+
|
| 640 |
+
</div>
|
| 641 |
+
</form>
|
| 642 |
+
</main>
|
| 643 |
+
|
| 644 |
+
<footer>
|
| 645 |
+
<p>© 2026 Customer Churn Prediction Engine. All rights reserved. Powered by Flask, XGBoost & Scikit-Learn.</p>
|
| 646 |
+
</footer>
|
| 647 |
+
|
| 648 |
+
<script>
|
| 649 |
+
// Initialize Theme
|
| 650 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 651 |
+
const savedTheme = localStorage.getItem('theme') || 'dark'; // Default to dark!
|
| 652 |
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
| 653 |
+
updateThemeUI(savedTheme);
|
| 654 |
+
|
| 655 |
+
// Initialize service dependencies and constraints
|
| 656 |
+
checkServiceDependencies();
|
| 657 |
+
document.getElementById('PhoneService').addEventListener('change', checkServiceDependencies);
|
| 658 |
+
document.getElementById('InternetService').addEventListener('change', checkServiceDependencies);
|
| 659 |
+
|
| 660 |
+
// Handle asynchronous native form submission
|
| 661 |
+
document.getElementById('churnForm').addEventListener('submit', function(event) {
|
| 662 |
+
event.preventDefault(); // Intercept page navigation
|
| 663 |
+
|
| 664 |
+
const phoneService = document.getElementById('PhoneService').value;
|
| 665 |
+
const internetService = document.getElementById('InternetService').value;
|
| 666 |
+
|
| 667 |
+
// Double check: if both phone and internet are 'No', block submission
|
| 668 |
+
if (phoneService === 'No' && internetService === 'No') {
|
| 669 |
+
return; // Block submission
|
| 670 |
+
}
|
| 671 |
+
|
| 672 |
+
const formData = new FormData(this);
|
| 673 |
+
|
| 674 |
+
// Asynchronously post to prediction endpoint
|
| 675 |
+
fetch('/predict_model', {
|
| 676 |
+
method: 'POST',
|
| 677 |
+
body: formData
|
| 678 |
+
})
|
| 679 |
+
.then(response => {
|
| 680 |
+
if (!response.ok) {
|
| 681 |
+
throw new Error('Network response error');
|
| 682 |
+
}
|
| 683 |
+
return response.json();
|
| 684 |
+
})
|
| 685 |
+
.then(data => {
|
| 686 |
+
if (data.success) {
|
| 687 |
+
displayPredictionResult(data.prediction, data.churn_prob);
|
| 688 |
+
} else {
|
| 689 |
+
console.error('Prediction failed: ' + data.error);
|
| 690 |
+
}
|
| 691 |
+
})
|
| 692 |
+
.catch(error => {
|
| 693 |
+
console.warn('Backend server unavailable. Executing client-side model fallback...', error);
|
| 694 |
+
|
| 695 |
+
// Fallback mechanism if opened directly via filesystem (file:///C:/...)
|
| 696 |
+
if (window.location.protocol === 'file:') {
|
| 697 |
+
runClientSideMockPrediction();
|
| 698 |
+
}
|
| 699 |
+
});
|
| 700 |
+
});
|
| 701 |
+
});
|
| 702 |
+
|
| 703 |
+
// Theme toggle function
|
| 704 |
+
function toggleTheme() {
|
| 705 |
+
const currentTheme = document.documentElement.getAttribute('data-theme');
|
| 706 |
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
| 707 |
+
document.documentElement.setAttribute('data-theme', newTheme);
|
| 708 |
+
localStorage.setItem('theme', newTheme);
|
| 709 |
+
updateThemeUI(newTheme);
|
| 710 |
+
}
|
| 711 |
+
|
| 712 |
+
// Update Theme Toggle Buttons
|
| 713 |
+
function updateThemeUI(theme) {
|
| 714 |
+
const btnText = document.getElementById('themeToggleText');
|
| 715 |
+
const btnIcon = document.querySelector('.theme-toggle-btn i');
|
| 716 |
+
if (theme === 'dark') {
|
| 717 |
+
btnText.textContent = 'Light Mode';
|
| 718 |
+
btnIcon.className = 'fa-solid fa-sun';
|
| 719 |
+
} else {
|
| 720 |
+
btnText.textContent = 'Dark Mode';
|
| 721 |
+
btnIcon.className = 'fa-solid fa-moon';
|
| 722 |
+
}
|
| 723 |
+
}
|
| 724 |
+
|
| 725 |
+
// Dynamic UI constraints for phone and internet services selection
|
| 726 |
+
function checkServiceDependencies() {
|
| 727 |
+
const phoneService = document.getElementById('PhoneService').value;
|
| 728 |
+
const multipleLines = document.getElementById('MultipleLines');
|
| 729 |
+
const internetService = document.getElementById('InternetService').value;
|
| 730 |
+
const submitBtn = document.querySelector('button[type="submit"]');
|
| 731 |
+
const resultContainer = document.getElementById('resultContainer');
|
| 732 |
+
|
| 733 |
+
// 1. Phone Service -> Multiple Lines constraint
|
| 734 |
+
if (phoneService === 'No') {
|
| 735 |
+
multipleLines.value = 'No phone service';
|
| 736 |
+
multipleLines.disabled = true;
|
| 737 |
+
multipleLines.style.opacity = '0.6';
|
| 738 |
+
} else {
|
| 739 |
+
if (multipleLines.value === 'No phone service') {
|
| 740 |
+
multipleLines.value = 'No';
|
| 741 |
+
}
|
| 742 |
+
multipleLines.disabled = false;
|
| 743 |
+
multipleLines.style.opacity = '1';
|
| 744 |
+
}
|
| 745 |
+
|
| 746 |
+
// 2. Internet Service -> Add-ons constraints
|
| 747 |
+
const addons = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies'];
|
| 748 |
+
if (internetService === 'No') {
|
| 749 |
+
addons.forEach(id => {
|
| 750 |
+
const select = document.getElementById(id);
|
| 751 |
+
select.value = 'No internet service';
|
| 752 |
+
select.disabled = true;
|
| 753 |
+
select.style.opacity = '0.6';
|
| 754 |
+
});
|
| 755 |
+
} else {
|
| 756 |
+
addons.forEach(id => {
|
| 757 |
+
const select = document.getElementById(id);
|
| 758 |
+
select.disabled = false;
|
| 759 |
+
select.style.opacity = '1';
|
| 760 |
+
if (select.value === 'No internet service') {
|
| 761 |
+
select.value = 'No';
|
| 762 |
+
}
|
| 763 |
+
});
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
// 3. Dual service deactivation check (Phone Service = No & Internet Service = No)
|
| 767 |
+
if (phoneService === 'No' && internetService === 'No') {
|
| 768 |
+
submitBtn.disabled = true;
|
| 769 |
+
|
| 770 |
+
// Render a warning card below buttons
|
| 771 |
+
resultContainer.innerHTML = `
|
| 772 |
+
<div class="result-card churn-high" style="border-left-color: var(--warning); margin-top: 2rem;">
|
| 773 |
+
<div class="result-icon" style="color: var(--warning);">
|
| 774 |
+
<i class="fa-solid fa-triangle-exclamation"></i>
|
| 775 |
+
</div>
|
| 776 |
+
<div class="result-info">
|
| 777 |
+
<h3>Inactive Profile Status</h3>
|
| 778 |
+
<p>This customer has selected no services from the telecom provider (Phone and Internet are both deactivated). Churn prediction model cannot run on an inactive subscription.</p>
|
| 779 |
+
</div>
|
| 780 |
+
</div>
|
| 781 |
+
`;
|
| 782 |
+
resultContainer.scrollIntoView({ behavior: 'smooth' });
|
| 783 |
+
} else {
|
| 784 |
+
submitBtn.disabled = false;
|
| 785 |
+
// Clear message only if it was the subscription warning
|
| 786 |
+
if (resultContainer.innerHTML.includes('Inactive Profile Status')) {
|
| 787 |
+
resultContainer.innerHTML = '';
|
| 788 |
+
}
|
| 789 |
+
}
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
// Function to calculate a mock prediction if running offline / from file system
|
| 793 |
+
function runClientSideMockPrediction() {
|
| 794 |
+
const tenure = parseFloat(document.getElementById('tenure').value) || 0;
|
| 795 |
+
const monthly = parseFloat(document.getElementById('MonthlyCharges').value) || 0;
|
| 796 |
+
const contract = document.getElementById('Contract').value;
|
| 797 |
+
const security = document.getElementById('OnlineSecurity').value;
|
| 798 |
+
|
| 799 |
+
// Basic churn risk logic matching general trends
|
| 800 |
+
let riskFactor = 0.3;
|
| 801 |
+
|
| 802 |
+
// Monthly charges increase risk
|
| 803 |
+
riskFactor += (monthly / 150) * 0.3;
|
| 804 |
+
// Tenure reduces risk
|
| 805 |
+
riskFactor -= (tenure / 72) * 0.4;
|
| 806 |
+
// Month-to-month contracts have high risk
|
| 807 |
+
if (contract === 'Month-to-month') riskFactor += 0.25;
|
| 808 |
+
if (contract === 'Two year') riskFactor -= 0.2;
|
| 809 |
+
// Security reduces risk
|
| 810 |
+
if (security === 'Yes') riskFactor -= 0.1;
|
| 811 |
+
|
| 812 |
+
// Clamp values between 0.02 and 0.98
|
| 813 |
+
const churnProb = Math.max(0.02, Math.min(0.98, riskFactor));
|
| 814 |
+
const prediction = churnProb > 0.5 ? 1 : 0;
|
| 815 |
+
|
| 816 |
+
displayPredictionResult(prediction, churnProb);
|
| 817 |
+
}
|
| 818 |
+
|
| 819 |
+
// Dynamically insert and display the prediction result below the action buttons
|
| 820 |
+
function displayPredictionResult(prediction, churnProb) {
|
| 821 |
+
const container = document.getElementById('resultContainer');
|
| 822 |
+
const isHighRisk = churnProb > 0.5;
|
| 823 |
+
|
| 824 |
+
container.innerHTML = `
|
| 825 |
+
<div class="result-card ${isHighRisk ? 'churn-high' : 'churn-low'}">
|
| 826 |
+
<div class="result-icon">
|
| 827 |
+
${isHighRisk ? '<i class="fa-solid fa-circle-exclamation"></i>' : '<i class="fa-solid fa-circle-check"></i>'}
|
| 828 |
+
</div>
|
| 829 |
+
<div class="result-info">
|
| 830 |
+
<h3>${isHighRisk ? 'High Churn Risk Predicted' : 'Low Churn Risk Predicted'}</h3>
|
| 831 |
+
<p>${isHighRisk ? 'This customer is likely to terminate service. Immediate retention measures are recommended.' : 'This customer is satisfied and is likely to remain with the service.'}</p>
|
| 832 |
+
</div>
|
| 833 |
+
<div class="result-score">
|
| 834 |
+
<div class="percentage">${(churnProb * 100).toFixed(1)}%</div>
|
| 835 |
+
<div class="label">Churn Probability</div>
|
| 836 |
+
</div>
|
| 837 |
+
</div>
|
| 838 |
+
`;
|
| 839 |
+
|
| 840 |
+
// Smooth scroll to view results natively
|
| 841 |
+
container.scrollIntoView({ behavior: 'smooth' });
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
// Reset Form to initial default state
|
| 845 |
+
function resetForm() {
|
| 846 |
+
document.getElementById('churnForm').reset();
|
| 847 |
+
|
| 848 |
+
// Clear result container
|
| 849 |
+
const container = document.getElementById('resultContainer');
|
| 850 |
+
if (container) {
|
| 851 |
+
container.innerHTML = '';
|
| 852 |
+
}
|
| 853 |
+
}
|
| 854 |
+
</script>
|
| 855 |
+
</body>
|
| 856 |
+
</html>
|