-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroutine_engine.py
More file actions
436 lines (410 loc) · 22.2 KB
/
Copy pathroutine_engine.py
File metadata and controls
436 lines (410 loc) · 22.2 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
import numpy as np
from typing import List, Dict, Any
# 8D Vector Schema:
# [f_walk, f_transit, f_bike, f_green, f_gym, f_social, f_errand, f_activity]
NEIGHBORHOOD_DATABASE = [
# --- NEW YORK CITY ---
{
"id": "nyc_williamsburg",
"city": "NYC",
"name": "Williamsburg",
"borough_district": "Brooklyn",
"description": "Trendy epicenter with vibrant third-wave cafes, rooftop bars, boutique gyms, and waterside parks.",
"vector": [0.95, 0.90, 0.90, 0.70, 0.90, 0.98, 0.92, 0.95],
"vibe": "Hip & Energetic",
"activity_level": 5,
"avg_commute_mins": 20,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Devoción & Partners Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Equinox & Brooklyn Boulders", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Whole Foods & Trader Joe's North 3rd", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Radegast Hall & Westlight", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Domino Park & McCarren Park", "icon": "fa-tree"}
]
},
{
"id": "nyc_west_village",
"city": "NYC",
"name": "West Village",
"borough_district": "Manhattan",
"description": "Historic cobblestone charm, world-class dining, intimate cafes, and tree-lined walking streets.",
"vector": [0.98, 0.95, 0.85, 0.75, 0.85, 0.96, 0.95, 0.88],
"vibe": "Charming & Historic Chic",
"activity_level": 4,
"avg_commute_mins": 15,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Stumptown & Bluestone Lane", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Barry's Bootcamp & SoulCycle", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Citarella Gourmet Market", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Ear Inn & Employees Only", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Hudson River Park & Washington Square", "icon": "fa-tree"}
]
},
{
"id": "nyc_astoria",
"city": "NYC",
"name": "Astoria",
"borough_district": "Queens",
"description": "Diverse culinary paradise, community beer gardens, parks along the East River, and great subway access.",
"vector": [0.90, 0.92, 0.75, 0.75, 0.75, 0.88, 0.90, 0.78],
"vibe": "Vibrant & Neighborhood-Centric",
"activity_level": 4,
"avg_commute_mins": 25,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Kinship Coffee & Coffee + Cake", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Club Fitness New York & Astoria Park Track", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Titan Foods & Green Bay Organic", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Bohemian Hall & Sweet Afton", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Astoria Park & Charybdis Playground", "icon": "fa-tree"}
]
},
# --- WASHINGTON, DC ---
{
"id": "dc_dupont_circle",
"city": "DC",
"name": "Dupont Circle",
"borough_district": "NW",
"description": "Historic rowhouses, embassies, indie bookstores, bustling nightlife along Connecticut Ave, and Red Line transit.",
"vector": [0.96, 0.92, 0.88, 0.78, 0.85, 0.94, 0.92, 0.85],
"vibe": "Cosmopolitan & Walkable",
"activity_level": 4,
"avg_commute_mins": 18,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Kramerbooks & Emissary Cafe", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "VIDA Fitness & Solidcore", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Trader Joe's & Dupont Farmers Market", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Board Room & Madhatter", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Dupont Circle Park & Rock Creek Trails", "icon": "fa-tree"}
]
},
{
"id": "dc_adams_morgan",
"city": "DC",
"name": "Adams Morgan",
"borough_district": "NW",
"description": "Eclectic nightlife hub on 18th St, live music venues, authentic international cuisine, and bohemian character.",
"vector": [0.92, 0.82, 0.85, 0.80, 0.80, 0.96, 0.85, 0.92],
"vibe": "Eclectic & Nightlife Hub",
"activity_level": 5,
"avg_commute_mins": 25,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Tryst & SoHo Tea & Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Mint DC & Kalorama Park Yoga", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Streets Market & Harris Teeter", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Madam's Organ & Songbyrd", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Kalorama Park & Rock Creek Park", "icon": "fa-tree"}
]
},
{
"id": "dc_navy_yard",
"city": "DC",
"name": "Navy Yard / Capitol Riverfront",
"borough_district": "SE",
"description": "Modern riverfront high-rises, Nationals Park, boardwalk breweries, waterfront running trails, and trendy dining.",
"vector": [0.90, 0.88, 0.88, 0.85, 0.95, 0.90, 0.88, 0.88],
"vibe": "Modern & Riverfront Active",
"activity_level": 4,
"avg_commute_mins": 15,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Slipstream & Yellow the Cafe", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "OneLife Fitness & F45 Training", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Harris Teeter & Whole Foods Navy Yard", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Bluejacket Brewery & Dacha Beer Garden", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Yards Park & Anacostia Riverwalk Trail", "icon": "fa-tree"}
]
},
# --- BALTIMORE, MD ---
{
"id": "baltimore_fells_point",
"city": "Baltimore",
"name": "Fells Point",
"borough_district": "Historic Waterfront",
"description": "Centuries-old cobblestone streets, waterfront taverns, seafood markets, and scenic promenade strolls.",
"vector": [0.94, 0.65, 0.80, 0.70, 0.75, 0.95, 0.85, 0.85],
"vibe": "Historic Maritime & Social",
"activity_level": 4,
"avg_commute_mins": 18,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Daily Grind & Pitango Bakery Cafe", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Merritt Clubs Fort Ave & Waterfront Yoga", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Broadway Market & Whole Foods Harbor East", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Horse You Came In On & Max's Taphouse", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Thames St Waterfront Promenade", "icon": "fa-tree"}
]
},
{
"id": "baltimore_charles_village",
"city": "Baltimore",
"name": "Charles Village / Hampden",
"borough_district": "North Baltimore",
"description": "Painted ladies architecture, Johns Hopkins campus green spaces, indie craft shops, and quirky hipster culture.",
"vector": [0.90, 0.70, 0.85, 0.85, 0.75, 0.88, 0.80, 0.75],
"vibe": "Academic & Artistic",
"activity_level": 3,
"avg_commute_mins": 20,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Bird in Hand & Artifact Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Ralph S. O'Connor Center & Movement Gym", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Eddie's of Charles Village & MOM's Organic", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Dizz & Bluebird Cocktail Room", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Wyman Park Dell & Stony Run Trail", "icon": "fa-tree"}
]
},
# --- PHILADELPHIA, PA ---
{
"id": "philly_rittenhouse",
"city": "Philadelphia",
"name": "Rittenhouse Square",
"borough_district": "Center City",
"description": "Prestigious park square, world-class outdoor dining, designer boutiques, and exceptionally high walkability.",
"vector": [0.98, 0.92, 0.88, 0.85, 0.90, 0.95, 0.95, 0.88],
"vibe": "Refined & Cosmopolitan",
"activity_level": 4,
"avg_commute_mins": 12,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "La Colombe Torrefaction & Elixr Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "City Fitness Rittenhouse & Rumble Boxing", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Trader Joe's & Di Bruno Bros", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Parc Brasserie & The Franklin Mortgage & Investment Co.", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Rittenhouse Square Park & Schuylkill River Trail", "icon": "fa-tree"}
]
},
{
"id": "philly_fishtown",
"city": "Philadelphia",
"name": "Fishtown",
"borough_district": "Riverwards",
"description": "Philly's creative core with renowned craft breweries, indie music venues (The Fillmore), and artisan bakeries.",
"vector": [0.92, 0.88, 0.90, 0.65, 0.82, 0.98, 0.85, 0.92],
"vibe": "Indie Music & Culinary Hub",
"activity_level": 5,
"avg_commute_mins": 22,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "ReAnimator Coffee & La Colombe Flagship", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Bespoke Athletic Club & Tula Yoga", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Riverwards Produce & Giant Heirloom Market", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Johnny Brenda's & Frankford Hall", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Penn Treaty Park & Palmer Park", "icon": "fa-tree"}
]
},
# --- BOSTON, MA ---
{
"id": "boston_back_bay",
"city": "Boston",
"name": "Back Bay",
"borough_district": "Central Boston",
"description": "Victorian brownstones, Newbury St shopping, the Esplanade on the Charles River, and central subway access.",
"vector": [0.98, 0.95, 0.88, 0.85, 0.90, 0.92, 0.96, 0.85],
"vibe": "Historic Elegance & Active",
"activity_level": 4,
"avg_commute_mins": 14,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Thinking Cup & Pavement Coffeehouse", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Equinox Dartmouth & Boston Sports Club", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Trader Joe's Boylston & Star Market", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Salty Pig & Bukowski Tavern", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Charles River Esplanade & Boston Public Garden", "icon": "fa-tree"}
]
},
{
"id": "boston_cambridge_kendall",
"city": "Boston",
"name": "Cambridge / Kendall Square & Central",
"borough_district": "Cambridge",
"description": "Global tech & biotech capital with MIT campus, vibrant beer gardens, rowing clubs, and Red Line rapid transit.",
"vector": [0.95, 0.94, 0.96, 0.80, 0.88, 0.89, 0.90, 0.82],
"vibe": "Innovative & Active Cyclist",
"activity_level": 4,
"avg_commute_mins": 16,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Barismo & 1369 Coffee House", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Central Rock Gym (Climbing) & MIT Z-Center", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "H Mart Central & Whole Foods River St", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Muddy Charles & Cambridge Brewing Co.", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Charles River Bike Path & Killian Court", "icon": "fa-tree"}
]
},
# --- ATLANTA, GA ---
{
"id": "atlanta_midtown",
"city": "Atlanta",
"name": "Midtown Atlanta",
"borough_district": "Midtown",
"description": "High-density cultural heart, direct Piedmont Park access, BeltLine cycling, and MARTA rail connectivity.",
"vector": [0.88, 0.78, 0.90, 0.92, 0.92, 0.92, 0.88, 0.88],
"vibe": "Parkside Cultural Metropolis",
"activity_level": 4,
"avg_commute_mins": 18,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Dancing Goats Coffee & Octane", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Midtown Athletic Club & Exhale Atlanta", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Whole Foods 14th St (Multi-level flagship)", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "The Vortex Bar & Grill & Cypress Street Pint & Plate", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Piedmont Park & Atlanta BeltLine Eastside Trail", "icon": "fa-tree"}
]
},
{
"id": "atlanta_inman_park",
"city": "Atlanta",
"name": "Inman Park / Old Fourth Ward",
"borough_district": "Eastside",
"description": "Historic Victorian enclave centered on the BeltLine, Krog Street Market, and award-winning patio dining.",
"vector": [0.90, 0.65, 0.96, 0.88, 0.85, 0.96, 0.86, 0.90],
"vibe": "BeltLine Epicenter & Foodie",
"activity_level": 5,
"avg_commute_mins": 22,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Chrome Yellow Trading Co. & Spiller Park", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "The Daily Pilates & BeltLine Running Club", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Krog Street Market & Savi Provisions", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Ladybird Grove & Mess Hall & Barcelona Wine Bar", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Historic Fourth Ward Park & Springvale Park", "icon": "fa-tree"}
]
},
# --- ARLINGTON & BETHESDA (DC METRO) ---
{
"id": "arlington_clarendon",
"city": "Arlington",
"name": "Clarendon / Courthouse",
"borough_district": "Arlington Corridor",
"description": "Hyper-walkable Metro corridor packed with fitness studios, dog parks, beer gardens, and young professionals.",
"vector": [0.94, 0.92, 0.88, 0.78, 0.92, 0.90, 0.95, 0.85],
"vibe": "Active Urban-Suburban Fusion",
"activity_level": 4,
"avg_commute_mins": 15,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Northside Social & Compass Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "OrangeTheory & Gold's Gym Clarendon", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Trader Joe's & Whole Foods Clarendon", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Whitlow's on Wilson & Spider Kelly's", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Rocky Run Park & Custis Trail", "icon": "fa-tree"}
]
},
{
"id": "bethesda_downtown",
"city": "Bethesda",
"name": "Downtown Bethesda / Bethesda Row",
"borough_district": "Montgomery County",
"description": "High-end shopping, outdoor dining promenades, Capital Crescent Trail cycling, and Red Line transit.",
"vector": [0.92, 0.88, 0.85, 0.82, 0.88, 0.86, 0.94, 0.78],
"vibe": "Sophisticated & Trailside",
"activity_level": 3,
"avg_commute_mins": 22,
"matched_stores": [
{"category": "Coffee / Third Place", "name": "Quartermaine Coffee Roasters & Ceremony Coffee", "icon": "fa-mug-hot"},
{"category": "Fitness & Wellness", "name": "Equinox Bethesda & Pure Barre", "icon": "fa-dumbbell"},
{"category": "Groceries / Organic", "name": "Giant & Bethesda Central Farm Market", "icon": "fa-basket-shopping"},
{"category": "Nightlife & Pubs", "name": "Mon Ami Gabi & Tommy Joe's", "icon": "fa-martini-glass"},
{"category": "Parks & Outdoors", "name": "Capital Crescent Trail & Elm Street Park", "icon": "fa-tree"}
]
}
]
def calculate_user_vector(answers: Dict[str, Any]) -> np.ndarray:
"""
Constructs an 8-dimensional user lifestyle vector U from questionnaire answers.
Dimensions: [f_walk, f_transit, f_bike, f_green, f_gym, f_social, f_errand, f_activity]
"""
# 1. Transit Mode
transit = answers.get("transit_mode", "transit")
f_walk, f_transit, f_bike = 0.7, 0.7, 0.7
if transit == "walk_bike":
f_walk, f_bike, f_transit = 0.98, 0.95, 0.70
elif transit == "transit":
f_walk, f_transit, f_bike = 0.90, 0.98, 0.75
elif transit == "car":
f_walk, f_transit, f_bike = 0.40, 0.30, 0.30
# 2. Exercise & Outdoors (multi-select)
exercise = answers.get("exercise_habits", [])
f_green = 0.4
f_gym = 0.4
if "gym" in exercise or "crossfit" in exercise:
f_gym += 0.5
if "trails" in exercise or "running" in exercise or "dog_park" in exercise or "strolls" in exercise:
f_green += 0.5
f_bike = min(1.0, f_bike + 0.15)
f_walk = min(1.0, f_walk + 0.15)
# 3. Social & Evening Habits (multi-select)
social = answers.get("social_habits", [])
f_social = 0.3
f_errand = 0.6
if "pubs" in social or "bars" in social:
f_social += 0.3
if "dining" in social or "foodie" in social:
f_social += 0.25
f_errand += 0.2
if "coffee" in social or "third_places" in social:
f_social += 0.2
f_errand += 0.2
if "arts" in social or "theatres" in social:
f_social += 0.15
# 4. Energy & Noise Level (1 to 5)
noise_level = float(answers.get("energy_level", 4))
f_activity = (noise_level / 5.0)
# 5. Work Routine
work_routine = answers.get("work_routine", "hybrid")
if work_routine == "wfh":
f_errand = min(1.0, f_errand + 0.15) # More local third places & cafes needed
elif work_routine == "office":
f_transit = min(1.0, f_transit + 0.15)
u_vec = np.array([f_walk, f_transit, f_bike, f_green, f_gym, f_social, f_errand, f_activity], dtype=float)
# Clip between 0.1 and 1.0
return np.clip(u_vec, 0.1, 1.0)
def match_routine(answers: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
Executes Cosine Similarity + Commute Decay + Noise Tolerance formula from MVP Spec:
S_raw = (U · N) / (||U||2 ||N||2)
gamma_commute = 1.0 if T <= Tmax else exp(-1.5 * (T - Tmax) / Tmax)
delta_noise = 1.0 - (|Unoise - Nactivity| / 4) * 0.25
Score_final = S_raw * gamma * delta_noise * 100
"""
target_city = answers.get("target_city", "").strip()
source_city = answers.get("source_city", "").strip()
max_commute = float(answers.get("max_commute", 30))
user_noise = float(answers.get("energy_level", 4))
u_vec = calculate_user_vector(answers)
u_norm = np.linalg.norm(u_vec)
# Filter neighborhoods by target city (or all if target not specified)
candidates = NEIGHBORHOOD_DATABASE
if target_city:
candidates = [n for n in NEIGHBORHOOD_DATABASE if n["city"].lower() == target_city.lower()]
if not candidates:
# Fallback if no exact match
candidates = NEIGHBORHOOD_DATABASE
results = []
for n in candidates:
n_vec = np.array(n["vector"], dtype=float)
n_norm = np.linalg.norm(n_vec)
# 1. Cosine similarity
s_raw = np.dot(u_vec, n_vec) / (u_norm * n_norm) if (u_norm > 0 and n_norm > 0) else 0.0
# 2. Commute Penalty Factor (gamma)
t_commute = n.get("avg_commute_mins", 20)
if t_commute <= max_commute:
gamma = 1.0
else:
gamma = np.exp(-1.5 * (t_commute - max_commute) / max_commute)
# 3. Noise Divergence Penalty (delta)
n_activity = float(n.get("activity_level", 4))
delta_noise = 1.0 - (abs(user_noise - n_activity) / 4.0) * 0.25
# 4. Final Match Score (0 to 100)
final_score = float(s_raw * gamma * delta_noise * 100.0)
final_score = round(min(99.4, max(45.0, final_score)), 1)
# Dimension breakdown for radar chart
breakdown = {
"Walkability": int(n_vec[0] * 100),
"Public Transit": int(n_vec[1] * 100),
"Cycling & Micro-mobility": int(n_vec[2] * 100),
"Parks & Green Spaces": int(n_vec[3] * 100),
"Fitness & Gyms": int(n_vec[4] * 100),
"Dining & Nightlife": int(n_vec[5] * 100),
"Third Places & Cafes": int(n_vec[6] * 100)
}
results.append({
**n,
"match_score": final_score,
"cosine_similarity": round(float(s_raw), 3),
"commute_mins": t_commute,
"dimension_breakdown": breakdown
})
# Sort descending by match score
results.sort(key=lambda x: x["match_score"], reverse=True)
return results