""" Wellness Tourism Package Prediction App Production-grade Streamlit application for predicting customer purchase likelihood """ import streamlit as st import pandas as pd import numpy as np import joblib from huggingface_hub import hf_hub_download import plotly.graph_objects as go import plotly.express as px from datetime import datetime import os # Page configuration st.set_page_config( page_title="Wellness Tourism Predictor", page_icon="โ๏ธ", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS for better UI st.markdown(""" """, unsafe_allow_html=True) @st.cache_resource def load_model(): """ Load the trained model from Hugging Face Hub Uses caching to avoid reloading on every interaction """ try: model_path = hf_hub_download( repo_id="TheHumanAgent/tour_pkg_pred_model", filename="final_tour_pkg_pred_model_v1.joblib", repo_type="model" ) model = joblib.load(model_path) return model except Exception as e: st.error(f"Error loading model: {str(e)}") st.error("Please ensure the model is uploaded to Hugging Face Hub") st.stop() def create_input_features(): """ Create input form for all features required by the model Returns a dictionary with user inputs based on actual data ranges """ st.sidebar.header("๐ Customer Information") # Initialize session state for form if 'prediction_made' not in st.session_state: st.session_state.prediction_made = False with st.sidebar: st.subheader("๐ค Personal Details") # Age: Range from 18-61 based on data age = st.slider("Age", min_value=18, max_value=61, value=36, # median help="Customer's age (18-61 years)") # Gender: Male, Female, Fe Male (as seen in data) gender = st.selectbox("Gender", ["Female", "Male", "Fe Male"], help="Customer's gender") # MaritalStatus: Single, Married, Divorced, Unmarried marital_status = st.selectbox("Marital Status", ["Single", "Divorced", "Married", "Unmarried"], help="Customer's marital status") # CityTier: 1, 2, 3 city_tier = st.selectbox("City Tier", [1, 2, 3], index=0, # median is 1 help="City development level (1=Most developed, 3=Least developed)") st.markdown("---") st.subheader("๐ผ Professional Details") # Occupation: Salaried, Small Business, Large Business, Free Lancer occupation = st.selectbox("Occupation", ["Salaried", "Free Lancer", "Small Business", "Large Business"], help="Customer's occupation type") # Designation: Executive, Manager, Senior Manager, AVP, VP designation = st.selectbox("Designation", ["Manager", "Executive", "Senior Manager", "AVP", "VP"], help="Customer's job designation") # MonthlyIncome: Range from 1000 to 98678 monthly_income = st.number_input("Monthly Income (โน)", min_value=1000, max_value=100000, value=22418, # median step=1000, help="Gross monthly income in Rupees (โน1,000 - โน98,678)") st.markdown("---") st.subheader("โ๏ธ Travel Preferences") # NumberOfTrips: Range from 1-22 num_trips = st.slider("Number of Trips (Annually)", min_value=1, max_value=22, value=3, # median help="Average annual trips taken (1-22)") # Passport: 0 or 1 passport = st.selectbox("Valid Passport", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No", index=0, # median is 0 help="Does customer have a valid passport?") # OwnCar: 0 or 1 own_car = st.selectbox("Own Car", [0, 1], format_func=lambda x: "Yes" if x == 1 else "No", index=1, # median is 1 help="Does customer own a car?") # PreferredPropertyStar: 3, 4, 5 preferred_property_star = st.selectbox("Preferred Hotel Rating", [3, 4, 5], index=0, # median is 3 help="Preferred hotel star rating (3-5 stars)") st.markdown("---") st.subheader("๐จโ๐ฉโ๐งโ๐ฆ Trip Details") # NumberOfPersonVisiting: Range from 1-5 num_persons = st.slider("Number of Persons Visiting", min_value=1, max_value=5, value=3, # median help="Total people in the group (1-5)") # NumberOfChildrenVisiting: Range from 0-3 num_children = st.slider("Number of Children (<5 years)", min_value=0, max_value=3, value=1, # median help="Number of children under 5 years (0-3)") st.markdown("---") st.subheader("๐ Interaction Details") # TypeofContact: Company Invited, Self Enquiry type_of_contact = st.selectbox("Type of Contact", ["Self Enquiry", "Company Invited"], help="How was the customer contacted?") # ProductPitched: Basic, Standard, Deluxe, Super Deluxe, King product_pitched = st.selectbox("Product Pitched", ["Deluxe", "Basic", "Standard", "Super Deluxe", "King"], help="Type of package pitched to the customer") # DurationOfPitch: Range from 5-127 minutes duration_of_pitch = st.slider("Duration of Pitch (minutes)", min_value=5, max_value=127, value=14, # median help="Sales pitch duration in minutes (5-127)") # NumberOfFollowups: Range from 1-6 num_followups = st.slider("Number of Follow-ups", min_value=1, max_value=6, value=4, # median help="Total follow-ups after initial pitch (1-6)") # PitchSatisfactionScore: Range from 1-5 pitch_satisfaction = st.slider("Pitch Satisfaction Score", min_value=1, max_value=5, value=3, # median help="Customer satisfaction with the pitch (1=Very Low, 5=Very High)") # Create feature dictionary matching exact column names from training data features = { 'Age': age, 'CityTier': city_tier, 'DurationOfPitch': duration_of_pitch, 'NumberOfPersonVisiting': num_persons, 'NumberOfFollowups': num_followups, 'PreferredPropertyStar': preferred_property_star, 'NumberOfTrips': num_trips, 'Passport': passport, 'PitchSatisfactionScore': pitch_satisfaction, 'NumberOfChildrenVisiting': num_children, 'MonthlyIncome': monthly_income, 'TypeofContact': type_of_contact, 'Occupation': occupation, 'Gender': gender, 'OwnCar': own_car, 'ProductPitched': product_pitched, 'MaritalStatus': marital_status, 'Designation': designation } return features def create_gauge_chart(probability): """ Create a gauge chart to visualize purchase probability """ fig = go.Figure(go.Indicator( mode = "gauge+number+delta", value = probability * 100, domain = {'x': [0, 1], 'y': [0, 1]}, title = {'text': "Purchase Probability (%)", 'font': {'size': 24}}, delta = {'reference': 45, 'increasing': {'color': "green"}}, gauge = { 'axis': {'range': [None, 100], 'tickwidth': 1, 'tickcolor': "darkblue"}, 'bar': {'color': "darkblue"}, 'bgcolor': "white", 'borderwidth': 2, 'bordercolor': "gray", 'steps': [ {'range': [0, 30], 'color': '#ffcccc'}, {'range': [30, 70], 'color': '#ffffcc'}, {'range': [70, 100], 'color': '#ccffcc'} ], 'threshold': { 'line': {'color': "red", 'width': 4}, 'thickness': 0.75, 'value': 45 } } )) fig.update_layout( height=300, margin=dict(l=20, r=20, t=50, b=20) ) return fig def create_feature_importance_chart(features_df): """ Create a bar chart showing key customer metrics """ # Select key features for visualization key_features = { 'Monthly Income (โนK)': features_df['MonthlyIncome'].values[0] / 1000, 'Age': features_df['Age'].values[0], 'Annual Trips': features_df['NumberOfTrips'].values[0], 'Pitch Duration (min)': features_df['DurationOfPitch'].values[0], 'Follow-ups': features_df['NumberOfFollowups'].values[0], 'Satisfaction': features_df['PitchSatisfactionScore'].values[0], 'Hotel Rating': features_df['PreferredPropertyStar'].values[0], 'Group Size': features_df['NumberOfPersonVisiting'].values[0] } fig = px.bar( x=list(key_features.values()), y=list(key_features.keys()), orientation='h', title='Key Customer Metrics Overview', labels={'x': 'Value', 'y': 'Feature'}, color=list(key_features.values()), color_continuous_scale='Blues' ) fig.update_layout( height=400, showlegend=False, margin=dict(l=20, r=20, t=50, b=20) ) return fig def get_recommendation(probability, features): """ Generate actionable recommendations based on prediction and customer profile """ recommendations = [] # Priority level based on probability if probability >= 0.7: recommendations.append("โ **HIGH PRIORITY LEAD** - Strong purchase likelihood") recommendations.append("๐ฏ **Action**: Schedule immediate follow-up call within 24 hours") recommendations.append("๐ **Strategy**: Offer premium package options and exclusive benefits") elif probability >= 0.45: recommendations.append("โ ๏ธ **MEDIUM PRIORITY LEAD** - Moderate purchase likelihood") recommendations.append("๐ง **Action**: Send personalized email highlighting package benefits") recommendations.append("๐ **Strategy**: Consider offering limited-time discount (5-10%)") else: recommendations.append("โ **LOW PRIORITY LEAD** - Lower purchase likelihood") recommendations.append("๐ฌ **Action**: Add to nurture email campaign") recommendations.append("๐ **Strategy**: Re-engage after 2-3 months with seasonal offers") recommendations.append("") # Spacing # Additional contextual recommendations based on specific features if features['NumberOfFollowups'] <= 2: recommendations.append("๐ **Insight**: Low follow-up count - Increase engagement frequency") if features['PitchSatisfactionScore'] <= 2: recommendations.append("โ ๏ธ **Alert**: Low satisfaction score - Review and improve pitch approach") elif features['PitchSatisfactionScore'] >= 4: recommendations.append("โญ **Positive**: High satisfaction - Customer is engaged, act quickly!") if features['MonthlyIncome'] >= 30000: recommendations.append("๐ฐ **Insight**: High-income customer - Emphasize luxury and premium features") if features['NumberOfTrips'] >= 5: recommendations.append("โ๏ธ **Insight**: Frequent traveler - Highlight loyalty benefits and travel perks") if features['Passport'] == 0: recommendations.append("๐ **Note**: No passport - Consider domestic package options") if features['NumberOfChildrenVisiting'] >= 2: recommendations.append("๐จโ๐ฉโ๐งโ๐ฆ **Insight**: Family with children - Emphasize family-friendly amenities") if features['DurationOfPitch'] < 10: recommendations.append("โฑ๏ธ **Note**: Short pitch duration - May need more detailed product information") return recommendations def display_customer_summary(features): """ Display a formatted summary of customer information """ col1, col2, col3, col4 = st.columns(4) with col1: st.metric("๐ค Age", f"{features['Age']} years") st.metric("๐๏ธ City Tier", f"Tier {features['CityTier']}") with col2: st.metric("๐ฐ Income", f"โน{features['MonthlyIncome']:,}") st.metric("โ๏ธ Annual Trips", features['NumberOfTrips']) with col3: st.metric("๐ Follow-ups", features['NumberOfFollowups']) st.metric("โญ Satisfaction", f"{features['PitchSatisfactionScore']}/5") with col4: st.metric("๐ฅ Group Size", features['NumberOfPersonVisiting']) st.metric("๐จ Hotel Pref", f"{features['PreferredPropertyStar']} Star") def main(): """ Main application function """ # Header st.markdown('
โ๏ธ Wellness Tourism Package Predictor
', unsafe_allow_html=True) st.markdown('AI-Powered Customer Purchase Prediction System
', unsafe_allow_html=True) # Load model with st.spinner("๐ Loading ML model..."): model = load_model() st.success("โ Model loaded successfully!") # Create input form features = create_input_features() # Main content area st.markdown("---") st.subheader("๐ Customer Profile Summary") display_customer_summary(features) # Show detailed information in expandable section with st.expander("๐ View Complete Customer Details"): df_display = pd.DataFrame([features]).T df_display.columns = ['Value'] st.dataframe(df_display, use_container_width=True, height=600) st.markdown("---") # Prediction section col_left, col_right = st.columns([2, 1]) with col_right: st.subheader("๐ฏ Make Prediction") predict_button = st.button("๐ฎ Predict Purchase Likelihood", type="primary", use_container_width=True) if st.button("๐ Reset", use_container_width=True): st.session_state.prediction_made = False st.rerun() with col_left: if predict_button: with st.spinner("๐ค Analyzing customer data..."): # Create DataFrame with exact feature order input_df = pd.DataFrame([features]) # Make prediction try: prediction_proba = model.predict_proba(input_df)[0, 1] prediction = 1 if prediction_proba >= 0.45 else 0 # Store in session state st.session_state.prediction_made = True st.session_state.prediction = prediction st.session_state.probability = prediction_proba st.session_state.features = features st.session_state.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") except Exception as e: st.error(f"โ Prediction Error: {str(e)}") st.error("Please check that all input values are valid.") st.stop() # Display prediction results if st.session_state.prediction_made: st.markdown("---") st.subheader("๐ Prediction Results") prediction = st.session_state.prediction probability = st.session_state.probability # Prediction box with color coding if prediction == 1: st.markdown(f"""๐ข Visit with Us - Wellness Tourism Package Prediction System
Powered by XGBoost ML Model | Classification Threshold: 45% | Trained on 4,128 customers
Model Version: v1.0 | Last Updated: December 2024