Update app.py
Browse files
app.py
CHANGED
|
@@ -8,7 +8,8 @@ import os
|
|
| 8 |
import xgboost as xgb
|
| 9 |
import logging
|
| 10 |
|
| 11 |
-
|
|
|
|
| 12 |
CORS(app)
|
| 13 |
logging.basicConfig(level=logging.INFO)
|
| 14 |
|
|
@@ -29,61 +30,55 @@ with open("tile_catalog.json", "r", encoding="utf-8") as f:
|
|
| 29 |
with open("tile_sizes.json", "r", encoding="utf-8") as f:
|
| 30 |
tile_sizes = json.load(f)
|
| 31 |
|
| 32 |
-
# === Serve
|
| 33 |
@app.route('/')
|
| 34 |
def serve_index():
|
| 35 |
-
return
|
| 36 |
|
|
|
|
| 37 |
@app.route('/<path:path>')
|
| 38 |
-
def
|
| 39 |
return send_from_directory('.', path)
|
| 40 |
|
| 41 |
-
# === Calculate
|
| 42 |
@app.route('/calculate', methods=['POST'])
|
| 43 |
def calculate():
|
| 44 |
try:
|
| 45 |
data = request.get_json()
|
| 46 |
-
|
| 47 |
-
for field in required_fields:
|
| 48 |
if field not in data:
|
| 49 |
return jsonify({"error": f"Missing field: {field}"}), 400
|
| 50 |
|
| 51 |
tile_type = data['tile_type'].lower()
|
| 52 |
-
if tile_type not in ['floor', 'wall']:
|
| 53 |
-
return jsonify({"error": "Invalid tile type"}), 400
|
| 54 |
-
|
| 55 |
-
tile_size = data['tile_size']
|
| 56 |
-
if tile_size not in tile_sizes:
|
| 57 |
-
return jsonify({"error": "Invalid tile size"}), 400
|
| 58 |
-
|
| 59 |
-
# Validate and compute area
|
| 60 |
length = float(data['length'])
|
| 61 |
width = float(data['width'])
|
| 62 |
validate_positive_number(length, "length")
|
| 63 |
validate_positive_number(width, "width")
|
| 64 |
-
area = length * width
|
| 65 |
-
validate_positive_number(area, "area")
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
area_per_tile = tile_info['length'] * tile_info['width']
|
| 70 |
tiles_needed = math.ceil((area / area_per_tile) * 1.1)
|
| 71 |
tiles_per_box = tile_info.get('tiles_per_box', 10)
|
| 72 |
boxes_needed = math.ceil(tiles_needed / tiles_per_box)
|
| 73 |
|
| 74 |
-
# Find matching products
|
| 75 |
matching_products = [
|
| 76 |
{
|
| 77 |
**p,
|
| 78 |
"link": p.get("url", "#")
|
| 79 |
}
|
| 80 |
for p in tile_catalog
|
| 81 |
-
if p['type'].lower() == tile_type and p['size'] == tile_size
|
| 82 |
]
|
| 83 |
|
| 84 |
return jsonify({
|
| 85 |
"tile_type": tile_type,
|
| 86 |
-
"tile_size": tile_size,
|
| 87 |
"length": round(length, 2),
|
| 88 |
"width": round(width, 2),
|
| 89 |
"area": round(area, 2),
|
|
@@ -97,7 +92,7 @@ def calculate():
|
|
| 97 |
app.logger.error(f"Error in /calculate: {str(e)}")
|
| 98 |
return jsonify({"error": "Internal server error"}), 500
|
| 99 |
|
| 100 |
-
# ===
|
| 101 |
@app.route('/recommend', methods=['POST'])
|
| 102 |
def recommend():
|
| 103 |
try:
|
|
@@ -108,9 +103,6 @@ def recommend():
|
|
| 108 |
return jsonify({"error": f"Missing field: {field}"}), 400
|
| 109 |
|
| 110 |
tile_type = data['tile_type'].lower()
|
| 111 |
-
if tile_type not in ['floor', 'wall']:
|
| 112 |
-
return jsonify({"error": "Invalid tile type"}), 400
|
| 113 |
-
|
| 114 |
length = float(data['length'])
|
| 115 |
width = float(data['width'])
|
| 116 |
validate_positive_number(length, "length")
|
|
@@ -123,7 +115,6 @@ def recommend():
|
|
| 123 |
if not isinstance(data['price_range'], list) or len(data['price_range']) != 2:
|
| 124 |
return jsonify({"error": "Invalid price range"}), 400
|
| 125 |
|
| 126 |
-
# Prepare features
|
| 127 |
features = prepare_features({
|
| 128 |
**data,
|
| 129 |
"area": area
|
|
@@ -151,11 +142,12 @@ def recommend():
|
|
| 151 |
app.logger.error(f"Error in /recommend: {str(e)}")
|
| 152 |
return jsonify({"error": "Internal server error"}), 500
|
| 153 |
|
| 154 |
-
# ===
|
| 155 |
def prepare_features(data):
|
| 156 |
tile_type_num = 0 if data['tile_type'] == 'floor' else 1
|
| 157 |
price_per_sqft = data['price_range'][1] / data['coverage']
|
| 158 |
budget_efficiency = data['coverage'] / data['price_range'][1]
|
|
|
|
| 159 |
return np.array([[tile_type_num, data['area'], data['coverage'],
|
| 160 |
data['price_range'][0], data['price_range'][1],
|
| 161 |
price_per_sqft, budget_efficiency]])
|
|
@@ -196,6 +188,6 @@ def validate_positive_number(value, field):
|
|
| 196 |
if not isinstance(value, (int, float)) or value <= 0:
|
| 197 |
raise ValueError(f"{field} must be a positive number")
|
| 198 |
|
| 199 |
-
# ===
|
| 200 |
if __name__ == '__main__':
|
| 201 |
app.run(host='0.0.0.0', port=7860, debug=False)
|
|
|
|
| 8 |
import xgboost as xgb
|
| 9 |
import logging
|
| 10 |
|
| 11 |
+
# === Flask Setup ===
|
| 12 |
+
app = Flask(__name__, static_folder='.', static_url_path='')
|
| 13 |
CORS(app)
|
| 14 |
logging.basicConfig(level=logging.INFO)
|
| 15 |
|
|
|
|
| 30 |
with open("tile_sizes.json", "r", encoding="utf-8") as f:
|
| 31 |
tile_sizes = json.load(f)
|
| 32 |
|
| 33 |
+
# === Serve index.html ===
|
| 34 |
@app.route('/')
|
| 35 |
def serve_index():
|
| 36 |
+
return app.send_static_file('index.html')
|
| 37 |
|
| 38 |
+
# === Serve static assets (if any JS, CSS, images) ===
|
| 39 |
@app.route('/<path:path>')
|
| 40 |
+
def serve_static_files(path):
|
| 41 |
return send_from_directory('.', path)
|
| 42 |
|
| 43 |
+
# === Calculate tiles endpoint ===
|
| 44 |
@app.route('/calculate', methods=['POST'])
|
| 45 |
def calculate():
|
| 46 |
try:
|
| 47 |
data = request.get_json()
|
| 48 |
+
for field in ['tile_type', 'length', 'width', 'tile_size']:
|
|
|
|
| 49 |
if field not in data:
|
| 50 |
return jsonify({"error": f"Missing field: {field}"}), 400
|
| 51 |
|
| 52 |
tile_type = data['tile_type'].lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
length = float(data['length'])
|
| 54 |
width = float(data['width'])
|
| 55 |
validate_positive_number(length, "length")
|
| 56 |
validate_positive_number(width, "width")
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
if tile_type not in ['floor', 'wall']:
|
| 59 |
+
return jsonify({"error": "Invalid tile type"}), 400
|
| 60 |
+
if data['tile_size'] not in tile_sizes:
|
| 61 |
+
return jsonify({"error": "Invalid tile size"}), 400
|
| 62 |
+
|
| 63 |
+
area = length * width
|
| 64 |
+
tile_info = tile_sizes[data['tile_size']]
|
| 65 |
area_per_tile = tile_info['length'] * tile_info['width']
|
| 66 |
tiles_needed = math.ceil((area / area_per_tile) * 1.1)
|
| 67 |
tiles_per_box = tile_info.get('tiles_per_box', 10)
|
| 68 |
boxes_needed = math.ceil(tiles_needed / tiles_per_box)
|
| 69 |
|
|
|
|
| 70 |
matching_products = [
|
| 71 |
{
|
| 72 |
**p,
|
| 73 |
"link": p.get("url", "#")
|
| 74 |
}
|
| 75 |
for p in tile_catalog
|
| 76 |
+
if p['type'].lower() == tile_type and p['size'] == data['tile_size']
|
| 77 |
]
|
| 78 |
|
| 79 |
return jsonify({
|
| 80 |
"tile_type": tile_type,
|
| 81 |
+
"tile_size": data['tile_size'],
|
| 82 |
"length": round(length, 2),
|
| 83 |
"width": round(width, 2),
|
| 84 |
"area": round(area, 2),
|
|
|
|
| 92 |
app.logger.error(f"Error in /calculate: {str(e)}")
|
| 93 |
return jsonify({"error": "Internal server error"}), 500
|
| 94 |
|
| 95 |
+
# === Recommend endpoint ===
|
| 96 |
@app.route('/recommend', methods=['POST'])
|
| 97 |
def recommend():
|
| 98 |
try:
|
|
|
|
| 103 |
return jsonify({"error": f"Missing field: {field}"}), 400
|
| 104 |
|
| 105 |
tile_type = data['tile_type'].lower()
|
|
|
|
|
|
|
|
|
|
| 106 |
length = float(data['length'])
|
| 107 |
width = float(data['width'])
|
| 108 |
validate_positive_number(length, "length")
|
|
|
|
| 115 |
if not isinstance(data['price_range'], list) or len(data['price_range']) != 2:
|
| 116 |
return jsonify({"error": "Invalid price range"}), 400
|
| 117 |
|
|
|
|
| 118 |
features = prepare_features({
|
| 119 |
**data,
|
| 120 |
"area": area
|
|
|
|
| 142 |
app.logger.error(f"Error in /recommend: {str(e)}")
|
| 143 |
return jsonify({"error": "Internal server error"}), 500
|
| 144 |
|
| 145 |
+
# === Helper Functions ===
|
| 146 |
def prepare_features(data):
|
| 147 |
tile_type_num = 0 if data['tile_type'] == 'floor' else 1
|
| 148 |
price_per_sqft = data['price_range'][1] / data['coverage']
|
| 149 |
budget_efficiency = data['coverage'] / data['price_range'][1]
|
| 150 |
+
|
| 151 |
return np.array([[tile_type_num, data['area'], data['coverage'],
|
| 152 |
data['price_range'][0], data['price_range'][1],
|
| 153 |
price_per_sqft, budget_efficiency]])
|
|
|
|
| 188 |
if not isinstance(value, (int, float)) or value <= 0:
|
| 189 |
raise ValueError(f"{field} must be a positive number")
|
| 190 |
|
| 191 |
+
# === Start the server ===
|
| 192 |
if __name__ == '__main__':
|
| 193 |
app.run(host='0.0.0.0', port=7860, debug=False)
|