BaskaranAIExpert commited on
Commit
68906b9
·
verified ·
1 Parent(s): f403b33

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +449 -198
app.py CHANGED
@@ -1,237 +1,488 @@
1
  """
2
  Streamlit App for Wellness Tourism Package Prediction
3
- This application allows users to input customer data and predict
4
- whether they will purchase the Wellness Tourism Package.
 
 
 
 
 
 
 
 
 
 
5
  """
6
 
 
 
 
 
 
7
  import streamlit as st
 
 
8
  import pandas as pd
 
 
9
  from huggingface_hub import hf_hub_download
10
  import joblib
11
 
 
 
 
 
 
 
12
  HF_USERNAME = "BaskaranAIExpert"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Page configuration
15
- st.set_page_config(
16
- page_title="Wellness Tourism Package Prediction",
17
- page_icon="✈️",
18
- layout="wide"
19
- )
20
 
21
- # Download and load the model
22
  @st.cache_resource
23
- def load_model():
24
- """Load the trained model from Hugging Face Hub"""
 
 
 
 
 
 
 
 
 
 
 
25
  try:
26
- model_path = hf_hub_download(
27
- repo_id=f"{HF_USERNAME}/wellness-tourism-model",
28
- filename="wellness_tourism_model_v1.joblib"
29
- )
30
- model = joblib.load(model_path)
31
- return model
 
32
  except Exception as e:
33
- st.error(f"Error loading model: {str(e)}")
34
- st.info("Please ensure the model is uploaded to Hugging Face Hub and the username is correct.")
35
- return None
36
 
37
- # Load model
38
- model = load_model()
39
 
40
- # Streamlit UI
41
- st.title("✈️ Wellness Tourism Package Prediction App")
42
- st.markdown("""
43
- This application predicts whether a customer will purchase the **Wellness Tourism Package**
44
- based on their profile and interaction data. Enter the customer information below to get a prediction.
45
- """)
 
 
 
 
 
 
 
 
 
 
46
 
47
- if model is None:
48
- st.stop()
49
 
50
- # Create two columns for better layout
51
- col1, col2 = st.columns(2)
 
52
 
53
- with col1:
 
 
 
 
 
 
54
  st.subheader("📋 Customer Details")
55
 
