Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -170,11 +170,8 @@ def build_categorical_features(profiles):
|
|
| 170 |
|
| 171 |
for col in CATEGORICAL_COLS:
|
| 172 |
try:
|
| 173 |
-
# Get all values and sort for deterministic ordering
|
| 174 |
unique_vals = sorted(set(p[col] for p in profiles if p[col]))
|
| 175 |
-
# Create value-to-index mapping (0-based, 0 reserved for unknown)
|
| 176 |
val_to_idx = {val: idx+1 for idx, val in enumerate(unique_vals)}
|
| 177 |
-
# Encode each profile's value as integer (0 for unknown/missing)
|
| 178 |
encoded = np.array([val_to_idx.get(p[col], 0) for p in profiles])
|
| 179 |
cat_features.append(encoded.reshape(-1, 1))
|
| 180 |
except Exception as e:
|
|
@@ -196,7 +193,6 @@ def build_text_features(profiles, vectorizers=None):
|
|
| 196 |
try:
|
| 197 |
corpus = [p[col] for p in profiles]
|
| 198 |
|
| 199 |
-
# Always create fresh vectorizer to match current corpus
|
| 200 |
tfidf = TfidfVectorizer(
|
| 201 |
max_features=30,
|
| 202 |
stop_words='english',
|
|
@@ -235,7 +231,7 @@ def build_feature_matrix(profiles, vectorizers=None):
|
|
| 235 |
features.append(ages_norm)
|
| 236 |
logger.debug(f"Age features: {ages_norm.shape}")
|
| 237 |
|
| 238 |
-
# 2. Categorical features
|
| 239 |
logger.debug("Building categorical features...")
|
| 240 |
cat_feat = build_categorical_features(profiles)
|
| 241 |
if cat_feat.size > 0:
|
|
@@ -266,7 +262,6 @@ async def refresh_cache():
|
|
| 266 |
try:
|
| 267 |
logger.info("π Refreshing profile cache...")
|
| 268 |
|
| 269 |
-
# Fetch profiles
|
| 270 |
raw_profiles = await fetch_profiles_paginated()
|
| 271 |
|
| 272 |
if not raw_profiles or len(raw_profiles) < 5:
|
|
@@ -274,7 +269,6 @@ async def refresh_cache():
|
|
| 274 |
cache.error_message = f"Only {len(raw_profiles)} profiles, need at least 5"
|
| 275 |
return False
|
| 276 |
|
| 277 |
-
# Preprocess
|
| 278 |
logger.info("Preprocessing profiles...")
|
| 279 |
profiles = []
|
| 280 |
for p in raw_profiles:
|
|
@@ -292,7 +286,6 @@ async def refresh_cache():
|
|
| 292 |
|
| 293 |
logger.info(f"Preprocessed {len(profiles)} profiles")
|
| 294 |
|
| 295 |
-
# Build features
|
| 296 |
logger.info("Building feature matrix...")
|
| 297 |
X, vectorizers, scaler = build_feature_matrix(profiles, cache.tfidf_vectorizers)
|
| 298 |
|
|
@@ -301,7 +294,6 @@ async def refresh_cache():
|
|
| 301 |
cache.error_message = "Feature matrix is empty"
|
| 302 |
return False
|
| 303 |
|
| 304 |
-
# Update cache
|
| 305 |
cache.profiles = profiles
|
| 306 |
cache.feature_matrix = X
|
| 307 |
cache.profile_ids = [p['id'] for p in profiles]
|
|
@@ -397,7 +389,6 @@ async def recommend_query(
|
|
| 397 |
Returns raw list: [{user_id, score, reasons}, ...]
|
| 398 |
"""
|
| 399 |
matches = await _compute_matches(user_id, top_n, min_score)
|
| 400 |
-
# Transform to AiMatchService format
|
| 401 |
return [
|
| 402 |
{
|
| 403 |
"user_id": m['id'],
|
|
@@ -480,7 +471,7 @@ async def _compute_matches(user_id: str, top_n: int, min_score: float):
|
|
| 480 |
# Top N
|
| 481 |
top_positions = np.argsort(sims)[::-1][:top_n]
|
| 482 |
|
| 483 |
-
# Build result list
|
| 484 |
result = []
|
| 485 |
for pos in top_positions:
|
| 486 |
score = float(sims[pos])
|
|
@@ -498,7 +489,20 @@ async def _compute_matches(user_id: str, top_n: int, min_score: float):
|
|
| 498 |
'score': score
|
| 499 |
})
|
| 500 |
|
|
|
|
| 501 |
logger.info(f"β Generated {len(result)} recommendations for {user_id}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
return result
|
| 503 |
|
| 504 |
|
|
|
|
| 170 |
|
| 171 |
for col in CATEGORICAL_COLS:
|
| 172 |
try:
|
|
|
|
| 173 |
unique_vals = sorted(set(p[col] for p in profiles if p[col]))
|
|
|
|
| 174 |
val_to_idx = {val: idx+1 for idx, val in enumerate(unique_vals)}
|
|
|
|
| 175 |
encoded = np.array([val_to_idx.get(p[col], 0) for p in profiles])
|
| 176 |
cat_features.append(encoded.reshape(-1, 1))
|
| 177 |
except Exception as e:
|
|
|
|
| 193 |
try:
|
| 194 |
corpus = [p[col] for p in profiles]
|
| 195 |
|
|
|
|
| 196 |
tfidf = TfidfVectorizer(
|
| 197 |
max_features=30,
|
| 198 |
stop_words='english',
|
|
|
|
| 231 |
features.append(ages_norm)
|
| 232 |
logger.debug(f"Age features: {ages_norm.shape}")
|
| 233 |
|
| 234 |
+
# 2. Categorical features
|
| 235 |
logger.debug("Building categorical features...")
|
| 236 |
cat_feat = build_categorical_features(profiles)
|
| 237 |
if cat_feat.size > 0:
|
|
|
|
| 262 |
try:
|
| 263 |
logger.info("π Refreshing profile cache...")
|
| 264 |
|
|
|
|
| 265 |
raw_profiles = await fetch_profiles_paginated()
|
| 266 |
|
| 267 |
if not raw_profiles or len(raw_profiles) < 5:
|
|
|
|
| 269 |
cache.error_message = f"Only {len(raw_profiles)} profiles, need at least 5"
|
| 270 |
return False
|
| 271 |
|
|
|
|
| 272 |
logger.info("Preprocessing profiles...")
|
| 273 |
profiles = []
|
| 274 |
for p in raw_profiles:
|
|
|
|
| 286 |
|
| 287 |
logger.info(f"Preprocessed {len(profiles)} profiles")
|
| 288 |
|
|
|
|
| 289 |
logger.info("Building feature matrix...")
|
| 290 |
X, vectorizers, scaler = build_feature_matrix(profiles, cache.tfidf_vectorizers)
|
| 291 |
|
|
|
|
| 294 |
cache.error_message = "Feature matrix is empty"
|
| 295 |
return False
|
| 296 |
|
|
|
|
| 297 |
cache.profiles = profiles
|
| 298 |
cache.feature_matrix = X
|
| 299 |
cache.profile_ids = [p['id'] for p in profiles]
|
|
|
|
| 389 |
Returns raw list: [{user_id, score, reasons}, ...]
|
| 390 |
"""
|
| 391 |
matches = await _compute_matches(user_id, top_n, min_score)
|
|
|
|
| 392 |
return [
|
| 393 |
{
|
| 394 |
"user_id": m['id'],
|
|
|
|
| 471 |
# Top N
|
| 472 |
top_positions = np.argsort(sims)[::-1][:top_n]
|
| 473 |
|
| 474 |
+
# Build result list
|
| 475 |
result = []
|
| 476 |
for pos in top_positions:
|
| 477 |
score = float(sims[pos])
|
|
|
|
| 489 |
'score': score
|
| 490 |
})
|
| 491 |
|
| 492 |
+
# ββ Display recommendations in terminal ββ
|
| 493 |
logger.info(f"β Generated {len(result)} recommendations for {user_id}")
|
| 494 |
+
logger.info(f"β{'β'*60}")
|
| 495 |
+
logger.info(f"β Requesting user : {user['full_name']} ({user['gender']}, {user['age']})")
|
| 496 |
+
logger.info(f"β{'β'*60}")
|
| 497 |
+
for rank, r in enumerate(result, 1):
|
| 498 |
+
logger.info(
|
| 499 |
+
f"β #{rank:<2} {r['full_name']:<25} "
|
| 500 |
+
f"Age: {r['age']:<4} "
|
| 501 |
+
f"City: {r['city']:<15} "
|
| 502 |
+
f"Score: {r['score']*100:.1f}%"
|
| 503 |
+
)
|
| 504 |
+
logger.info(f"β{'β'*60}")
|
| 505 |
+
|
| 506 |
return result
|
| 507 |
|
| 508 |
|