File size: 4,907 Bytes
5d5165f
 
 
 
a43c45a
 
 
e09d218
61ffb8e
0616b98
5d5165f
d12096d
1139b4c
61ffb8e
5d5165f
61ffb8e
 
0616b98
61ffb8e
0616b98
5d5165f
61ffb8e
e09d218
5d5165f
14765ac
5d5165f
0616b98
 
 
5d5165f
1139b4c
 
857baf5
 
e09d218
1139b4c
 
 
 
857baf5
e09d218
 
857baf5
1139b4c
 
 
 
857baf5
1139b4c
857baf5
1139b4c
 
 
857baf5
0616b98
1139b4c
0616b98
 
1139b4c
 
0616b98
 
1139b4c
0616b98
e09d218
857baf5
1139b4c
e09d218
1139b4c
e09d218
 
 
 
 
 
1139b4c
e09d218
 
 
 
1139b4c
e09d218
 
 
0616b98
e09d218
 
 
 
 
0616b98
 
e09d218
 
 
1139b4c
 
 
 
0616b98
857baf5
1139b4c
e09d218
0616b98
 
 
 
1139b4c
 
0616b98
 
 
 
5d5165f
a43c45a
0616b98
 
 
 
 
 
 
1139b4c
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
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

# Load tile catalog
with open("tile_catalog.json", "r", encoding="utf-8") as f:
    tile_catalog = json.load(f)

@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, 100])
        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

        # Extract tile length and width in feet using regex
        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  # in sq.ft
        tiles_needed = math.ceil((area / tile_area) * 1.1)  # +10% buffer
        boxes = math.ceil(tiles_needed / 10)  # assuming 10 tiles per box

        # Filter matching products by type and approximate size
        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)