Spaces:
Sleeping
Sleeping
| 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() |