File size: 6,867 Bytes
5d5165f a43c45a e09d218 61ffb8e 0616b98 5d5165f d12096d 1139b4c 61ffb8e 5d5165f 61ffb8e 0616b98 61ffb8e 0616b98 5d5165f 61ffb8e 2c2d005 fc94d32 0616b98 5d5165f 1139b4c 857baf5 e09d218 1139b4c 2c2d005 857baf5 e09d218 857baf5 1139b4c 857baf5 2c2d005 857baf5 1139b4c 2c2d005 857baf5 0616b98 1139b4c 0616b98 1139b4c 0616b98 1139b4c 0616b98 e09d218 857baf5 1139b4c e09d218 1139b4c e09d218 1139b4c e09d218 1139b4c fc94d32 0616b98 e09d218 0616b98 e09d218 1139b4c 0616b98 857baf5 1139b4c e09d218 0616b98 1139b4c 0616b98 5d5165f a43c45a 0616b98 2c2d005 0616b98 2c2d005 0616b98 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
import joblib
import numpy as np
import json
import math
import xgboost as xgb
import re
app = Flask(__name__, static_folder='.', static_url_path='/')
CORS(app)
# Load ML models
try:
rf = joblib.load("rf_model.pkl")
xgb_model = xgb.Booster()
xgb_model.load_model("xgb_model.json")
print("β
Models loaded successfully.")
except Exception as e:
print(f"β Error loading models: {e}")
raise e
# Tile product catalog (hardcoded, replacing tile_catalog.json)
tile_catalog = [
{"name": "Travertino Light Beige", "type": "Floor", "price": 780, "coverage": 15.5, "size": "1200x1200 MM", "url": "https://arqonz.ae/products/6775"},
{"name": "Marquina Glossy Black", "type": "Wall", "price": 620, "coverage": 12.0, "size": "600x600 MM", "url": "https://arqonz.ae/products/6776"},
{"name": "Carrara White Polished", "type": "Floor", "price": 1100, "coverage": 23.23, "size": "1800x1200 MM", "url": "https://arqonz.ae/products/103"},
{"name": "Noir Marble Effect", "type": "Wall", "price": 550, "coverage": 6.89, "size": "800x800 MM", "url": "https://arqonz.ae/products/104"},
{"name": "Sandstone Beige Matte", "type": "Floor", "price": 670, "coverage": 13.78, "size": "1600x800 MM", "url": "https://arqonz.ae/products/6644"},
{"name": "Onyx Mist Polished", "type": "Wall", "price": 890, "coverage": 15.5, "size": "1200x1200 MM", "url": "https://arqonz.ae/products/6880"},
{"name": "Terrazzo Pearl", "type": "Floor", "price": 720, "coverage": 7.75, "size": "1200x600 MM", "url": "https://arqonz.ae/products/6878"},
{"name": "Statuario Silver", "type": "Wall", "price": 660, "coverage": 3.87, "size": "600x600 MM", "url": "https://arqonz.ae/products/6883"},
{"name": "Grigio Urban", "type": "Floor", "price": 500, "coverage": 2.52, "size": "1200x195 MM", "url": "https://arqonz.ae/products/6653"},
{"name": "Calacatta Nero", "type": "Wall", "price": 740, "coverage": 10.33, "size": "800x1200 MM", "url": "https://arqonz.ae/products/6885"},
{"name": "Ivory Rock Slate", "type": "Floor", "price": 690, "coverage": 13.0, "size": "1200x600 MM", "url": "https://arqonz.ae/products/6777"},
{"name": "Classic White Glossy", "type": "Wall", "price": 580, "coverage": 12.0, "size": "600x600 MM", "url": "https://arqonz.ae/products/89"},
{"name": "Dark Stone Matte", "type": "Floor", "price": 830, "coverage": 15.0, "size": "1200x600 MM", "url": "https://arqonz.ae/products/107"},
{"name": "Polished Statuario", "type": "Wall", "price": 810, "coverage": 10.5, "size": "600x1200 MM", "url": "https://arqonz.ae/products/116"}
# NOTE: Truncated for brevity β continue adding your full product list here
]
@app.route("/")
def index():
return send_from_directory(".", "index.html")
@app.route("/recommend", methods=["POST"])
def recommend():
try:
data = request.get_json()
tile_type = data.get("tile_type", "").strip().lower()
coverage = float(data.get("coverage", 1))
area = float(data.get("area", 1))
price_range = data.get("price_range", [1, 10000])
preferred_sizes = data.get("preferred_sizes", [])
if area <= 0 or coverage <= 0:
return jsonify({"error": "Please enter valid area and coverage values."}), 400
features = prepare_features(tile_type, coverage, area, price_range)
xgb_pred = xgb_model.predict(xgb.DMatrix(features))[0]
rf_pred = rf.predict_proba(features)[0][1]
score = (xgb_pred + rf_pred) / 2
products = filter_products(tile_type, price_range, preferred_sizes)
return jsonify({
"recommendation_score": round(float(score), 3),
"recommended_products": products[:4],
"total_matches": len(products),
})
except Exception as e:
print("β Error in /recommend:", str(e))
return jsonify({"error": "Server error"}), 500
@app.route("/calculate", methods=["POST"])
def calculate():
try:
data = request.get_json()
tile_type = data.get("tile_type", "").strip().lower()
area = float(data.get("area", 0))
tile_size_raw = data.get("tile_size", "").strip()
if not tile_type:
return jsonify({"error": "Please select a tile type (e.g., floor, wall)."}), 400
if area <= 0:
return jsonify({"error": "Area must be greater than 0."}), 400
match = re.match(r"(\d+(\.\d+)?)\s*(ft|feet)?\s*[xXΓ*]\s*(\d+(\.\d+)?)\s*(ft|feet)?", tile_size_raw)
if not match:
return jsonify({"error": f"Invalid tile size format: '{tile_size_raw}'. Please enter like '2 x 2 ft'."}), 400
length_ft = float(match.group(1))
width_ft = float(match.group(4))
if length_ft <= 0 or width_ft <= 0:
return jsonify({"error": "Tile dimensions must be greater than 0."}), 400
tile_area = length_ft * width_ft
tiles_needed = math.ceil((area / tile_area) * 1.1)
boxes = math.ceil(tiles_needed / 10)
matches = [
p for p in tile_catalog
if p["type"].lower() == tile_type
]
return jsonify({
"tile_type": tile_type,
"area_sqft": area,
"tile_size": f"{length_ft:.2f} ft x {width_ft:.2f} ft",
"tiles_needed": tiles_needed,
"boxes_needed": boxes,
"matching_products": matches[:3],
"total_matches": len(matches)
})
except Exception as e:
print("β Error in /calculate:", str(e))
return jsonify({"error": "An error occurred. Please check your input values."}), 500
def prepare_features(tile_type, coverage, area, price_range):
tile_type_num = 0 if tile_type == "floor" else 1
min_price, max_price = price_range
price_per_sqft = max_price / coverage
efficiency = coverage / max_price
return np.array([[tile_type_num, area, coverage, min_price, max_price, price_per_sqft, efficiency]])
def filter_products(tile_type, price_range, preferred_sizes):
min_price, max_price = price_range
filtered = []
for product in tile_catalog:
if product["type"].lower() != tile_type:
continue
if not (min_price <= product["price"] <= max_price):
continue
if preferred_sizes and product["size"] not in preferred_sizes:
continue
price_score = 1 - (product["price"] - min_price) / (max_price - min_price + 1e-6)
size_score = 1 if product["size"] in preferred_sizes else 0.5
score = round((price_score + size_score) / 2, 2)
filtered.append({**product, "recommendation_score": score})
return sorted(filtered, key=lambda x: x["recommendation_score"], reverse=True)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)
|