Tilo / app.py
Coots's picture
Update app.py
389dccc verified
raw
history blame
6.17 kB
import gradio as gr
import joblib
import json
import math
import re
from deep_translator import GoogleTranslator
import warnings
warnings.filterwarnings("ignore")
# Load models
from xgboost import Booster
xgb = Booster()
xgb.load_model("xgb_model.json")
rf = joblib.load("rf_model.pkl") # Needs scikit-learn
# Load tile catalog and sizes
with open("tile_catalog.json", "r", encoding="utf-8") as f:
tile_catalog = json.load(f)
with open("tile_sizes.json", "r", encoding="utf-8") as f:
tile_sizes = json.load(f)
tile_size_map = {s["label"].lower().replace(" ", ""): s["area_sqft"] for s in tile_sizes}
def translate(text, lang="en"):
try:
return GoogleTranslator(source="auto", target=lang).translate(text)
except:
return text
def extract_tile_area(msg, unit):
msg = msg.lower().replace("ร—", "x").replace("into", "x").replace("*", "x")
msg = msg.replace("mm", "").replace("ft", "").replace("feet", "").strip()
if "x" in msg:
parts = re.split(r"x", msg)
if len(parts) == 2:
try:
val1 = float(re.sub(r"[^\d.]", "", parts[0]))
val2 = float(re.sub(r"[^\d.]", "", parts[1]))
if unit == "mm":
sqft = (val1 * val2) / 92903.04
else:
sqft = val1 * val2
return round(sqft, 2)
except:
return None
else:
# Single value fallback for ft
try:
val = float(re.sub(r"[^\d.]", "", msg))
return val if unit == "ft" else None
except:
return None
def chat_fn(message, history, user_state={}):
message = message.strip().lower()
if "lang" not in user_state:
try:
user_state["lang"] = GoogleTranslator(source="auto", target="en").detect(message)
except:
user_state["lang"] = "en"
lang = user_state["lang"]
def reply(text): return translate(text, lang)
# Greeting handler
if message in ["hi", "hello", "hey", "start", "help"]:
user_state.clear()
return reply("๐Ÿ‘‹ Hello! Are you planning for Floor or Wall tiles?"), None, user_state
if message in ["floor", "wall"]:
user_state.clear()
user_state["tile_type"] = message.capitalize()
user_state["step"] = "get_length"
return reply(f"Great! You chose {user_state['tile_type']} tiles.\nPlease enter the **length** of the space in feet:"), None, user_state
# Stepwise logic
if user_state.get("step") == "get_length":
try:
user_state["length"] = float(re.sub(r"[^\d.]", "", message))
user_state["step"] = "get_width"
return reply("Now enter the **width** of the space in feet:"), None, user_state
except:
return reply("Please enter a valid number for length (e.g. 12.5):"), None, user_state
if user_state.get("step") == "get_width":
try:
user_state["width"] = float(re.sub(r"[^\d.]", "", message))
user_state["area"] = round(user_state["length"] * user_state["width"], 2)
user_state["step"] = "get_unit"
return reply(f"Your total area is {user_state['area']} sq.ft.\nWould you like to enter tile size in **mm** or **ft**?"), None, user_state
except:
return reply("Please enter a valid number for width (e.g. 10):"), None, user_state
if user_state.get("step") == "get_unit":
if message not in ["mm", "ft"]:
return reply("Please type either **mm** or **ft** to choose your preferred unit."), None, user_state
user_state["unit"] = message
user_state["step"] = "get_tile_size"
return reply(f"Enter the tile size in {message.upper()} (e.g. 600x600 or 2x2):"), None, user_state
if user_state.get("step") == "get_tile_size":
area_per_tile = extract_tile_area(message, user_state["unit"])
if not area_per_tile:
return reply("I couldn't understand that size. Try something like `600x600`, `2x2`, or just `4` if square in ft."), None, user_state
user_state["tile_area"] = area_per_tile
user_state["step"] = "done"
area_needed = user_state["area"]
tile_type = user_state["tile_type"]
tile_needed = math.ceil((area_needed / area_per_tile) * 1.1)
# Suggest tiles
best = []
for tile in tile_catalog:
if tile["type"].lower() == tile_type.lower():
per_box = tile["coverage"] / area_per_tile
if per_box > 0:
boxes = math.ceil(tile_needed / per_box)
total = boxes * tile["price"]
best.append({
"name": tile["name"],
"size": tile["size"],
"price": tile["price"],
"boxes": boxes,
"total": total,
"url": tile["url"]
})
best.sort(key=lambda x: x["total"])
top3 = best[:3]
summary = f"""๐Ÿงฑ Tile Type: {tile_type}
๐Ÿ“ Area: {area_needed} sq.ft
๐Ÿงฎ Tile Size Area: {round(area_per_tile, 2)} sq.ft
๐Ÿ”ข Tiles Needed: {tile_needed} (10% extra included)
๐ŸŽฏ Top Recommendations:
"""
for i, t in enumerate(top3, 1):
summary += f"{i}. {t['name']} ({t['size']}) โ€“ โ‚น{t['price']} per box, ~{t['boxes']} boxes\n ๐Ÿ”— {t['url']}\n"
summary += "\nType 'Floor' or 'Wall' to start over."
user_state["summary"] = summary
return reply(summary), None, user_state
return reply("๐Ÿ‘‹ Please type 'Floor' or 'Wall' to begin tile estimation."), None, user_state
# UI setup
with gr.Blocks() as demo:
gr.ChatInterface(
fn=chat_fn,
title="๐Ÿงฑ TileBot โ€“ Smart Tile Estimator",
description=(
"Plan your tile needs easily. Just type `hi` to begin or start with `Floor` or `Wall`.\n"
"Iโ€™ll ask for room size, tile size, and recommend the best fit tiles."
),
theme=gr.themes.Soft(),
retry_btn=None,
undo_btn=None
)
demo.launch()