Coots commited on
Commit
389dccc
ยท
verified ยท
1 Parent(s): d00ed7b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -83
app.py CHANGED
@@ -5,16 +5,17 @@ import math
5
  import re
6
  from deep_translator import GoogleTranslator
7
  import warnings
8
- import xgboost as xgb
9
 
10
  warnings.filterwarnings("ignore")
11
 
12
  # Load models
13
- xgb_model = xgb.Booster()
14
- xgb_model.load_model("xgb_model.json")
15
- rf = joblib.load("rf_model.pkl")
16
 
17
- # Load catalog and sizes
 
 
18
  with open("tile_catalog.json", "r", encoding="utf-8") as f:
19
  tile_catalog = json.load(f)
20
 
@@ -30,28 +31,33 @@ def translate(text, lang="en"):
30
  return text
31
 
32
  def extract_tile_area(msg, unit):
33
- msg = msg.lower().replace("feet", "").replace("ft", "").replace("mm", "").strip()
34
- msg = re.sub(r"\s*(x|\u00d7|\*|into)\s*", "x", msg)
35
- parts = msg.split("x")
36
-
37
- try:
38
  if len(parts) == 2:
39
- val1 = float(re.sub(r"[^\d.]", "", parts[0]))
40
- val2 = float(re.sub(r"[^\d.]", "", parts[1]))
41
- sqft = (val1 * val2) / 92903.04 if unit == "mm" else val1 * val2
42
- return round(sqft, 2)
43
- elif len(parts) == 1 and parts[0]:
44
- return float(re.sub(r"[^\d.]", "", parts[0]))
45
- else:
 
 
 
 
 
 
 
 
 
46
  return None
47
- except:
48
- return None
49
 
50
  def chat_fn(message, history, user_state={}):
51
- if "step" not in user_state and message.strip().lower() in ["floor", "wall"]:
52
- user_state.clear()
53
 
54
- # Language detection
55
  if "lang" not in user_state:
56
  try:
57
  user_state["lang"] = GoogleTranslator(source="auto", target="en").detect(message)
@@ -60,59 +66,59 @@ def chat_fn(message, history, user_state={}):
60
  lang = user_state["lang"]
61
  def reply(text): return translate(text, lang)
62
 
63
- # Greeting handling
64
- if message.strip().lower() in ["hi", "hello", "hey", "start", "tile", "help"]:
65
- return reply("\U0001F44B Hello! I'm TileBot, your assistant for tile estimates.\nAre you planning for Floor or Wall tiles?"), None, user_state
 
66
 
67
- # Start conversation
68
- if "step" not in user_state:
69
- if message.lower() in ["floor", "wall"]:
70
- user_state["tile_type"] = message.capitalize()
71
- user_state["step"] = "get_length"
72
- return reply(f"You selected {user_state['tile_type']} tiles.\nEnter the length of the space in feet:"), None, user_state
73
- return reply("Hi! Are you planning for Floor or Wall tiles?"), None, user_state
74
 
75
- if user_state["step"] == "get_length":
 
76
  try:
77
- user_state["length"] = float(message)
78
  user_state["step"] = "get_width"
79
- return reply("Now enter the width of the space in feet:"), None, user_state
80
  except:
81
- return reply("That doesn't seem like a number. Please enter the length in feet (e.g. 12)."), None, user_state
82
 
83
- if user_state["step"] == "get_width":
84
  try:
85
- user_state["width"] = float(message)
86
  user_state["area"] = round(user_state["length"] * user_state["width"], 2)
87
  user_state["step"] = "get_unit"
88
- return reply("Got it! Would you like to enter tile size in mm or ft?"), None, user_state
89
  except:
90
- return reply("Please enter a valid width in feet (e.g. 10)."), None, user_state
91
 
92
- if user_state["step"] == "get_unit":
93
- if message.lower() not in ["mm", "ft"]:
94
- return reply("Please type either mm or ft to choose your preferred unit."), None, user_state
95
- user_state["unit"] = message.lower()
96
  user_state["step"] = "get_tile_size"
97
- unit_label = "mm" if user_state["unit"] == "mm" else "feet"
98
- return reply(f"Please enter the tile size in {unit_label} (e.g. 600 x 600, 2 x 2, or 4):"), None, user_state
99
 
100
- if user_state["step"] == "get_tile_size":
101
- area = extract_tile_area(message, user_state["unit"])
102
- if not area or area <= 0:
103
- return reply("\u274C That tile size didnโ€™t work. Try something like:\n- `600 x 600`\n- `2 x 2`\n- `4` (if in ft)"), None, user_state
104
 
105
- user_state["tile_area"] = area
106
  user_state["step"] = "done"
