| 1 | from sentence_transformers import SentenceTransformer
|
|---|
| 2 | import psycopg2
|
|---|
| 3 | import numpy as np
|
|---|
| 4 |
|
|---|
| 5 | model = SentenceTransformer('all-MiniLM-L6-v2')
|
|---|
| 6 |
|
|---|
| 7 | conn = psycopg2.connect(
|
|---|
| 8 | host="localhost",
|
|---|
| 9 | port="5432",
|
|---|
| 10 | database="your_db_name",
|
|---|
| 11 | user="your_db_user",
|
|---|
| 12 | password="your_password"
|
|---|
| 13 | )
|
|---|
| 14 |
|
|---|
| 15 | cur = conn.cursor()
|
|---|
| 16 |
|
|---|
| 17 | client_id = input("Enter client_id: ")
|
|---|
| 18 |
|
|---|
| 19 | # =========================
|
|---|
| 20 | # 1. GET CLIENT HISTORY
|
|---|
| 21 | # =========================
|
|---|
| 22 | cur.execute("""
|
|---|
| 23 | SELECT s.embedding
|
|---|
| 24 | FROM appointment a
|
|---|
| 25 | JOIN appointment_service aps
|
|---|
| 26 | ON a.appointment_id = aps.appointment_id
|
|---|
| 27 | JOIN service s
|
|---|
| 28 | ON aps.service_id = s.service_id
|
|---|
| 29 | WHERE a.client_id = %s
|
|---|
| 30 | """, (client_id,))
|
|---|
| 31 |
|
|---|
| 32 | history = cur.fetchall()
|
|---|
| 33 |
|
|---|
| 34 | if not history:
|
|---|
| 35 | print("No history found for this client. Using cold-start mode.")
|
|---|
| 36 |
|
|---|
| 37 | # fallback: generic query vector
|
|---|
| 38 | user_query = input("What are you looking for? ")
|
|---|
| 39 | client_vector = model.encode(user_query)
|
|---|
| 40 |
|
|---|
| 41 | else:
|
|---|
| 42 | # =========================
|
|---|
| 43 | # 2. BUILD USER PROFILE VECTOR
|
|---|
| 44 | # =========================
|
|---|
| 45 | embeddings = [np.array(row[0]) for row in history]
|
|---|
| 46 | client_vector = np.mean(embeddings, axis=0)
|
|---|
| 47 |
|
|---|
| 48 | # =========================
|
|---|
| 49 | # 3. FETCH ALL SERVICES
|
|---|
| 50 | # =========================
|
|---|
| 51 | cur.execute("""
|
|---|
| 52 | SELECT
|
|---|
| 53 | s.service_id,
|
|---|
| 54 | s.service_name,
|
|---|
| 55 | sc.category_name,
|
|---|
| 56 | s.price,
|
|---|
| 57 | s.embedding,
|
|---|
| 58 | s.avg_rating,
|
|---|
| 59 | s.popularity
|
|---|
| 60 | FROM service s
|
|---|
| 61 | JOIN service_category sc
|
|---|
| 62 | ON s.service_category_id = sc.service_category_id
|
|---|
| 63 | WHERE s.embedding IS NOT NULL
|
|---|
| 64 | """)
|
|---|
| 65 |
|
|---|
| 66 | rows = cur.fetchall()
|
|---|
| 67 |
|
|---|
| 68 | service_embeddings = np.array([r[4] for r in rows])
|
|---|
| 69 |
|
|---|
| 70 | # =========================
|
|---|
| 71 | # 4. VECTORIZED SIMILARITY
|
|---|
| 72 | # =========================
|
|---|
| 73 | norms = np.linalg.norm(service_embeddings, axis=1) * np.linalg.norm(client_vector)
|
|---|
| 74 |
|
|---|
| 75 | similarities = (service_embeddings @ client_vector) / norms
|
|---|
| 76 |
|
|---|
| 77 | # =========================
|
|---|
| 78 | # 5. SCORING
|
|---|
| 79 | # =========================
|
|---|
| 80 | results = []
|
|---|
| 81 |
|
|---|
| 82 | for i, row in enumerate(rows):
|
|---|
| 83 |
|
|---|
| 84 | service_id, name, category, price, _, rating, popularity = row
|
|---|
| 85 |
|
|---|
| 86 | rating_score = rating / 5
|
|---|
| 87 | popularity_score = min(popularity / 50, 1)
|
|---|
| 88 |
|
|---|
| 89 | final_score = (
|
|---|
| 90 | similarities[i] * 0.75 +
|
|---|
| 91 | rating_score * 0.15 +
|
|---|
| 92 | popularity_score * 0.10
|
|---|
| 93 | )
|
|---|
| 94 |
|
|---|
| 95 | results.append({
|
|---|
| 96 | "service": name,
|
|---|
| 97 | "category": category,
|
|---|
| 98 | "price": price,
|
|---|
| 99 | "score": final_score
|
|---|
| 100 | })
|
|---|
| 101 |
|
|---|
| 102 | # =========================
|
|---|
| 103 | # 6. SORT + FILTER
|
|---|
| 104 | # =========================
|
|---|
| 105 | results.sort(key=lambda x: x["score"], reverse=True)
|
|---|
| 106 |
|
|---|
| 107 | seen = set()
|
|---|
| 108 |
|
|---|
| 109 | print("\nPersonalized recommendations:\n")
|
|---|
| 110 |
|
|---|
| 111 | count = 0
|
|---|
| 112 |
|
|---|
| 113 | for r in results:
|
|---|
| 114 |
|
|---|
| 115 | if r["service"] in seen:
|
|---|
| 116 | continue
|
|---|
| 117 |
|
|---|
| 118 | seen.add(r["service"])
|
|---|
| 119 |
|
|---|
| 120 | print(
|
|---|
| 121 | f"{r['service']} | {r['category']} | {r['price']} | Score: {r['score']:.3f}"
|
|---|
| 122 | )
|
|---|
| 123 |
|
|---|
| 124 | count += 1
|
|---|
| 125 | if count == 5:
|
|---|
| 126 | break
|
|---|
| 127 |
|
|---|
| 128 | cur.close()
|
|---|
| 129 | conn.close() |
|---|