import gradio as gr import pandas as pd import numpy as np import os import requests from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import LabelEncoder # ========================= # NLTK FIX (HF SAFE) # ========================= import nltk from nltk.sentiment import SentimentIntensityAnalyzer try: nltk.data.find('sentiment/vader_lexicon.zip') except: nltk.download('vader_lexicon') sia = SentimentIntensityAnalyzer() # ========================= # LOAD DATA # ========================= bookings = pd.read_csv("bookings_small.csv") X = bookings.drop(columns=["is_canceled"]) y = bookings["is_canceled"] # ========================= # ENCODERS # ========================= encoders = {} for col in X.select_dtypes(include="object").columns: le = LabelEncoder() X[col] = le.fit_transform(X[col].astype(str)) encoders[col] = le # ========================= # MODEL # ========================= model = RandomForestClassifier(n_estimators=150, max_depth=12) model.fit(X, y) WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL") # ========================= # SAFE ENCODE # ========================= def safe_encode(df): for col, le in encoders.items(): if col in df: df[col] = df[col].apply( lambda x: le.transform([x])[0] if x in le.classes_ else 0 ) return df # ========================= # BOOKING PREDICTION # ========================= def predict_booking(hotel, lead_time, adr, total_nights, total_guests): try: lead_time = int(lead_time) total_nights = int(total_nights) total_guests = int(total_guests) adr = float(adr) input_dict = { "hotel": hotel, "lead_time": lead_time, "adr": adr, "total_nights": total_nights, "total_guests": total_guests, # SAFE VALUES FROM DATA "market_segment": bookings["market_segment"].mode()[0], "deposit_type": bookings["deposit_type"].mode()[0], "is_repeated_guest": 0, "previous_cancellations": 0, "total_of_special_requests": 1, "seasonality_index": 1.0, "competitor_price_index": 1.0, "service_quality_proxy": 50, "booking_value_score": adr * total_nights * max(total_guests, 1) } df_input = pd.DataFrame([input_dict]) df_input = safe_encode(df_input) for col in X.columns: if col not in df_input: df_input[col] = 0 df_input = df_input[X.columns] prob = model.predict_proba(df_input)[0][1] # 🔥 FIXED THRESHOLDS (WIDER RANGE) if prob > 0.5: risk = "🔴 HIGH RISK" rec = "High cancellation probability → reduce price or verify booking" elif prob > 0.25: risk = "🟠 MEDIUM RISK" rec = "Moderate uncertainty → monitor demand" else: risk = "🟢 LOW RISK" rec = "Stable demand → pricing power available" return prob, risk, rec, input_dict except Exception as e: return 0, "ERROR", str(e), {} # ========================= # SENTIMENT (BOOSTED) # ========================= def run_sentiment(text): try: text = text.lower() score = sia.polarity_scores(text)["compound"] # 🔥 BOOST FOR SHORT NEGATIVE TEXT if any(word in text for word in ["trash", "bad", "terrible", "awful"]): score -= 0.4 if score > 0.2: label = "🟢 Positive" elif score < -0.2: label = "🔴 Negative" else: label = "🟡 Neutral" return f""" ### 💬 Sentiment Analysis **Score:** {score:.2f} **Label:** {label} **Insight:** {"🚨 Negative feedback → improve service before pricing increases" if "Negative" in label else "✅ Customer perception supports pricing strategy"} """, {"sentiment_label": label, "sentiment_score": score} except Exception as e: return f"❌ ERROR: {str(e)}", {} # ========================= # SEND TO N8N (FIXED PAYLOAD) # ========================= def send_to_n8n(source_tab, payload): if not WEBHOOK_URL: return "❌ Missing webhook secret" try: response = requests.post( WEBHOOK_URL, json=payload, # 🔥 IMPORTANT: send FLAT payload timeout=10 ) if response.status_code == 200: data = response.json() return f""" ### 🔗 n8n Response **Message:** {data.get("message")} **Decision:** {data.get("decision")} **Severity:** {data.get("severity")} **Recommendation:** {data.get("recommendation")} """ else: return f"❌ Error {response.status_code}: {response.text}" except Exception as e: return f"❌ Request failed: {str(e)}" # ========================= # UI # ========================= with gr.Blocks() as demo: gr.Markdown("# 🏨 LuxeRate AI") gr.Markdown("Smart hotel pricing & risk decision system") # BOOKING TAB with gr.Tab("📊 Booking Risk"): hotel = gr.Dropdown(["City Hotel", "Resort Hotel"], label="Hotel Type") lead_time = gr.Slider(0, 200, value=30, label="Lead Time") adr = gr.Slider(50, 300, value=120, label="Price (€)") total_nights = gr.Slider(1, 10, value=2, label="Nights") total_guests = gr.Slider(1, 5, value=2, label="Guests") output = gr.Markdown() state_payload = gr.State() def run_booking(hotel, lead_time, adr, total_nights, total_guests): prob, risk, rec, payload = predict_booking( hotel, lead_time, adr, total_nights, total_guests ) return f""" ### 📊 Booking Analysis **Cancellation Probability:** {prob:.2%} **Risk Level:** {risk} **Recommendation:** {rec} """, { "risk_label": risk, "cancellation_probability": prob } gr.Button("🔍 Analyze Booking").click( fn=run_booking, inputs=[hotel, lead_time, adr, total_nights, total_guests], outputs=[output, state_payload] ) send_output = gr.Markdown() gr.Button("🚀 Send to n8n").click( lambda p: send_to_n8n("booking", p), state_payload, send_output ) # SENTIMENT TAB with gr.Tab("💬 Review Sentiment"): review = gr.Textbox(label="Paste Review", lines=4) sentiment_output = gr.Markdown() state_review = gr.State() gr.Button("🔍 Analyze Sentiment").click( fn=run_sentiment, inputs=review, outputs=[sentiment_output, state_review] ) send_output2 = gr.Markdown() gr.Button("🚀 Send to n8n").click( lambda p: send_to_n8n("review", p), state_review, send_output2 ) demo.launch()