107
 
108
  area_needed = user_state["area"]
109
  tile_type = user_state["tile_type"]
110
- tile_needed = math.ceil((area_needed / area) * 1.1)
111
 
 
112
  best = []
113
  for tile in tile_catalog:
114
  if tile["type"].lower() == tile_type.lower():
115
- per_box = tile["coverage"] / area
116
  if per_box > 0:
117
  boxes = math.ceil(tile_needed / per_box)
118
  total = boxes * tile["price"]
@@ -128,44 +134,35 @@ def chat_fn(message, history, user_state={}):
128
  best.sort(key=lambda x: x["total"])
129
  top3 = best[:3]
130
 
131
- summary = f"""
132
- \U0001F9F1 Tile Type: {tile_type}
133
- \U0001F4D0 Space: {user_state['length']} ft x {user_state['width']} ft
134
- \U0001F4CF Area to Cover: {area_needed} sq.ft
135
- \U0001F9EE Tile Size Area: {round(area, 2)} sq.ft
136
- \U0001F522 Estimated Tiles Needed: {tile_needed} (with 10% buffer)
137
 
138
- \U0001F3AF Top Suggestions:
139
  """
140
  for i, t in enumerate(top3, 1):
141
- summary += f"\n{i}. {t['name']} ({t['size']})\n โ‚น{t['price']} per box โ†’ ~{t['boxes']} boxes\n {t['url']}\n"
142
 
143
- summary += "\nType 'Floor' or 'Wall' to start another estimate."
 
 
144
  return reply(summary), None, user_state
145
 
146
- return reply("I didn't understand that. Please continue or type 'Floor' or 'Wall' to start over."), None, user_state
147
-
148
- # Interface
149
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", radius_size="lg")) as demo:
150
- gr.Markdown(
151
- """
152
- # ๐Ÿงฑ <span style="color:#3B82F6">TileBot</span> โ€“ Smart Tile Estimator
153
- Welcome! I'm here to help you estimate tiles for your floor or wall.
154
-
155
- ๐Ÿ”น Start with **"Floor"** or **"Wall"**
156
- ๐Ÿ“€ Enter **length and width** (in feet)
157
- ๐Ÿ“ Then your tile size (in `mm` or `ft`) โ€“ like `600 x 600`, `2x2`, or `4`
158
- ๐Ÿ“‹ Iโ€™ll recommend tiles and calculate how many you need.
159
- """,
160
- elem_id="header"
161
- )
162
 
163
- chatbot = gr.ChatInterface(
 
 
164
  fn=chat_fn,
165
- type="messages",
166
- chatbot=gr.Chatbot(height=500, bubble_full_width=False),
 
 
 
 
 
 
167
  )
168
 
169
- gr.Markdown("---\nNeed to start over? Just type **Floor** or **Wall** again.")
170
-
171
  demo.launch()
 
5
  import re
6
  from deep_translator import GoogleTranslator
7
  import warnings
 
8
 
9
  warnings.filterwarnings("ignore")
10
 
11
  # Load models
12
+ from xgboost import Booster
13
+ xgb = Booster()
14
+ xgb.load_model("xgb_model.json")
15
 
16
+ rf = joblib.load("rf_model.pkl") # Needs scikit-learn
17
+
18
+ # Load tile catalog and sizes
19
  with open("tile_catalog.json", "r", encoding="utf-8") as f:
20
  tile_catalog = json.load(f)
21
 
 
31
  return text
32
 
33
  def extract_tile_area(msg, unit):
34
+ msg = msg.lower().replace("ร—", "x").replace("into", "x").replace("*", "x")
35
+ msg = msg.replace("mm", "").replace("ft", "").replace("feet", "").strip()
36
+
37
+ if "x" in msg:
38
+ parts = re.split(r"x", msg)
39
  if len(parts) == 2:
40
+ try:
41
+ val1 = float(re.sub(r"[^\d.]", "", parts[0]))
42
+ val2 = float(re.sub(r"[^\d.]", "", parts[1]))
43
+ if unit == "mm":
44
+ sqft = (val1 * val2) / 92903.04
45
+ else:
46
+ sqft = val1 * val2
47
+ return round(sqft, 2)
48
+ except:
49
+ return None
50
+ else:
51
+ # Single value fallback for ft
52
+ try:
53
+ val = float(re.sub(r"[^\d.]", "", msg))
54
+ return val if unit == "ft" else None
55
+ except:
56
  return None
 
 
57
 
58
  def chat_fn(message, history, user_state={}):
59
+ message = message.strip().lower()
 
60
 
 
61
  if "lang" not in user_state:
62
  try:
63
  user_state["lang"] = GoogleTranslator(source="auto", target="en").detect(message)
 
