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 os 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 data with open("tile_catalog.json", "r", encoding="utf-8") as f: tile_catalog = json.load(f) with open("tile_sizes.json", "r", encoding="utf-8") as f: tile_sizes = 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", "").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", []) 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", "").lower() area = float(data.get("area", 0)) tile_size = data.get("tile_size", "") if tile_size not in tile_sizes: return jsonify({"error": "Invalid tile size"}), 400 info = tile_sizes[tile_size] per_tile_area = info["length"] * info["width"] tiles_needed = math.ceil((area / per_tile_area) * 1.1) boxes = math.ceil(tiles_needed / info.get("tiles_per_box", 10)) matches = [p for p in tile_catalog if p["type"].lower() == tile_type and p["size"] == tile_size] return jsonify({ "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": "Server error"}), 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)