# app.py # This is the file Hugging Face looks for automatically. # It runs your Gradio interface and exposes the API. import gradio as gr import numpy as np import pandas as pd import joblib import os # ── Load all models (Hugging Face runs this once on startup) ────────────────── print("Loading models...") reg_model = joblib.load("model1_regression.pkl") cls_model = joblib.load("model1_classifier.pkl") m1_features = joblib.load("model1_features.pkl") forecast_6h = joblib.load("model2_forecast_6h.pkl") forecast_12h = joblib.load("model2_forecast_12h.pkl") m2_features = joblib.load("model2_features.pkl") print("All models loaded.") STATUS_NAMES = {0: "🟢 GREEN — Empty", 1: "🟡 YELLOW — Filling", 2: "🔴 RED — Full"} STATUS_ACTION = { 0: "No action needed", 1: "Monitor — schedule collection soon", 2: "DISPATCH TRUCK IMMEDIATELY" } # ── Shared feature builder ──────────────────────────────────────────────────── def build_row(ultrasonic, weight, fill_now, hour_of_day, day_of_week, bin_type, location, zone, fill_rate_1h=0, fill_rate_3h=0, fill_rate_6h=0, rolling_fill_3h=None, rolling_fill_6h=None, rolling_fill_12h=None, rolling_fill_24h=None, hours_since_collection=24, week_number=0): rolling_fill_3h = rolling_fill_3h or fill_now rolling_fill_6h = rolling_fill_6h or fill_now rolling_fill_12h = rolling_fill_12h or fill_now rolling_fill_24h = rolling_fill_24h or fill_now hours_to_full = max(0, min(72, (80 - fill_now) / fill_rate_1h if fill_rate_1h > 0.01 else 72 )) fill_acceleration = fill_rate_1h - (fill_rate_3h / 3 if fill_rate_3h else 0) return { "ultrasonic": ultrasonic, "weight": weight, "sensor_ratio": weight / (ultrasonic + 1e-5), "fill_percent": fill_now, "fill_rate_1h": fill_rate_1h, "fill_rate_3h": fill_rate_3h, "fill_rate_6h": fill_rate_6h, "fill_rate_12h": 0, "fill_acceleration": fill_acceleration, "rolling_fill_3h": rolling_fill_3h, "rolling_fill_6h": rolling_fill_6h, "rolling_fill_12h": rolling_fill_12h, "rolling_fill_24h": rolling_fill_24h, "rolling_weight_3h": weight, "hour_of_day": hour_of_day, "day_of_week": day_of_week, "week_number": week_number, "is_weekend": int(day_of_week >= 5), "is_rush_hour": int(hour_of_day in [7,8,9,12,13,17,18,19,20]), "is_night": int(hour_of_day in [0,1,2,3,4,5]), "sin_hour": np.sin(2 * np.pi * hour_of_day / 24), "cos_hour": np.cos(2 * np.pi * hour_of_day / 24), "sin_day": np.sin(2 * np.pi * day_of_week / 7), "cos_day": np.cos(2 * np.pi * day_of_week / 7), "hours_since_collection": hours_since_collection, "hours_to_full": hours_to_full, "bin_type_residential":int(bin_type == "residential"), "bin_type_commercial": int(bin_type == "commercial"), "location_urban": int(location == "urban"), "location_suburban": int(location == "suburban"), "location_mall": int(location == "mall"), "zone_north": int(zone == "north"), "zone_south": int(zone == "south"), "zone_east": int(zone == "east"), "zone_west": int(zone == "west"), "zone_central": int(zone == "central"), } def safe_predict(model, features, row_dict): df = pd.DataFrame([row_dict]) for col in features: if col not in df.columns: df[col] = 0 return model.predict(df[features]) # ── PREDICTION FUNCTION 1: Current fill status ──────────────────────────────── def predict_current_status( ultrasonic, weight, fill_now, hour_of_day, day_of_week, bin_type, location, zone, fill_rate_1h, hours_since_collection ): try: row = build_row( ultrasonic=ultrasonic, weight=weight, fill_now=fill_now, hour_of_day=int(hour_of_day), day_of_week=int(day_of_week), bin_type=bin_type, location=location, zone=zone, fill_rate_1h=fill_rate_1h, hours_since_collection=int(hours_since_collection) ) fill_pred = float(np.clip(safe_predict(reg_model, m1_features, row)[0], 0, 100)) status_idx = int(safe_predict(cls_model, m1_features, row)[0]) proba = cls_model.predict_proba( pd.DataFrame([row])[[f for f in m1_features if f in row or True]] )[0] # Fix proba dataframe df_row = pd.DataFrame([row]) for col in m1_features: if col not in df_row.columns: df_row[col] = 0 proba = cls_model.predict_proba(df_row[m1_features])[0] confidence = float(proba.max()) * 100 result = f""" ## 📊 Current Bin Status | Metric | Value | |--------|-------| | **Predicted Fill** | {fill_pred:.1f}% | | **Status** | {STATUS_NAMES[status_idx]} | | **Action** | {STATUS_ACTION[status_idx]} | | **Confidence** | {confidence:.1f}% | ### Probability Breakdown - 🟢 GREEN (Empty): {proba[0]*100:.1f}% - 🟡 YELLOW (Filling): {proba[1]*100:.1f}% - 🔴 RED (Full): {proba[2]*100:.1f}% {"⚠️ **DISPATCH TRUCK NOW**" if status_idx == 2 else "✅ No immediate action required"} """ return result except Exception as e: return f"❌ Error: {str(e)}" # ── PREDICTION FUNCTION 2: Flow forecast ───────────────────────────────────── def predict_forecast( ultrasonic, weight, fill_now, hour_of_day, day_of_week, bin_type, location, zone, fill_rate_1h, fill_rate_3h ): try: row = build_row( ultrasonic=ultrasonic, weight=weight, fill_now=fill_now, hour_of_day=int(hour_of_day), day_of_week=int(day_of_week), bin_type=bin_type, location=location, zone=zone, fill_rate_1h=fill_rate_1h, fill_rate_3h=fill_rate_3h ) df_row = pd.DataFrame([row]) for col in m2_features: if col not in df_row.columns: df_row[col] = 0 df_row = df_row[m2_features] p6h = float(np.clip(forecast_6h.predict(df_row)[0], 0, 100)) p12h = float(np.clip(forecast_12h.predict(df_row)[0], 0, 100)) # Urgency if p6h >= 80: urgency = "🔴 HIGH — Collect within 6 hours" elif p12h >= 80: urgency = "🟡 MEDIUM — Collect within 12 hours" else: urgency = "🟢 LOW — No collection needed soon" bar_now = "█" * int(fill_now / 5) + "░" * (20 - int(fill_now / 5)) bar_6h = "█" * int(p6h / 5) + "░" * (20 - int(p6h / 5)) bar_12h = "█" * int(p12h / 5) + "░" * (20 - int(p12h / 5)) result = f""" ## 📈 Fill Level Forecast | Time | Predicted Fill | Bar | |------|---------------|-----| | **Now** | {fill_now:.1f}% | `{bar_now}` | | **+6 hours** | {p6h:.1f}% | `{bar_6h}` | | **+12 hours** | {p12h:.1f}% | `{bar_12h}` | ### 🚛 Collection Urgency **{urgency}** ### For Route Optimization ``` bin_fill_now: {fill_now:.1f}% bin_fill_6h: {p6h:.1f}% bin_fill_12h: {p12h:.1f}% dispatch_urgent: {str(p6h >= 80).lower()} ``` """ return result except Exception as e: return f"❌ Error: {str(e)}" # ── BUILD GRADIO UI ──────────────────────────────────────────────────────────── with gr.Blocks(title="Smart Bin AI") as demo: gr.Markdown(""" # 🗑️ Smart Bin AI — Waste Management Intelligence **Model 1:** Predict current fill level (GREEN / YELLOW / RED) **Model 2:** Forecast fill level 6h and 12h into the future > Built with Random Forest + Gradient Boosting on 72,000 sensor readings """) # ── TAB 1: Current Status ───────────────────────────────────────────────── with gr.Tab("📡 Current Fill Status (Model 1)"): gr.Markdown("### Enter live sensor readings to get current bin status") with gr.Row(): with gr.Column(): ultra1 = gr.Slider(4, 65, value=30, label="Ultrasonic Distance (cm) — lower = more full") weight1 = gr.Slider(0, 36, value=15, label="Weight (kg)") fill1 = gr.Slider(0, 100, value=50, label="Current Fill % (from previous reading)") rate1 = gr.Slider(0, 10, value=1.0, label="Fill Rate per hour (%/hr)", step=0.1) hours_sc = gr.Slider(0, 72, value=24, label="Hours Since Last Collection") with gr.Column(): hour1 = gr.Slider(0, 23, value=12, label="Hour of Day (0–23)", step=1) day1 = gr.Slider(0, 6, value=1, label="Day of Week (0=Mon, 6=Sun)", step=1) btype1 = gr.Dropdown(["residential", "commercial"], value="commercial", label="Bin Type") loc1 = gr.Dropdown(["urban", "suburban", "mall"], value="urban", label="Location") zone1 = gr.Dropdown(["north","south","east","west","central"], value="central", label="Zone") btn1 = gr.Button("🔍 Predict Current Status", variant="primary", size="lg") output1 = gr.Markdown() btn1.click( fn=predict_current_status, inputs=[ultra1, weight1, fill1, hour1, day1, btype1, loc1, zone1, rate1, hours_sc], outputs=output1 ) gr.Examples( examples=[ [55, 2.5, 8, 9, 1, "residential", "suburban", "north", 0.3, 48], [32, 15, 50, 13, 2, "commercial", "urban", "central", 2.0, 24], [8, 30, 90, 18, 4, "commercial", "urban", "central", 4.0, 6], ], inputs=[ultra1, weight1, fill1, hour1, day1, btype1, loc1, zone1, rate1, hours_sc], label="Try these examples" ) # ── TAB 2: Flow Forecast ────────────────────────────────────────────────── with gr.Tab("📈 Fill Forecast (Model 2)"): gr.Markdown("### Predict how full this bin will be in 6 and 12 hours") with gr.Row(): with gr.Column(): ultra2 = gr.Slider(4, 65, value=30, label="Ultrasonic Distance (cm)") weight2 = gr.Slider(0, 36, value=15, label="Weight (kg)") fill2 = gr.Slider(0, 100, value=50, label="Current Fill %") rate1h = gr.Slider(0, 10, value=1.5, label="Fill Rate last 1h (%/hr)", step=0.1) rate3h = gr.Slider(0, 30, value=4.0, label="Fill Rate last 3h (total)", step=0.1) with gr.Column(): hour2 = gr.Slider(0, 23, value=12, label="Hour of Day", step=1) day2 = gr.Slider(0, 6, value=1, label="Day of Week", step=1) btype2 = gr.Dropdown(["residential","commercial"], value="commercial", label="Bin Type") loc2 = gr.Dropdown(["urban","suburban","mall"], value="urban", label="Location") zone2 = gr.Dropdown(["north","south","east","west","central"], value="central", label="Zone") btn2 = gr.Button("📈 Forecast Fill Level", variant="primary", size="lg") output2 = gr.Markdown() btn2.click( fn=predict_forecast, inputs=[ultra2, weight2, fill2, hour2, day2, btype2, loc2, zone2, rate1h, rate3h], outputs=output2 ) gr.Examples( examples=[ [52, 4, 15, 7, 1, "residential", "suburban", "north", 0.3, 0.8], [30, 16, 55, 11, 2, "commercial", "urban", "central", 2.8, 7.5], [12, 27, 78, 17, 4, "commercial", "urban", "central", 3.5, 9.0], ], inputs=[ultra2, weight2, fill2, hour2, day2, btype2, loc2, zone2, rate1h, rate3h], label="Try these examples" ) # ── TAB 3: API Docs for Node.js ─────────────────────────────────────────── with gr.Tab("🔌 Node.js API Docs"): gr.Markdown(""" ## Using This As a Node.js API Hugging Face Spaces exposes your Gradio app as a REST API automatically. Install the client: `npm install @gradio/client` ### Call Model 1 (Current Status) ```javascript const { Client } = require("@gradio/client"); async function getBinStatus(sensorData) { const client = await Client.connect("YOUR_HF_USERNAME/smart-bin-ai"); const result = await client.predict("/predict_current_status", { ultrasonic: sensorData.ultrasonic, weight: sensorData.weight, fill_now: sensorData.fill_percent, hour_of_day: new Date().getHours(), day_of_week: new Date().getDay(), bin_type: sensorData.bin_type || "commercial", location: sensorData.location || "urban", zone: sensorData.zone || "central", fill_rate_1h: sensorData.fill_rate || 0, hours_since_collection: sensorData.hours_since_collection || 24, }); return result.data; } ``` ### Call Model 2 (Forecast) ```javascript async function getForecast(sensorData) { const client = await Client.connect("YOUR_HF_USERNAME/smart-bin-ai"); const result = await client.predict("/predict_forecast", { ultrasonic: sensorData.ultrasonic, weight: sensorData.weight, fill_now: sensorData.fill_percent, hour_of_day: new Date().getHours(), day_of_week: new Date().getDay(), bin_type: sensorData.bin_type || "commercial", location: sensorData.location || "urban", zone: sensorData.zone || "central", fill_rate_1h: sensorData.fill_rate_1h || 0, fill_rate_3h: sensorData.fill_rate_3h || 0, }); return result.data; } ``` ### Replace `YOUR_HF_USERNAME` with your actual Hugging Face username. ### Replace `smart-bin-ai` with your Space name if you chose a different one. """) gr.Markdown(""" --- Built for Smart Waste Management | Random Forest (Model 1) + Gradient Boosting (Model 2) | 72,000 training rows | GroupKFold validated """) # Launch demo.launch(server_name="0.0.0.0", server_port=7860)