56
- age = st.number_input("Age", min_value=18, max_value=100, value=35, step=1)
57
- gender = st.selectbox("Gender", ["Male", "Female"])
58
- marital_status = st.selectbox("Marital Status", ["Single", "Married", "Divorced"])
59
- occupation = st.selectbox("Occupation", [
60
- "Salaried", "Freelancer", "Small Business", "Large Business", "Other"
61
- ])
62
- designation = st.selectbox("Designation", [
63
- "Executive", "Manager", "Senior Manager", "AVP", "VP", "Other"
64
- ])
65
- monthly_income = st.number_input(
66
- "Monthly Income (₹)",
67
- min_value=0,
68
- max_value=1000000,
69
- value=50000,
70
- step=1000
71
- )
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- city_tier = st.selectbox("City Tier", ["Tier 1", "Tier 2", "Tier 3"])
74
- number_of_trips = st.number_input(
75
- "Number of Trips (Annual Average)",
76
- min_value=0,
77
- max_value=20,
78
- value=2,
79
- step=1
80
- )
81
- passport = st.selectbox("Has Passport", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No")
82
- own_car = st.selectbox("Owns Car", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No")
83
 
84
- with col2:
 
 
 
 
 
 
85
  st.subheader("👨‍👩‍👧‍👦 Travel Details")
86
 
87
- number_of_persons = st.number_input(
88
- "Number of Persons Visiting",
89
- min_value=1,
90
- max_value=10,
91
- value=2,
92
- step=1
93
- )
94
- number_of_children = st.number_input(
95
- "Number of Children Visiting (Below 5 years)",
96
- min_value=0,
97
- max_value=5,
98
- value=0,
99
- step=1
100
- )
101
- preferred_property_star = st.selectbox(
102
- "Preferred Property Star Rating",
103
- [3, 4, 5],
104
- index=1
105
- )
 
 
106
 
 
 
 
 
 
 
 
 
 
 
107
  st.subheader("📞 Interaction Details")
108
 
109
- type_of_contact = st.selectbox(
110
- "Type of Contact",
111
- ["Company Invited", "Self Inquiry"]
112
- )
113
- product_pitched = st.selectbox(
114
- "Product Pitched",
115
- ["Basic", "Standard", "Deluxe", "Super Deluxe", "King"]
116
- )
117
- pitch_satisfaction_score = st.slider(
118
- "Pitch Satisfaction Score",
119
- min_value=1,
120
- max_value=5,
121
- value=3,
122
- step=1
123
- )
124
- number_of_followups = st.number_input(
125
- "Number of Follow-ups",
126
- min_value=0,
127
- max_value=10,
128
- value=2,
129
- step=1
130
- )
131
- duration_of_pitch = st.number_input(
132
- "Duration of Pitch (minutes)",
133
- min_value=0.0,
134
- max_value=60.0,
135
- value=10.0,
136
- step=0.5
137
- )
138
-
139
- # Encode categorical variables (matching the preprocessing in prep.py)
140
- def encode_categorical(value, category_type):
141
- """Encode categorical values to match training data encoding"""
142
- encodings = {
143
- 'Gender': {'Male': 0, 'Female': 1},
144
- 'MaritalStatus': {'Single': 0, 'Married': 1, 'Divorced': 2},
145
- 'TypeofContact': {'Company Invited': 0, 'Self Inquiry': 1},
146
- 'CityTier': {'Tier 1': 0, 'Tier 2': 1, 'Tier 3': 2},
147
- 'Occupation': {
148
- 'Salaried': 0, 'Freelancer': 1, 'Small Business': 2,
149
- 'Large Business': 3, 'Other': 4
150
- },
151
- 'Designation': {
152
- 'Executive': 0, 'Manager': 1, 'Senior Manager': 2,
153
- 'AVP': 3, 'VP': 4, 'Other': 5
154
- },
155
- 'ProductPitched': {
156
- 'Basic': 0, 'Standard': 1, 'Deluxe': 2,
157
- 'Super Deluxe': 3, 'King': 4
158
- }
159
  }
160
- return encodings.get(category_type, {}).get(value, 0)
 
161
 
162
- # Assemble input into DataFrame
163
- if st.button("🔮 Predict Purchase Likelihood", type="primary"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  input_data = pd.DataFrame([{
165
- 'Age': age,
166
- 'TypeofContact': encode_categorical(type_of_contact, 'TypeofContact'),
167
- 'CityTier': encode_categorical(city_tier, 'CityTier'),
168
- 'Occupation': encode_categorical(occupation, 'Occupation'),
169
- 'Gender': encode_categorical(gender, 'Gender'),
170
- 'NumberOfPersonVisiting': number_of_persons,
171
- 'PreferredPropertyStar': preferred_property_star,
172
- 'MaritalStatus': encode_categorical(marital_status, 'MaritalStatus'),
173
- 'NumberOfTrips': number_of_trips,
174
- 'Passport': passport,
175
- 'OwnCar': own_car,
176
- 'NumberOfChildrenVisiting': number_of_children,
177
- 'Designation': encode_categorical(designation, 'Designation'),
178
- 'MonthlyIncome': monthly_income,
179
- 'PitchSatisfactionScore': pitch_satisfaction_score,
180
- 'ProductPitched': encode_categorical(product_pitched, 'ProductPitched'),
181
- 'NumberOfFollowups': number_of_followups,
182
- 'DurationOfPitch': duration_of_pitch
183
  }])
184
 
185
- try:
186
- # Get expected columns from the preprocessing step in the pipeline
187
- # The model is a Pipeline with a ColumnTransformer as the first step
188
- expected_cols = None
189
- if hasattr(model, 'steps') and len(model.steps) > 0:
190
- preprocessor = model.steps[0][1] # Get the ColumnTransformer
191
- if hasattr(preprocessor, 'feature_names_in_'):
192
- expected_cols = list(preprocessor.feature_names_in_)
193
-
194
- # If model expects 'Unnamed: 0', add it (workaround for current model)
195
- # This will be fixed when the model is retrained without this column
196
- if expected_cols and 'Unnamed: 0' in expected_cols:
197
- if 'Unnamed: 0' not in input_data.columns:
198
- input_data['Unnamed: 0'] = 0
199
-
200
- # Reorder columns to match expected order if available
201
- if expected_cols:
202
- # Ensure all expected columns are present
203
- for col in expected_cols:
204
- if col not in input_data.columns:
205
- input_data[col] = 0
206
- # Select columns in the expected order
207
- input_data = input_data[expected_cols]
208
-
209
- prediction = model.predict(input_data)[0]
210
- prediction_proba = model.predict_proba(input_data)[0]
211
-
212
- st.markdown("---")
213
- st.subheader("📊 Prediction Result")
214
 
215
- if prediction == 1:
216
- st.success(f"✅ **The customer is LIKELY to purchase the Wellness Tourism Package!**")
217
- st.info(f"Confidence: {prediction_proba[1]*100:.2f}%")
218
- else:
219
- st.warning(f"❌ **The customer is NOT LIKELY to purchase the Wellness Tourism Package.**")
220
- st.info(f"Confidence: {prediction_proba[0]*100:.2f}%")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
- col_prob1, col_prob2 = st.columns(2)
223
- with col_prob1:
224
- st.metric("Probability of Purchase", f"{prediction_proba[1]*100:.2f}%")
225
- with col_prob2:
226
- st.metric("Probability of No Purchase", f"{prediction_proba[0]*100:.2f}%")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
- except Exception as e:
229
- st.error(f"Error making prediction: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
- st.markdown("---")
232
- st.markdown("""
233
- <div style='text-align: center; color: gray;'>
234
- <p>Built with ❤️ for Visit with Us | MLOps Pipeline</p>
235
- </div>
236
- """, unsafe_allow_html=True)
237
 
 
 
 
1
  """
2
  Streamlit App for Wellness Tourism Package Prediction
3
+ ======================================================
4
+
5
+ This application provides a user-friendly web interface for predicting
6
+ whether a customer will purchase the Wellness Tourism Package.
7
+
8
+ Features:
9
+ - Interactive input forms for customer data
10
+ - Real-time prediction with confidence scores
11
+ - Professional UI with clear visualizations
12
+
13
+ Author: Baskaran Radhakrishnan
14
+ Date: 2026
15
  """
16
 
17
+ # ============================================================================
18
+ # SECTION 1: IMPORTS AND DEPENDENCIES
19
+ # ============================================================================
20
+
21
+ # Streamlit for web application framework
22
  import streamlit as st
23
+
24
+ # Data manipulation
25
  import pandas as pd
26
+
27
+ # Model loading and prediction
28
  from huggingface_hub import hf_hub_download
29
  import joblib
30
 
31
+
32
+ # ============================================================================
33
+ # SECTION 2: CONFIGURATION AND CONSTANTS
34
+ # ============================================================================
35
+
36
+ # Hugging Face Configuration
37
  HF_USERNAME = "BaskaranAIExpert"
38
+ MODEL_REPO = "wellness-tourism-model"
39
+ MODEL_FILENAME = "wellness_tourism_model_v1.joblib"
40
+
41
+ # Page Configuration
42
+ PAGE_TITLE = "Wellness Tourism Package Prediction"
43
+ PAGE_ICON = "✈️"
44
+ LAYOUT = "wide"
45
+
46
+
47
+ # ============================================================================
48
+ # SECTION 3: CATEGORICAL ENCODING MAPPINGS
49
+ # ============================================================================
50
+
51
+ # Categorical value encodings (must match training data preprocessing)
52
+ CATEGORICAL_ENCODINGS = {
53
+ 'Gender': {'Male': 0, 'Female': 1},
54
+ 'MaritalStatus': {'Single': 0, 'Married': 1, 'Divorced': 2},
55
+ 'TypeofContact': {'Company Invited': 0, 'Self Inquiry': 1},
56
+ 'CityTier': {'Tier 1': 0, 'Tier 2': 1, 'Tier 3': 2},
57
+ 'Occupation': {
58
+ 'Salaried': 0, 'Freelancer': 1, 'Small Business': 2,
59
+ 'Large Business': 3, 'Other': 4
60
+ },
61
+ 'Designation': {
62
+ 'Executive': 0, 'Manager': 1, 'Senior Manager': 2,
63
+ 'AVP': 3, 'VP': 4, 'Other': 5
64
+ },
65
+ 'ProductPitched': {
66
+ 'Basic': 0, 'Standard': 1, 'Deluxe': 2,
67
+ 'Super Deluxe': 3, 'King': 4
68
+ }
69
+ }
70
+
71
+
72
+ # ============================================================================
73
+ # SECTION 4: PAGE CONFIGURATION
74
+ # ============================================================================
75
+
76
+ def configure_page():
77
+ """
78
+ Configures Streamlit page settings.
79
+ """
80
+ st.set_page_config(
81
+ page_title=PAGE_TITLE,
82
+ page_icon=PAGE_ICON,
83
+ layout=LAYOUT,
84
+ initial_sidebar_state="expanded"
85
+ )
86
+
87
 
88
+ # ============================================================================
89
+ # SECTION 5: MODEL LOADING
90
+ # ============================================================================
 
 
 
91
 
 
92
  @st.cache_resource
93
+ def load_model(hf_username, model_repo, model_filename):
94
+ """
95
+ Loads the trained model from Hugging Face Hub.
96
+ Uses caching to avoid reloading on every interaction.
97
+
98
+ Args:
99
+ hf_username (str): Hugging Face username
100
+ model_repo (str): Model repository name
101
+ model_filename (str): Name of the model file
102
+
103
+ Returns:
104
+ tuple: (model, error_message) - Model object and error message (if any)
105
+ """
106
  try:
107
+ with st.spinner("Loading model from Hugging Face Hub..."):
108
+ model_path = hf_hub_download(
109
+ repo_id=f"{hf_username}/{model_repo}",
110
+ filename=model_filename
111
+ )
112
+ model = joblib.load(model_path)
113
+ return model, None
114
  except Exception as e:
115
+ error_msg = f"Error loading model: {str(e)}"
116
+ return None, error_msg
 
117
 
 
 
118
 
119
+ # ============================================================================
120
+ # SECTION 6: CATEGORICAL ENCODING
121
+ # ============================================================================
122
+
123
+ def encode_categorical(value, category_type):
124
+ """
125
+ Encodes categorical values to match training data encoding.
126
+
127
+ Args:
128
+ value (str): Categorical value to encode
129
+ category_type (str): Type of category (e.g., 'Gender', 'CityTier')
130
+
131
+ Returns:
132
+ int: Encoded value (defaults to 0 if not found)
133
+ """
134
+ return CATEGORICAL_ENCODINGS.get(category_type, {}).get(value, 0)
135
 
 
 
136
 
137
+ # ============================================================================
138
+ # SECTION 7: USER INPUT COLLECTION
139
+ # ============================================================================
140
 
141
+ def collect_customer_details():
142
+ """
143
+ Collects customer demographic and profile information.
144
+
145
+ Returns:
146
+ dict: Dictionary containing customer details
147
+ """
148
  st.subheader("📋 Customer Details")
149
 
150
+ customer_data = {
151
+ 'age': st.number_input("Age", min_value=18, max_value=100, value=35, step=1),
152
+ 'gender': st.selectbox("Gender", ["Male", "Female"]),
153
+ 'marital_status': st.selectbox("Marital Status", ["Single", "Married", "Divorced"]),
154
+ 'occupation': st.selectbox("Occupation", [
155
+ "Salaried", "Freelancer", "Small Business", "Large Business", "Other"
156
+ ]),
157
+ 'designation': st.selectbox("Designation", [
158
+ "Executive", "Manager", "Senior Manager", "AVP", "VP", "Other"
159
+ ]),
160
+ 'monthly_income': st.number_input(
161
+ "Monthly Income (₹)",
162
+ min_value=0,
163
+ max_value=1000000,
164
+ value=50000,
165
+ step=1000
166
+ ),
167
+ 'city_tier': st.selectbox("City Tier", ["Tier 1", "Tier 2", "Tier 3"]),
168
+ 'number_of_trips': st.number_input(
169
+ "Number of Trips (Annual Average)",
170
+ min_value=0,
171
+ max_value=20,
172
+ value=2,
173
+ step=1
174
+ ),
175
+ 'passport': st.selectbox("Has Passport", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No"),
176
+ 'own_car': st.selectbox("Owns Car", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No")
177
+ }
178
 
179
+ return customer_data
180
+
 
 
 
 
 
 
 
 
181
 
182
+ def collect_travel_details():
183
+ """
184
+ Collects travel-related information.
185
+
186
+ Returns:
187
+ dict: Dictionary containing travel details
188
+ """
189
  st.subheader("👨‍👩‍👧‍👦 Travel Details")
190
 
191
+ travel_data = {
192
+ 'number_of_persons': st.number_input(
193
+ "Number of Persons Visiting",
194
+ min_value=1,
195
+ max_value=10,
196
+ value=2,
197
+ step=1
198
+ ),
199
+ 'number_of_children': st.number_input(
200
+ "Number of Children Visiting (Below 5 years)",
201
+ min_value=0,
202
+ max_value=5,
203
+ value=0,
204
+ step=1
205
+ ),
206
+ 'preferred_property_star': st.selectbox(
207
+ "Preferred Property Star Rating",
208
+ [3, 4, 5],
209
+ index=1
210
+ )
211
+ }
212
 
213
+ return travel_data
214
+
215
+
216
+ def collect_interaction_details():
217
+ """
218
+ Collects customer interaction and sales pitch information.
219
+
220
+ Returns:
221
+ dict: Dictionary containing interaction details
222
+ """
223
  st.subheader("📞 Interaction Details")
224
 
225
+ interaction_data = {
226
+ 'type_of_contact': st.selectbox(
227
+ "Type of Contact",
228
+ ["Company Invited", "Self Inquiry"]
229
+ ),
230
+ 'product_pitched': st.selectbox(
231
+ "Product Pitched",
232
+ ["Basic", "Standard", "Deluxe", "Super Deluxe", "King"]
233
+ ),
234
+ 'pitch_satisfaction_score': st.slider(
235
+ "Pitch Satisfaction Score",
236
+ min_value=1,
237
+ max_value=5,
238
+ value=3,
239
+ step=1
240
+ ),
241
+ 'number_of_followups': st.number_input(
242
+ "Number of Follow-ups",
243
+ min_value=0,
244
+ max_value=10,
245
+ value=2,
246
+ step=1
247
+ ),
248
+ 'duration_of_pitch': st.number_input(
249
+ "Duration of Pitch (minutes)",
250
+ min_value=0.0,
251
+ max_value=60.0,
252
+ value=10.0,
253
+ step=0.5
254
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  }
256
+
257
+ return interaction_data
258
 
259
+
260
+ # ============================================================================
261
+ # SECTION 8: DATA PREPARATION FOR PREDICTION
262
+ # ============================================================================
263
+
264
+ def prepare_input_data(customer_data, travel_data, interaction_data):
265
+ """
266
+ Prepares input data in the format expected by the model.
267
+
268
+ Args:
269
+ customer_data (dict): Customer demographic information
270
+ travel_data (dict): Travel-related information
271
+ interaction_data (dict): Interaction details
272
+
273
+ Returns:
274
+ pd.DataFrame: Prepared input data
275
+ """
276
  input_data = pd.DataFrame([{
277
+ 'Age': customer_data['age'],
278
+ 'TypeofContact': encode_categorical(interaction_data['type_of_contact'], 'TypeofContact'),
279
+ 'CityTier': encode_categorical(customer_data['city_tier'], 'CityTier'),
280
+ 'Occupation': encode_categorical(customer_data['occupation'], 'Occupation'),
281
+ 'Gender': encode_categorical(customer_data['gender'], 'Gender'),
282
+ 'NumberOfPersonVisiting': travel_data['number_of_persons'],
283
+ 'PreferredPropertyStar': travel_data['preferred_property_star'],
284
+ 'MaritalStatus': encode_categorical(customer_data['marital_status'], 'MaritalStatus'),
285
+ 'NumberOfTrips': customer_data['number_of_trips'],
286
+ 'Passport': customer_data['passport'],
287
+ 'OwnCar': customer_data['own_car'],
288
+ 'NumberOfChildrenVisiting': travel_data['number_of_children'],
289
+ 'Designation': encode_categorical(customer_data['designation'], 'Designation'),
290
+ 'MonthlyIncome': customer_data['monthly_income'],
291
+ 'PitchSatisfactionScore': interaction_data['pitch_satisfaction_score'],
292
+ 'ProductPitched': encode_categorical(interaction_data['product_pitched'], 'ProductPitched'),
293
+ 'NumberOfFollowups': interaction_data['number_of_followups'],
294
+ 'DurationOfPitch': interaction_data['duration_of_pitch']
295
  }])
296
 
297
+ return input_data
298
+
299
+
300
+ def align_input_with_model(input_data, model):
301
+ """
302
+ Aligns input data columns with model's expected feature order.
303
+
304
+ Args:
305
+ input_data (pd.DataFrame): Input data
306
+ model: Trained model pipeline
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
+ Returns:
309
+ pd.DataFrame: Aligned input data
310
+ """
311
+ # Get expected columns from the preprocessing step in the pipeline
312
+ expected_cols = None
313
+ if hasattr(model, 'steps') and len(model.steps) > 0:
314
+ preprocessor = model.steps[0][1] # Get the ColumnTransformer
315
+ if hasattr(preprocessor, 'feature_names_in_'):
316
+ expected_cols = list(preprocessor.feature_names_in_)
317
+
318
+ # Handle 'Unnamed: 0' column if model expects it
319
+ if expected_cols and 'Unnamed: 0' in expected_cols:
320
+ if 'Unnamed: 0' not in input_data.columns:
321
+ input_data['Unnamed: 0'] = 0
322
+
323
+ # Reorder columns to match expected order
324
+ if expected_cols:
325
+ # Ensure all expected columns are present
326
+ for col in expected_cols:
327
+ if col not in input_data.columns:
328
+ input_data[col] = 0
329
+ # Select columns in the expected order
330
+ input_data = input_data[expected_cols]
331
+
332
+ return input_data
333
+
334
+
335
+ # ============================================================================
336
+ # SECTION 9: PREDICTION AND DISPLAY
337
+ # ============================================================================
338
+
339
+ def make_prediction(model, input_data):
340
+ """
341
+ Makes prediction using the trained model.
342
+
343
+ Args:
344
+ model: Trained model
345
+ input_data (pd.DataFrame): Prepared input data
346
 
347
+ Returns:
348
+ tuple: (prediction, prediction_proba) - Prediction and probabilities
349
+ """
350
+ prediction = model.predict(input_data)[0]
351
+ prediction_proba = model.predict_proba(input_data)[0]
352
+ return prediction, prediction_proba
353
+
354
+
355
+ def display_prediction_results(prediction, prediction_proba):
356
+ """
357
+ Displays prediction results with visualizations.
358
+
359
+ Args:
360
+ prediction (int): Predicted class (0 or 1)
361
+ prediction_proba (np.array): Prediction probabilities
362
+ """
363
+ st.markdown("---")
364
+ st.subheader("📊 Prediction Result")
365
+
366
+ # Display main prediction
367
+ if prediction == 1:
368
+ st.success(f"✅ **The customer is LIKELY to purchase the Wellness Tourism Package!**")
369
+ st.info(f"**Confidence Level:** {prediction_proba[1]*100:.2f}%")
370
+ else:
371
+ st.warning(f"❌ **The customer is NOT LIKELY to purchase the Wellness Tourism Package.**")
372
+ st.info(f"**Confidence Level:** {prediction_proba[0]*100:.2f}%")
373
+
374
+ # Display probability metrics
375
+ col_prob1, col_prob2 = st.columns(2)
376
+ with col_prob1:
377
+ st.metric(
378
+ "Probability of Purchase",
379
+ f"{prediction_proba[1]*100:.2f}%",
380
+ delta=f"{prediction_proba[1]*100 - 50:.2f}%"
381
+ )
382
+ with col_prob2:
383
+ st.metric(
384
+ "Probability of No Purchase",
385
+ f"{prediction_proba[0]*100:.2f}%",
386
+ delta=f"{prediction_proba[0]*100 - 50:.2f}%"
387
+ )
388
+
389
+ # Display recommendation
390
+ if prediction == 1:
391
+ st.info("💡 **Recommendation:** This customer shows high purchase likelihood. Consider prioritizing follow-up communication.")
392
+ else:
393
+ st.info("💡 **Recommendation:** This customer shows low purchase likelihood. Consider alternative marketing strategies.")
394
+
395
+
396
+ # ============================================================================
397
+ # SECTION 10: MAIN APPLICATION UI
398
+ # ============================================================================
399
+
400
+ def render_header():
401
+ """
402
+ Renders the application header and description.
403
+ """
404
+ st.title(f"{PAGE_ICON} {PAGE_TITLE}")
405
+ st.markdown("""
406
+ This application predicts whether a customer will purchase the **Wellness Tourism Package**
407
+ based on their profile and interaction data. Enter the customer information below to get a prediction.
408
+ """)
409
+
410
+
411
+ def render_footer():
412
+ """
413
+ Renders the application footer.
414
+ """
415
+ st.markdown("---")
416
+ st.markdown("""
417
+ <div style='text-align: center; color: gray; padding: 20px;'>
418
+ <p><strong>Built with ❤️ for Visit with Us</strong></p>
419
+ <p>MLOps Pipeline | Production Ready</p>
420
+ <p style='font-size: 0.8em;'>Model Version: v1.0 | Last Updated: 2024</p>
421
+ </div>
422
+ """, unsafe_allow_html=True)
423
+
424
+
425
+ def main():
426
+ """
427
+ Main application function that orchestrates the Streamlit UI.
428
+ """
429
+ # Configure page
430
+ configure_page()
431
+
432
+ # Render header
433
+ render_header()
434
+
435
+ # Load model
436
+ model, error = load_model(HF_USERNAME, MODEL_REPO, MODEL_FILENAME)
437
+
438
+ # Handle model loading error
439
+ if model is None:
440
+ st.error(f"⚠️ {error}")
441
+ st.info("💡 Please ensure:")
442
+ st.info("1. The model is uploaded to Hugging Face Hub")
443
+ st.info("2. The username is correct in the configuration")
444
+ st.info("3. You have internet connectivity")
445
+ st.stop()
446
+
447
+ # Display success message
448
+ st.success("✓ Model loaded successfully!")
449
+
450
+ # Create input form layout
451
+ col1, col2 = st.columns(2)
452
+
453
+ with col1:
454
+ customer_data = collect_customer_details()
455
+
456
+ with col2:
457
+ travel_data = collect_travel_details()
458
+ interaction_data = collect_interaction_details()
459
+
460
+ # Prediction button
461
+ if st.button("🔮 Predict Purchase Likelihood", type="primary", use_container_width=True):
462
+ try:
463
+ # Prepare input data
464
+ input_data = prepare_input_data(customer_data, travel_data, interaction_data)
465
 
466
+ # Align with model expectations
467
+ input_data = align_input_with_model(input_data, model)
468
+
469
+ # Make prediction
470
+ prediction, prediction_proba = make_prediction(model, input_data)
471
+
472
+ # Display results
473
+ display_prediction_results(prediction, prediction_proba)
474
+
475
+ except Exception as e:
476
+ st.error(f"❌ Error making prediction: {str(e)}")
477
+ st.info("Please check the input values and try again.")
478
+
479
+ # Render footer
480
+ render_footer()
481
+
482
 
483
+ # ============================================================================
484
+ # SECTION 11: SCRIPT ENTRY POINT
485
+ # ============================================================================
 
 
 
486
 
487
+ if __name__ == "__main__":
488
+ main()