import os import json import requests import pandas as pd import gradio as gr APP_NAME = "StayWise AI" DATA_FILE = "synthetic_airbnb_project_data.csv" N8N_WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL", "").strip() def load_data(): return pd.read_csv(DATA_FILE) df = load_data() def safe_mean(series, default=0): series = pd.to_numeric(series, errors="coerce").dropna() if len(series) == 0: return default return float(series.mean()) def get_choices(column): return sorted(df[column].dropna().astype(str).unique().tolist()) neighbourhood_groups = get_choices("neighbourhood_group") neighbourhoods = get_choices("neighbourhood") room_types = get_choices("room_type") seasons = get_choices("season") if "season" in df.columns else ["Low Season", "Medium Season", "High Season"] def call_n8n(payload): if not N8N_WEBHOOK_URL: return { "status": "not_configured", "insight": "n8n webhook is not configured yet.", "next_step": "Add N8N_WEBHOOK_URL in Hugging Face Space secrets.", "log": "Pipeline ran locally only." } try: response = requests.post(N8N_WEBHOOK_URL, json=payload, timeout=15) if response.status_code >= 200 and response.status_code < 300: try: data = response.json() return { "status": data.get("status", "success"), "insight": data.get("insight", "n8n processed the recommendation."), "next_step": data.get("next_step", "Review the generated automation output."), "log": data.get("log", "Automation completed.") } except Exception: return { "status": "success", "insight": "n8n received the data.", "next_step": "Check your n8n workflow output.", "log": response.text[:500] } return { "status": "error", "insight": f"n8n returned status code {response.status_code}.", "next_step": "Check your webhook and Respond to Webhook node.", "log": response.text[:500] } except Exception as e: return { "status": "error", "insight": "The app could not reach n8n.", "next_step": "Check that the n8n production webhook is active.", "log": str(e) } def run_pipeline( neighbourhood_group, neighbourhood, room_type, price, availability_365, season, local_event_score, rating, sentiment_score, send_to_n8n ): price = float(price) availability_365 = float(availability_365) local_event_score = float(local_event_score) rating = float(rating) sentiment_score = float(sentiment_score) comparable = df.copy() comparable = comparable[ (comparable["neighbourhood_group"].astype(str) == str(neighbourhood_group)) & (comparable["room_type"].astype(str) == str(room_type)) ] local_comparable = comparable[comparable["neighbourhood"].astype(str) == str(neighbourhood)] if len(local_comparable) >= 5: comparable = local_comparable if len(comparable) < 5: comparable = df[df["room_type"].astype(str) == str(room_type)] competitor_avg_price = safe_mean(comparable["price"], price) avg_occupancy = safe_mean(comparable["occupancy_rate"], 0.5) avg_demand = safe_mean(comparable["demand_score"], 50) price_gap_pct = ((price - competitor_avg_price) / competitor_avg_price) * 100 if competitor_avg_price else 0 season_boost = { "Low Season": -0.08, "Medium Season": 0.00, "High Season": 0.08, "Low": -0.08, "Medium": 0.00, "High": 0.08, "Peak": 0.12, "Spring": 0.03, "Summer": 0.08, "Autumn": 0.00, "Winter": -0.05 }.get(str(season), 0.00) price_penalty = max(min(price_gap_pct / 100, 0.35), -0.35) * 0.30 event_boost = (local_event_score / 100) * 0.12 rating_boost = (rating - 4.0) * 0.06 sentiment_boost = sentiment_score * 0.08 occupancy = avg_occupancy + season_boost + event_boost + rating_boost + sentiment_boost - price_penalty occupancy = max(0.05, min(0.95, occupancy)) booked_nights = round(occupancy * 30) monthly_revenue = round(price * booked_nights, 2) demand_score = ( 0.45 * avg_demand + 0.25 * (occupancy * 100) + 0.15 * local_event_score + 0.10 * ((rating / 5) * 100) + 0.05 * ((sentiment_score + 1) / 2 * 100) ) demand_score = round(max(0, min(100, demand_score)), 1) if demand_score >= 70: demand_level = "High" demand_badge = "๐ข High" elif demand_score >= 45: demand_level = "Medium" demand_badge = "๐ก Medium" else: demand_level = "Low" demand_badge = "๐ด Low" if price_gap_pct > 15 and demand_level != "High": pricing_recommendation = "Consider lowering price" suggested_price = round(competitor_avg_price * 1.05, 2) insight = "The listing appears overpriced compared with similar properties." next_step = f"Test a lower price around ${suggested_price} to improve occupancy." recommendation_badge = "๐ป Price Reduction Suggested" elif price_gap_pct < -10 and demand_level in ["Medium", "High"]: pricing_recommendation = "Consider raising price" suggested_price = round(min(competitor_avg_price * 0.98, price * 1.12), 2) insight = "The listing appears underpriced relative to comparable demand." next_step = f"Consider increasing the price toward ${suggested_price}." recommendation_badge = "๐ Revenue Opportunity" else: pricing_recommendation = "Keep price stable" suggested_price = round(price, 2) insight = "The current price is aligned with comparable listings." next_step = "Keep price stable and focus on visibility, reviews, and conversion." recommendation_badge = "โ Stable Positioning" opportunity_score = round( demand_score * 0.45 + occupancy * 100 * 0.25 + rating / 5 * 100 * 0.15 + ((sentiment_score + 1) / 2 * 100) * 0.15, 2 ) payload = { "app_name": APP_NAME, "neighbourhood_group": neighbourhood_group, "neighbourhood": neighbourhood, "room_type": room_type, "current_price": price, "suggested_price": suggested_price, "competitor_avg_price": round(competitor_avg_price, 2), "price_vs_competitor_pct": round(price_gap_pct, 2), "occupancy_estimate": round(occupancy, 3), "booked_nights_month": booked_nights, "monthly_revenue": monthly_revenue, "demand_score": demand_score, "demand_level": demand_level, "opportunity_score": opportunity_score, "pricing_recommendation": pricing_recommendation, "insight": insight, "next_step": next_step } if send_to_n8n: n8n_response = call_n8n(payload) else: n8n_response = { "status": "not_sent", "insight": "n8n automation was not triggered.", "next_step": "Tick the n8n checkbox to send this result to the workflow.", "log": "Local pipeline only." } result_text = f"""
| Competitor average price | ${competitor_avg_price:,.2f} |
| Price vs competitors | {price_gap_pct:.2f}% |
| Estimated occupancy | {occupancy * 100:.1f}% |
| Estimated booked nights / month | {booked_nights} |
| Demand score | {demand_score}/100 |
| Opportunity score | {opportunity_score}/100 |
{insight}
{next_step}
| Insight | {n8n_response.get("insight", "No insight returned.")} |
| Next step | {n8n_response.get("next_step", "No next step returned.")} |
| Log | {n8n_response.get("log", "No log returned.")} |
AI-powered pricing and performance optimization for short-term rentals.
Run the full pipeline, benchmark a listing against comparable properties, and return automation insights from n8n.