Update app.py
Browse files
app.py
CHANGED
|
@@ -2,13 +2,15 @@ from flask import Flask, request, jsonify, send_from_directory
|
|
| 2 |
from flask_cors import CORS
|
| 3 |
import joblib
|
| 4 |
import numpy as np
|
| 5 |
-
import
|
| 6 |
import math
|
|
|
|
|
|
|
| 7 |
|
| 8 |
app = Flask(__name__, static_folder='.', static_url_path='/')
|
| 9 |
CORS(app)
|
| 10 |
|
| 11 |
-
# Load models
|
| 12 |
try:
|
| 13 |
rf = joblib.load("rf_model.pkl")
|
| 14 |
xgb_model = xgb.Booster()
|
|
@@ -18,7 +20,7 @@ except Exception as e:
|
|
| 18 |
print(f"β Error loading models: {e}")
|
| 19 |
raise e
|
| 20 |
|
| 21 |
-
#
|
| 22 |
tile_catalog = [
|
| 23 |
{"name": "Travertino Light Beige", "type": "Floor", "price": 780, "coverage": 15.5, "size": "1200x1200 MM", "url": "https://arqonz.ae/products/6775"},
|
| 24 |
{"name": "Marquina Glossy Black", "type": "Wall", "price": 620, "coverage": 12.0, "size": "600x600 MM", "url": "https://arqonz.ae/products/6776"},
|
|
@@ -47,26 +49,25 @@ def recommend():
|
|
| 47 |
tile_type = data.get("tile_type", "").strip().lower()
|
| 48 |
coverage = float(data.get("coverage", 1))
|
| 49 |
area = float(data.get("area", 1))
|
| 50 |
-
price_range = data.get("price_range", [
|
| 51 |
preferred_sizes = data.get("preferred_sizes", [])
|
| 52 |
|
| 53 |
if area <= 0 or coverage <= 0:
|
| 54 |
-
return jsonify({"error": "
|
| 55 |
|
| 56 |
features = prepare_features(tile_type, coverage, area, price_range)
|
| 57 |
xgb_pred = xgb_model.predict(xgb.DMatrix(features))[0]
|
| 58 |
rf_pred = rf.predict_proba(features)[0][1]
|
| 59 |
score = (xgb_pred + rf_pred) / 2
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
return jsonify({
|
| 64 |
"recommendation_score": round(float(score), 3),
|
| 65 |
-
"recommended_products":
|
| 66 |
-
"total_matches": len(
|
| 67 |
})
|
| 68 |
except Exception as e:
|
| 69 |
-
print("β /recommend
|
| 70 |
return jsonify({"error": "Server error"}), 500
|
| 71 |
|
| 72 |
def prepare_features(tile_type, coverage, area, price_range):
|
|
@@ -85,22 +86,23 @@ def filter_products(tile_type, price_range, preferred_sizes):
|
|
| 85 |
if not (min_price <= p["price"] <= max_price):
|
| 86 |
continue
|
| 87 |
|
| 88 |
-
|
| 89 |
-
for
|
| 90 |
-
if
|
| 91 |
-
|
| 92 |
break
|
| 93 |
-
|
|
|
|
| 94 |
continue
|
| 95 |
|
| 96 |
price_score = 1 - (p["price"] - min_price) / (max_price - min_price + 1e-6)
|
| 97 |
filtered.append({**p, "recommendation_score": round(price_score, 2)})
|
| 98 |
return sorted(filtered, key=lambda x: x["recommendation_score"], reverse=True)
|
| 99 |
|
| 100 |
-
def
|
| 101 |
try:
|
| 102 |
-
w1, h1 = map(int,
|
| 103 |
-
w2, h2 = map(int,
|
| 104 |
return abs(w1 - w2) <= tolerance and abs(h1 - h2) <= tolerance
|
| 105 |
except:
|
| 106 |
return False
|
|
|
|
| 2 |
from flask_cors import CORS
|
| 3 |
import joblib
|
| 4 |
import numpy as np
|
| 5 |
+
import json
|
| 6 |
import math
|
| 7 |
+
import xgboost as xgb
|
| 8 |
+
import re
|
| 9 |
|
| 10 |
app = Flask(__name__, static_folder='.', static_url_path='/')
|
| 11 |
CORS(app)
|
| 12 |
|
| 13 |
+
# Load ML models
|
| 14 |
try:
|
| 15 |
rf = joblib.load("rf_model.pkl")
|
| 16 |
xgb_model = xgb.Booster()
|
|
|
|
| 20 |
print(f"β Error loading models: {e}")
|
| 21 |
raise e
|
| 22 |
|
| 23 |
+
# Product catalog
|
| 24 |
tile_catalog = [
|
| 25 |
{"name": "Travertino Light Beige", "type": "Floor", "price": 780, "coverage": 15.5, "size": "1200x1200 MM", "url": "https://arqonz.ae/products/6775"},
|
| 26 |
{"name": "Marquina Glossy Black", "type": "Wall", "price": 620, "coverage": 12.0, "size": "600x600 MM", "url": "https://arqonz.ae/products/6776"},
|
|
|
|
| 49 |
tile_type = data.get("tile_type", "").strip().lower()
|
| 50 |
coverage = float(data.get("coverage", 1))
|
| 51 |
area = float(data.get("area", 1))
|
| 52 |
+
price_range = data.get("price_range", [1, 10000])
|
| 53 |
preferred_sizes = data.get("preferred_sizes", [])
|
| 54 |
|
| 55 |
if area <= 0 or coverage <= 0:
|
| 56 |
+
return jsonify({"error": "Please enter valid area and coverage values."}), 400
|
| 57 |
|
| 58 |
features = prepare_features(tile_type, coverage, area, price_range)
|
| 59 |
xgb_pred = xgb_model.predict(xgb.DMatrix(features))[0]
|
| 60 |
rf_pred = rf.predict_proba(features)[0][1]
|
| 61 |
score = (xgb_pred + rf_pred) / 2
|
| 62 |
|
| 63 |
+
products = filter_products(tile_type, price_range, preferred_sizes)
|
|
|
|
| 64 |
return jsonify({
|
| 65 |
"recommendation_score": round(float(score), 3),
|
| 66 |
+
"recommended_products": products[:4],
|
| 67 |
+
"total_matches": len(products),
|
| 68 |
})
|
| 69 |
except Exception as e:
|
| 70 |
+
print("β Error in /recommend:", str(e))
|
| 71 |
return jsonify({"error": "Server error"}), 500
|
| 72 |
|
| 73 |
def prepare_features(tile_type, coverage, area, price_range):
|
|
|
|
| 86 |
if not (min_price <= p["price"] <= max_price):
|
| 87 |
continue
|
| 88 |
|
| 89 |
+
match_found = False
|
| 90 |
+
for pref in preferred_sizes:
|
| 91 |
+
if similar_size_mm(p["size"], pref):
|
| 92 |
+
match_found = True
|
| 93 |
break
|
| 94 |
+
|
| 95 |
+
if not match_found:
|
| 96 |
continue
|
| 97 |
|
| 98 |
price_score = 1 - (p["price"] - min_price) / (max_price - min_price + 1e-6)
|
| 99 |
filtered.append({**p, "recommendation_score": round(price_score, 2)})
|
| 100 |
return sorted(filtered, key=lambda x: x["recommendation_score"], reverse=True)
|
| 101 |
|
| 102 |
+
def similar_size_mm(a, b, tolerance=10):
|
| 103 |
try:
|
| 104 |
+
w1, h1 = map(int, a.lower().replace("mm", "").split("x"))
|
| 105 |
+
w2, h2 = map(int, b.lower().replace("mm", "").split("x"))
|
| 106 |
return abs(w1 - w2) <= tolerance and abs(h1 - h2) <= tolerance
|
| 107 |
except:
|
| 108 |
return False
|