Spaces:
Sleeping
Sleeping
| # app.py — Entrepreneurial Readiness Trainer Bot (future-proof Gradio version) | |
| import gradio as gr | |
| import random | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| import skops.io as sio | |
| import json | |
| import os | |
| import io | |
| import base64 | |
| from datetime import datetime | |
| from huggingface_hub import hf_hub_download | |
| # ---- CONFIG ---- | |
| MODEL_REPO = "MaryahGreene/entrepreneur_readiness_modelt" | |
| MODEL_FILE = "xgboost_readiness.skops" | |
| COLS_FILE = "feature_columns.json" | |
| LEADERBOARD_FILE = "leaderboard.csv" | |
| # ---- LOAD MODEL ---- | |
| model = None | |
| feature_columns = [] | |
| try: | |
| model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE) | |
| cols_path = hf_hub_download(repo_id=MODEL_REPO, filename=COLS_FILE) | |
| trusted_types = ["xgboost.core.Booster", "xgboost.sklearn.XGBRegressor"] | |
| with open(model_path, "rb") as f: | |
| model = sio.load(f, trusted=trusted_types) | |
| with open(cols_path, "r") as f: | |
| feature_columns = json.load(f) | |
| except Exception as e: | |
| print("⚠️ Model failed to load:", str(e)) | |
| model = None | |
| feature_columns = [] | |
| # ---- HELPERS ---- | |
| def make_scenario(): | |
| assets_choices = [ | |
| "N/A", | |
| f"car({random.randint(200,5000)})", | |
| f"house({random.randint(20000,200000)})", | |
| f"trust({random.randint(50000,1000000)})" | |
| ] | |
| return { | |
| "savings_account": round(random.uniform(0, 15000), 2), | |
| "monthly_income": round(random.uniform(0, 12000), 2), | |
| "monthly_bills": round(random.uniform(0, 12000), 2), | |
| "monthly_entertainment": round(random.uniform(0, 2000), 2), | |
| "sales_skills": random.randint(1, 10), | |
| "age": random.randint(16, 65), | |
| "dependents": random.randint(0, 5), | |
| "assets": random.choice(assets_choices), | |
| "risk_level": random.randint(1, 10), | |
| "confidence_level": random.randint(1, 10), | |
| "idea_difficulty": random.randint(1, 10), | |
| } | |
| def model_predict_percent(scenario): | |
| if model is None or not feature_columns: | |
| return None | |
| df = pd.DataFrame([scenario]) | |
| df = pd.get_dummies(df, columns=["assets"], dummy_na=False) | |
| df = df.reindex(columns=feature_columns, fill_value=0) | |
| pred = model.predict(df)[0] | |
| return float(np.clip(pred * 10.0, 0, 100)) | |
| def tips_for_scenario(scenario, percent): | |
| tips = [] | |
| if percent <= 50: | |
| if scenario["sales_skills"] < 5: tips.append("📈 Boost sales skills") | |
| if scenario["risk_level"] < 5: tips.append("⚖️ Be more comfortable with risk") | |
| if scenario["confidence_level"] < 5: tips.append("💪 Build your confidence") | |
| if scenario["idea_difficulty"] > 6: tips.append("🎯 Simplify your business idea") | |
| if scenario["monthly_income"] < scenario["monthly_bills"]: tips.append("💵 Balance income vs. expenses") | |
| advice = "⚡ Areas to improve:\n" + "\n".join([f"- {t}" for t in tips]) if tips else "⚡ Keep building your foundation!" | |
| vibe = "💡 Needs work, but you can level up!" | |
| elif percent >= 60: | |
| advice = "✅ You're on track! Keep pushing forward 🚀" | |
| vibe = "🚀🔥 You're launch-ready!" | |
| else: | |
| advice = "🌱 You're close — polish up a few areas." | |
| vibe = "🌕 Solid potential, keep growing!" | |
| return vibe, advice | |
| def gauge_to_base64(percent): | |
| fig = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=percent, | |
| number={'suffix': "%"}, | |
| title={'text': "Model Readiness"}, | |
| gauge={ | |
| 'axis': {'range': [0, 100]}, | |
| 'bar': {'color': "purple"}, | |
| 'steps': [ | |
| {'range': [0, 50], 'color': "lightpink"}, | |
| {'range': [50, 60], 'color': "plum"}, | |
| {'range': [60, 100], 'color': "mediumorchid"} | |
| ], | |
| } | |
| )) | |
| fig.update_layout(height=260) | |
| buf = io.BytesIO() | |
| fig.write_image(buf, format="png") | |
| buf.seek(0) | |
| img_b64 = base64.b64encode(buf.read()).decode("utf-8") | |
| return f"" | |
| # ---- LEADERBOARD ---- | |
| def log_result(user, guess, percent, diff, correct, mode): | |
| record = { | |
| "timestamp": datetime.utcnow().isoformat(), | |
| "user": user if user else "anonymous", | |
| "guess": guess, | |
| "model_score": percent, | |
| "diff": diff, | |
| "correct": correct, | |
| "mode": mode, | |
| } | |
| df = pd.DataFrame([record]) | |
| if os.path.exists(LEADERBOARD_FILE): | |
| df.to_csv(LEADERBOARD_FILE, mode="a", header=False, index=False) | |
| else: | |
| df.to_csv(LEADERBOARD_FILE, index=False) | |
| def show_leaderboard(): | |
| if os.path.exists(LEADERBOARD_FILE): | |
| return pd.read_csv(LEADERBOARD_FILE).tail(10) | |
| return pd.DataFrame(columns=["timestamp","user","guess","model_score","diff","correct","mode"]) | |
| def show_top_players(): | |
| if os.path.exists(LEADERBOARD_FILE): | |
| df = pd.read_csv(LEADERBOARD_FILE) | |
| top = df.groupby("user")["diff"].mean().reset_index() | |
| top = top.sort_values("diff", ascending=True).head(5) | |
| top = top.rename(columns={"diff":"avg_diff"}) | |
| # Medal emojis | |
| medals = ["🥇","🥈","🥉","🎖️","🏅"] | |
| top["rank"] = [medals[i] for i in range(len(top))] | |
| return top[["rank","user","avg_diff"]] | |
| return pd.DataFrame(columns=["rank","user","avg_diff"]) | |
| # ---- CHAT LOGIC ---- | |
| def start_game(mode="Easy"): | |
| scenario = make_scenario() | |
| percent = model_predict_percent(scenario) | |
| text = "**Scenario (decide readiness 0-100%)**\n" + "\n".join([f"- {k}: {v}" for k,v in scenario.items()]) | |
| prompt = f"\nGuess the entrepreneurial readiness (0-100%). Mode: {mode}." | |
| chat = [{"role":"assistant","content":f"{text}\n{prompt}"}] | |
| state = {"scenario": scenario, "mode": mode, "awaiting_guess": True, "answer": percent} | |
| return chat, state | |
| def user_guess(history, user_message, state, username="player"): | |
| try: | |
| guess = float(user_message.strip().rstrip("%")) | |
| except: | |
| history.append({"role":"assistant","content":"⚠️ Please reply with a number 0–100."}) | |
| return history, state | |
| percent = state["answer"] | |
| mode = state.get("mode","Easy") | |
| tol = 10 if mode=="Easy" else 7 if mode=="Medium" else 5 | |
| diff = abs(guess - percent) | |
| correct = diff <= tol | |
| if mode=="Free": | |
| verdict = f"Model score: {percent:.1f}% (Free mode)." | |
| else: | |
| if correct: | |
| verdict = f"✅ Correct! Your guess {guess:.1f}% vs model {percent:.1f}% (within ±{tol})." | |
| else: | |
| verdict = f"❌ Off. You guessed {guess:.1f}%, model is {percent:.1f}% (diff {diff:.1f}%)." | |
| vibe, advice = tips_for_scenario(state["scenario"], percent) | |
| gauge_img = gauge_to_base64(percent) | |
| history.extend([ | |
| {"role":"user","content":user_message}, | |
| {"role":"assistant","content":verdict}, | |
| {"role":"assistant","content":gauge_img}, | |
| {"role":"assistant","content":f"{vibe}\n\n{advice}"} | |
| ]) | |
| log_result(username, guess, percent, diff, bool(correct), mode) | |
| state["awaiting_guess"] = False | |
| return history, state | |
| def handle_user(history, message, state, username): | |
| msg = message.strip().lower() | |
| if msg in ["start","new","begin"]: | |
| return start_game(state.get("mode","Easy")) | |
| if msg.startswith("mode:"): | |
| m = msg.split("mode:")[1].strip().capitalize() | |
| if m not in ["Easy","Medium","Hard","Free"]: | |
| history.append({"role":"assistant","content":"Unknown mode. Try Easy, Medium, Hard, Free."}) | |
| return history, state | |
| return start_game(m) | |
| if msg=="next": | |
| if state.get("awaiting_guess",False): | |
| history.append({"role":"assistant","content":"⚠️ Guess first before 'next'."}) | |
| return history, state | |
| return start_game(state.get("mode","Easy")) | |
| if state.get("awaiting_guess",False): | |
| return user_guess(history, message, state, username) | |
| return user_guess(history, message, state, username) | |
| # ---- GRADIO UI ---- | |
| with gr.Blocks(theme=gr.themes.Soft(primary_hue="pink",secondary_hue="purple")) as demo: | |
| gr.Markdown("## 🎮 Entrepreneurial Readiness Trainer Bot") | |
| gr.Markdown("Type `start` to begin, or `mode:easy|medium|hard|free` to change difficulty.") | |
| nickname = gr.Textbox(label="Nickname", value="player") | |
| chatbot = gr.Chatbot(label="Trainer Bot", height=520, type="messages") | |
| user_input = gr.Textbox(placeholder="Type 'start' to begin, or enter your guess…", show_label=False) | |
| state = gr.State({"scenario":None,"mode":"Easy","awaiting_guess":False,"answer":None}) | |
| mode_selector = gr.Radio(["Easy","Medium","Hard","Free"], value="Easy", label="Mode") | |
| new_btn = gr.Button("Start / New Scenario") | |
| with gr.Accordion("📊 Leaderboard", open=False): | |
| with gr.Row(): | |
| leaderboard_btn = gr.Button("Refresh Recent") | |
| top_btn = gr.Button("Show Top Players") | |
| leaderboard_display = gr.Dataframe(interactive=False) | |
| def submit_message(msg, hist, st, mode, user): | |
| st["mode"] = mode | |
| if hist is None: hist = [] | |
| hist.append({"role":"user","content":msg}) | |
| updated, st_new = handle_user(hist, msg, st, user) | |
| return updated, "", st_new | |
| def new_game(mode): | |
| chat, st = start_game(mode) | |
| return chat, st | |
| user_input.submit(submit_message, | |
| inputs=[user_input, chatbot, state, mode_selector, nickname], | |
| outputs=[chatbot, user_input, state]) | |
| new_btn.click(new_game, inputs=[mode_selector], outputs=[chatbot, state]) | |
| leaderboard_btn.click(show_leaderboard, outputs=[leaderboard_display]) | |
| top_btn.click(show_top_players, outputs=[leaderboard_display]) | |
| demo.launch() |