Tilo / app.py
Coots's picture
Update app.py
1f0bb49 verified
raw
history blame
5.3 kB
import gradio as gr
import joblib
import numpy as np
import json
import math
import re
from fpdf import FPDF
import tempfile
from deep_translator import GoogleTranslator
from xgboost import Booster
# Load models
xgb = Booster()
xgb.load_model("xgb_model.json")
rf = joblib.load("rf_model.pkl")
# Load tile catalog and size mappings
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 remove_emojis(text):
return re.sub(r'[^\x00-\x7F]+', '', text)
def create_pdf(text):
pdf = FPDF()
pdf.add_page()
pdf.add_font("FreeSans", "", "FreeSans.ttf", uni=True)
pdf.set_font("FreeSans", size=12)
pdf.cell(0, 10, "Tile Estimate Report", ln=True, align="C")
pdf.ln(5)
for line in text.strip().split("\n"):
pdf.multi_cell(0, 10, remove_emojis(line))
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
pdf.output(tmp.name)
return tmp.name
def extract_tile_area(msg):
msg = msg.lower().replace("ร—", "x").replace("into", "x").replace("*", "x")
msg = msg.replace("ft", "").replace("feet", "").replace("mm", "").strip()
if "x" in msg:
parts = msg.split("x")
if len(parts) == 2:
try:
val1 = float(re.sub(r"[^\d.]", "", parts[0]))
val2 = float(re.sub(r"[^\d.]", "", parts[1]))
if val1 > 20: # treat as mm
sqft = (val1 * val2) / 92903.04 # mmยฒ to ftยฒ
else: # treat as feet
sqft = val1 * val2
return round(sqft, 2)
except:
return None
return None
def chat_fn(message, history, user_state={}):
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)
if "step" not in user_state:
if message.lower() in ["floor", "wall"]:
user_state["tile_type"] = message.capitalize()
user_state["step"] = "get_area"
return reply(f"โœ… Great! You selected {user_state['tile_type']} tiles.\nPlease enter the total area in sq.ft:"), None, user_state
return reply("๐Ÿ‘‹ Hello! Are you looking for Floor or Wall tiles?"), None, user_state
if user_state["step"] == "get_area":
try:
user_state["area"] = float(message)
user_state["step"] = "get_tile_size"
return reply("๐Ÿ“ Now enter the tile size like 2 x 2 ft or 600x600 MM or 200 * 200:"), None, user_state
except:
return reply("โ— Please enter a number for area (e.g. 120)."), None, user_state
if user_state["step"] == "get_tile_size":
area = extract_tile_area(message)
if area is None:
examples = "2 x 2 ft, 600x600 MM, 200*200, 1.5x1.5"
return reply(f"โ— Oops, I didn't catch that size. Try formats like: {examples}"), None, user_state
user_state["tile_area"] = area
user_state["step"] = "done"
area_needed = user_state["area"]
tile_type = user_state["tile_type"]
tile_needed = math.ceil((area_needed / area) * 1.1)
best = []
for tile in tile_catalog:
if tile["type"].lower() == tile_type.lower():
per_box = tile["coverage"] / area
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: {round(area, 2)} sq.ft
๐Ÿ”ข Total Tiles Needed (10% buffer): {tile_needed}
๐ŸŽฏ Top Recommendations:
"""
for i, t in enumerate(top3, 1):
summary += f"\n{i}. {t['name']} ({t['size']})\n โ‚น{t['price']}/box โ†’ ~{t['boxes']} boxes\n {t['url']}\n"
summary += "\nSay 'Floor' or 'Wall' to start a new estimate."
pdf_path = create_pdf(summary)
return reply(summary), [pdf_path], {}
return reply("Say 'Floor' or 'Wall' to begin again."), None, {}
# Launch the bot
with gr.Blocks() as demo:
chatbot = gr.ChatInterface(
fn=chat_fn,
title="๐Ÿงฑ TileBot โ€“ ChatGPT-Style Tile Estimator",
description="Chat like a human. Enter sizes naturally like 200x200, 2 x 2 ft, or 600x600 MM.\nSay 'Floor' or 'Wall' to begin.",
type="messages",
additional_outputs=[gr.File(label="๐Ÿ“„ Download Report")]
)
demo.launch()