Coots commited on
Commit
fc55797
·
verified ·
1 Parent(s): 10833a5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +156 -133
app.py CHANGED
@@ -1,12 +1,10 @@
1
- import gradio as gr
2
  import joblib
 
3
  import json
4
  import math
5
- import re
6
- from deep_translator import GoogleTranslator
7
- import warnings
8
 
9
- warnings.filterwarnings("ignore")
10
 
11
  # Load models
12
  xgb = joblib.load("xgb_model.pkl")
@@ -19,133 +17,158 @@ with open("tile_catalog.json", "r", encoding="utf-8") as f:
19
  with open("tile_sizes.json", "r", encoding="utf-8") as f:
20
  tile_sizes = json.load(f)
21
 
22
- def translate(text, lang="en"):
 
 
 
 
 
 
 
 
 
 
 
 
23
  try:
24
- return GoogleTranslator(source="auto", target=lang).translate(text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  except Exception as e:
26
- print(f"Translation error: {e}")
27
- return text
28
-
29
- def extract_tile_area(msg):
30
- msg = msg.lower().replace("×", "x").replace("into", "x").replace("*", "x")
31
- msg = msg.replace("ft", "").replace("feet", "").replace("mm", "").strip()
32
-
33
- if "x" in msg:
34
- parts = msg.split("x")
35
- if len(parts) == 2:
36
- try:
37
- val1 = float(re.sub(r"[^\d.]", "", parts[0]))
38
- val2 = float(re.sub(r"[^\d.]", "", parts[1]))
39
- if val1 > 20: # Assuming mm
40
- sqft = (val1 * val2) / 92903.04 # Convert mm² to ft²
41
- else:
42
- sqft = val1 * val2 # ft²
43
- return round(sqft, 2)
44
- except ValueError:
45
- return None
46
- elif re.match(r'^\d+(\.\d+)?$', msg):
47
- try:
48
- val = float(msg)
49
- if val > 20: # Assuming mm
50
- return round((val * val) / 92903.04, 2)
51
- else:
52
- return round(val * val, 2)
53
- except ValueError:
54
- return None
55
- return None
56
-
57
- def chat_fn(message, history, user_state={}):
58
- msg = message.strip().lower()
59
-
60
- # Reset flow on keywords
61
- if msg in ["restart", "reset", "start", "floor", "wall"]:
62
- user_state.clear()
63
-
64
- if "lang" not in user_state:
65
- try:
66
- user_state["lang"] = GoogleTranslator(source="auto", target="en").detect(message)
67
- except Exception as e:
68
- print(f"Language detection error: {e}")
69
- user_state["lang"] = "en"
70
- lang = user_state["lang"]
71
- def reply(text): return translate(text, lang)
72
-
73
- # Step 1: Detect tile type
74
- if "step" not in user_state:
75
- if msg in ["floor", "wall"]:
76
- user_state["tile_type"] = message.capitalize()
77
- user_state["step"] = "get_area"
78
- return reply(f" You selected {user_state['tile_type']} tiles. What's the total area in sq.ft?"), None, user_state
79
- else:
80
- return reply("👋 Welcome! Are you planning for Floor or Wall tiles? Type 'Floor' or 'Wall' to begin."), None, user_state
81
-
82
- # Step 2: Get area
83
- if user_state["step"] == "get_area":
84
- try:
85
- user_state["area"] = float(message)
86
- user_state["step"] = "get_tile_size"
87
- return reply("📏 Now enter the tile size (e.g. 2x2, 600x600 mm, 200 * 200):"), None, user_state
88
- except ValueError:
89
- return reply("❗ Please enter a valid number for the area (e.g. 120)."), None, user_state
90
-
91
- # Step 3: Get tile size
92
- if user_state["step"] == "get_tile_size":
93
- area = extract_tile_area(message)
94
- if area is None:
95
- return reply("❗ I couldn't understand that tile size. Try: 2 x 2, 600x600 mm, 200*200."), None, user_state
96
-
97
- user_state["tile_area"] = area
98
- user_state["step"] = "done"
99
-
100
- total_area = user_state["area"]
101
- tile_type = user_state["tile_type"]
102
- tile_needed = math.ceil((total_area / area) * 1.1)
103
-
104
- # Recommendations
105
- recommendations = []
106
- for tile in tile_catalog:
107
- if tile["type"].lower() == tile_type.lower():
108
- per_box = tile["coverage"] / area
109
- if per_box > 0:
110
- boxes = math.ceil(tile_needed / per_box)
111
- total_price = boxes * tile["price"]
112
- recommendations.append({
113
- "name": tile["name"],
114
- "size": tile["size"],
115
- "price": tile["price"],
116
- "boxes": boxes,
117
- "total": total_price,
118
- "url": tile["url"]
119
- })
120
-
121
- recommendations.sort(key=lambda x: x["total"])
122
- top3 = recommendations[:3]
123
-
124
- result = f"""
125
- 🧱 Tile Type: {tile_type}
126
- 📐 Area to Cover: {total_area} sq.ft
127
- 🧮 Tile Size Area: {round(area, 2)} sq.ft
128
- 🔢 Tiles Needed (with buffer): {tile_needed}
129
-
130
- 🎯 Top Product Suggestions:
131
- """
132
- for i, t in enumerate(top3, 1):
133
- result += f"{i}. {t['name']} ({t['size']}) - ₹{t['price']}/box\n ~{t['boxes']} boxes → ₹{int(t['total'])}\n 🔗 {t['url']}\n\n"
134
-
135
- result += "You can type 'Floor' or 'Wall' to estimate again."
136
- return reply(result), None, {}
137
-
138
- return reply("Say 'Floor' or 'Wall' to begin."), None, {}
139
-
140
- # Gradio interface
141
- with gr.Blocks() as demo:
142
- gr.Markdown("# 🧱 Tilo – Tile Estimator")
143
- gr.Markdown("Type 'Floor' or 'Wall' to start. Then give your room size and tile size (e.g. 2x2 ft, 600x600 mm).")
144
- gr.ChatInterface(
145
- fn=chat_fn,
146
- title="Tile Estimator Chat",
147
- description="Get estimates for your tiling needs.",
148
- type="messages"
149
- )
150
-
151
- demo.launch()
 
1
+ from flask import Flask, request, jsonify
2
  import joblib
3
+ import numpy as np
4
  import json
5
  import math
 
 
 
6
 
7
+ app = Flask(_name_)
8
 
9
  # Load models
10
  xgb = joblib.load("xgb_model.pkl")
 
17
  with open("tile_sizes.json", "r", encoding="utf-8") as f:
18
  tile_sizes = json.load(f)
19
 
20
+ @app.route('/recommend', methods=['POST'])
21
+ def recommend():
22
+ """
23
+ Endpoint for product recommendations
24
+ Expected JSON payload:
25
+ {
26
+ "tile_type": "floor"|"wall",
27
+ "coverage": float,
28
+ "area": float,
29
+ "price_range": [min, max],
30
+ "preferred_sizes": [size1, size2] (optional)
31
+ }
32
+ """
33
  try:
34
+ data = request.get_json()
35
+
36
+ # Validate input
37
+ required_fields = ['tile_type', 'coverage', 'area', 'price_range']
38
+ if not all(field in data for field in required_fields):
39
+ return jsonify({"error": "Missing required fields"}), 400
40
+
41
+ tile_type = data['tile_type'].lower()
42
+ if tile_type not in ['floor', 'wall']:
43
+ return jsonify({"error": "Invalid tile type. Use 'floor' or 'wall'"}), 400
44
+
45
+ # Feature engineering for ML prediction
46
+ features = prepare_features(data)
47
+
48
+ # Get predictions from both models
49
+ xgb_pred = xgb.predict_proba(features)[0][1]
50
+ rf_pred = rf.predict_proba(features)[0][1]
51
+ combined_score = (xgb_pred + rf_pred) / 2
52
+
53
+ # Filter products based on criteria
54
+ recommended_products = filter_products(
55
+ tile_type=tile_type,
56
+ min_price=data['price_range'][0],
57
+ max_price=data['price_range'][1],
58
+ preferred_sizes=data.get('preferred_sizes', []),
59
+ min_score=0.5 # Threshold for recommendation
60
+ )
61
+
62
+ # Prepare response
63
+ response = {
64
+ "recommendation_score": round(float(combined_score), 3),
65
+ "recommended_products": recommended_products[:5], # Return top 5
66
+ "calculation": calculate_requirements(data['area'], data['coverage'])
67
+ }
68
+
69
+ return jsonify(response)
70
+
71
  except Exception as e:
72
+ return jsonify({"error": str(e)}), 500
73
+
74
+ @app.route('/calculate', methods=['POST'])
75
+ def calculate():
76
+ """
77
+ Endpoint for tile calculation
78
+ Expected JSON payload:
79
+ {
80
+ "tile_type": "floor"|"wall",
81
+ "area": float,
82
+ "tile_size": "12x12"|etc (from tile_sizes.json)
83
+ }
84
+ """
85
+ try:
86
+ data = request.get_json()
87
+
88
+ # Validate input
89
+ if 'tile_type' not in data or 'area' not in data or 'tile_size' not in data:
90
+ return jsonify({"error": "Missing required fields"}), 400
91
+
92
+ tile_type = data['tile_type'].lower()
93
+ if tile_type not in ['floor', 'wall']:
94
+ return jsonify({"error": "Invalid tile type"}), 400
95
+
96
+ if data['tile_size'] not in tile_sizes:
97
+ return jsonify({"error": "Invalid tile size"}), 400
98
+
99
+ # Perform calculation
100
+ tile_info = tile_sizes[data['tile_size']]
101
+ area_per_tile = tile_info['length'] * tile_info['width']
102
+ num_tiles = math.ceil((data['area'] / area_per_tile) * 1.1) # 10% buffer
103
+ num_boxes = math.ceil(num_tiles / tile_info.get('tiles_per_box', 10))
104
+
105
+ # Get matching products
106
+ matching_products = [
107
+ p for p in tile_catalog
108
+ if p['type'].lower() == tile_type and p['size'] == data['tile_size']
109
+ ]
110
+
111
+ return jsonify({
112
+ "tile_type": tile_type,
113
+ "area": data['area'],
114
+ "tile_size": data['tile_size'],
115
+ "tiles_needed": num_tiles,
116
+ "boxes_needed": num_boxes,
117
+ "matching_products": matching_products[:3] # Return top 3 matches
118
+ })
119
+
120
+ except Exception as e:
121
+ return jsonify({"error": str(e)}), 500
122
+
123
+ def prepare_features(data):
124
+ """Prepare feature vector for ML model prediction"""
125
+ tile_type_num = 0 if data['tile_type'] == 'floor' else 1
126
+ price_per_sqft = data['price_range'][1] / data['coverage'] # Using max price
127
+ budget_efficiency = data['coverage'] / data['price_range'][1]
128
+
129
+ return np.array([[
130
+ tile_type_num,
131
+ data['area'],
132
+ data['coverage'],
133
+ data['price_range'][0], # min price
134
+ data['price_range'][1], # max price
135
+ price_per_sqft,
136
+ budget_efficiency
137
+ ]])
138
+
139
+ def filter_products(tile_type, min_price, max_price, preferred_sizes, min_score=0.5):
140
+ """Filter products based on criteria"""
141
+ filtered = []
142
+
143
+ for product in tile_catalog:
144
+ if (product['type'].lower() == tile_type and
145
+ min_price <= product['price'] <= max_price and
146
+ (not preferred_sizes or product['size'] in preferred_sizes)):
147
+
148
+ # Calculate a simple score (could be enhanced)
149
+ price_score = 1 - ((product['price'] - min_price) / (max_price - min_price))
150
+ size_score = 1 if not preferred_sizes or product['size'] in preferred_sizes else 0.5
151
+ product_score = (price_score + size_score) / 2
152
+
153
+ if product_score >= min_score:
154
+ filtered.append({
155
+ **product,
156
+ "recommendation_score": round(product_score, 2)
157
+ })
158
+
159
+ # Sort by recommendation score
160
+ return sorted(filtered, key=lambda x: x['recommendation_score'], reverse=True)
161
+
162
+ def calculate_requirements(area, coverage):
163
+ """Calculate basic requirements"""
164
+ return {
165
+ "minimum_tiles": math.ceil(area / coverage),
166
+ "suggested_tiles": math.ceil((area / coverage) * 1.1), # 10% buffer
167
+ "estimated_cost_range": [
168
+ round(area * 3, 2), # $3/sqft (example)
169
+ round(area * 10, 2) # $10/sqft (example)
170
+ ]
171
+ }
172
+
173
+ if _name_ == '_main_':
174
+ app.run(host='0.0.0.0', port=5000, debug=True)