66
  lang = user_state["lang"]
67
  def reply(text): return translate(text, lang)
68
 
69
+ # Greeting handler
70
+ if message in ["hi", "hello", "hey", "start", "help"]:
71
+ user_state.clear()
72
+ return reply("๐Ÿ‘‹ Hello! Are you planning for Floor or Wall tiles?"), None, user_state
73
 
74
+ if message in ["floor", "wall"]:
75
+ user_state.clear()
76
+ user_state["tile_type"] = message.capitalize()
77
+ user_state["step"] = "get_length"
78
+ return reply(f"Great! You chose {user_state['tile_type']} tiles.\nPlease enter the **length** of the space in feet:"), None, user_state
 
 
79
 
80
+ # Stepwise logic
81
+ if user_state.get("step") == "get_length":
82
  try:
83
+ user_state["length"] = float(re.sub(r"[^\d.]", "", message))
84
  user_state["step"] = "get_width"
85
+ return reply("Now enter the **width** of the space in feet:"), None, user_state
86
  except:
87
+ return reply("Please enter a valid number for length (e.g. 12.5):"), None, user_state
88
 
89
+ if user_state.get("step") == "get_width":
90
  try:
91
+ user_state["width"] = float(re.sub(r"[^\d.]", "", message))
92
  user_state["area"] = round(user_state["length"] * user_state["width"], 2)
93
  user_state["step"] = "get_unit"
94
+ 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
95
  except:
96
+ return reply("Please enter a valid number for width (e.g. 10):"), None, user_state
97
 
98
+ if user_state.get("step") == "get_unit":
99
+ if message not in ["mm", "ft"]:
100
+ return reply("Please type either **mm** or **ft** to choose your preferred unit."), None, user_state
101
+ user_state["unit"] = message
102
  user_state["step"] = "get_tile_size"
103
+ return reply(f"Enter the tile size in {message.upper()} (e.g. 600x600 or 2x2):"), None, user_state
 
104
 
105
+ if user_state.get("step") == "get_tile_size":
106
+ area_per_tile = extract_tile_area(message, user_state["unit"])
107
+ if not area_per_tile:
108
+ return reply("I couldn't understand that size. Try something like `600x600`, `2x2`, or just `4` if square in ft."), None, user_state
109
 
110
+ user_state["tile_area"] = area_per_tile
111
  user_state["step"] = "done"
112
 
113
  area_needed = user_state["area"]
114
  tile_type = user_state["tile_type"]
115
+ tile_needed = math.ceil((area_needed / area_per_tile) * 1.1)
116
 
117
+ # Suggest tiles
118
  best = []
119
  for tile in tile_catalog:
120
  if tile["type"].lower() == tile_type.lower():
121
+ per_box = tile["coverage"] / area_per_tile
122
  if per_box > 0:
123
  boxes = math.ceil(tile_needed / per_box)
124
  total = boxes * tile["price"]
 
134
  best.sort(key=lambda x: x["total"])
135
  top3 = best[:3]
136
 
137
+ summary = f"""๐Ÿงฑ Tile Type: {tile_type}
138
+ ๐Ÿ“ Area: {area_needed} sq.ft
139
+ ๐Ÿงฎ Tile Size Area: {round(area_per_tile, 2)} sq.ft
140
+ ๐Ÿ”ข Tiles Needed: {tile_needed} (10% extra included)
 
 
141
 
142
+ ๐ŸŽฏ Top Recommendations:
143
  """
144
  for i, t in enumerate(top3, 1):
145
+ summary += f"{i}. {t['name']} ({t['size']}) โ€“ โ‚น{t['price']} per box, ~{t['boxes']} boxes\n ๐Ÿ”— {t['url']}\n"
146
 
147
+ summary += "\nType 'Floor' or 'Wall' to start over."
148
+
149
+ user_state["summary"] = summary
150
  return reply(summary), None, user_state
151
 
152
+ return reply("๐Ÿ‘‹ Please type 'Floor' or 'Wall' to begin tile estimation."), None, user_state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
+ # UI setup
155
+ with gr.Blocks() as demo:
156
+ gr.ChatInterface(
157
  fn=chat_fn,
158
+ title="๐Ÿงฑ TileBot โ€“ Smart Tile Estimator",
159
+ description=(
160
+ "Plan your tile needs easily. Just type `hi` to begin or start with `Floor` or `Wall`.\n"
161
+ "Iโ€™ll ask for room size, tile size, and recommend the best fit tiles."
162
+ ),
163
+ theme=gr.themes.Soft(),
164
+ retry_btn=None,
165
+ undo_btn=None
166
  )
167
 
 
 
168
  demo.launch()