adham-Ashraf commited on
Commit
34ecb66
·
verified ·
1 Parent(s): 8934e62

Upload 7 files

Browse files
Files changed (7) hide show
  1. app.py +1485 -0
  2. categories.json +318 -0
  3. floor_0_grid.npy +3 -0
  4. mall.db +0 -0
  5. navigation_memory.json +0 -0
  6. requirements.txt +9 -0
  7. sort_data.json +0 -0
app.py ADDED
@@ -0,0 +1,1485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """app.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1e0kcqimC-jHT2w8MwJMSXxzS-wJh1jYS
8
+ """
9
+
10
+ import json
11
+ import math
12
+ import os
13
+ import random
14
+ import heapq
15
+ import re
16
+ import sqlite3
17
+ import gradio as gr
18
+ import numpy as np
19
+ import matplotlib.pyplot as plt
20
+ from scipy.ndimage import label, distance_transform_edt
21
+ from gtts import gTTS
22
+ from pydub import AudioSegment
23
+ import speech_recognition as sr
24
+ import openai
25
+
26
+ CATEGORIES_FILE = "categories.json"
27
+ PRODUCTS_FILE = "sort_data.json"
28
+ DB_PATH = "mall.db"
29
+ # Smart Mall Assistant
30
+ # Navigation + chatbot + recommender system merged into one Colab-ready script.
31
+
32
+
33
+ # ============================================================
34
+ # CONFIG
35
+ # ============================================================
36
+ CELL_SIZE = 0.2
37
+ MEMORY_FILE = "navigation_memory.json"
38
+ GRID_0_FILE = "floor_0_grid.npy"
39
+ GRID_1_FILE = "floor_0_grid.npy"
40
+
41
+ OPENROUTER_KEY =os.getenv("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")
42
+ OPENROUTER_MODEL = "arcee-ai/trinity-large-preview:free"
43
+
44
+ # ============================================================
45
+ # DATA LOADING
46
+ # ============================================================
47
+ def load_json_file(path, default):
48
+ try:
49
+ with open(path, "r", encoding="utf-8") as f:
50
+ return json.load(f)
51
+ except Exception:
52
+ return default
53
+
54
+ categories_data = load_json_file(CATEGORIES_FILE, [])
55
+ products_data = load_json_file(PRODUCTS_FILE, [])
56
+
57
+ # ============================================================
58
+ # STORE / ROOM MAPPING
59
+ # ============================================================
60
+ ROOM_INFO = {
61
+ 350: "Smart Devices Hub",
62
+ 351: "Gaming & Accessories Hub",
63
+ 352: "Computer Systems Hub",
64
+ 353: "Mobile & Tablets Hub",
65
+ 354: "Health & Personal Care",
66
+ 355: "Bedroom",
67
+ 356: "Living Room",
68
+ 357: "Study/Office",
69
+ 358: "Kitchen",
70
+ 450: "Dining Room",
71
+ 451: "Bathroom",
72
+ 452: "Furnishings",
73
+ 453: "Kitchen and Dining",
74
+ 454: "Home Decor",
75
+ 455: "Tools and utility",
76
+ 456: "Lighting and Electricals",
77
+ 457: "Cleaning and Bath",
78
+ 458: "Pet and Gardening",
79
+ }
80
+
81
+ STORE_CLUSTER_ROOM = {
82
+ "Smart Devices Hub": 350,
83
+ "Gaming & Accessories Hub": 351,
84
+ "Computer Systems Hub": 352,
85
+ "Mobile & Tablets Hub": 353,
86
+ "Health & Personal Care": 354,
87
+ "Bedroom": 355,
88
+ "Living Room": 356,
89
+ "Study/Office": 357,
90
+ "Kitchen": 358,
91
+ "Dining Room": 450,
92
+ "Bathroom": 451,
93
+ "Furnishings": 452,
94
+ "Kitchen and Dining": 453,
95
+ "Home Decor": 454,
96
+ "Tools and utility": 455,
97
+ "Lighting and Electricals": 456,
98
+ "Cleaning and Bath": 457,
99
+ "Pet and Gardening": 458,
100
+ }
101
+
102
+ STORE_CLUSTER = {
103
+ "Audio": "Smart Devices Hub",
104
+ "Cameras": "Smart Devices Hub",
105
+ "Smart Home Automation": "Smart Devices Hub",
106
+ "Smart Wearables": "Smart Devices Hub",
107
+ "Gaming": "Gaming & Accessories Hub",
108
+ "Laptop Accessories": "Gaming & Accessories Hub",
109
+ "Computer Peripherals": "Computer Systems Hub",
110
+ "Laptop & Desktop": "Computer Systems Hub",
111
+ "Storage": "Computer Systems Hub",
112
+ "Mobiles": "Mobile & Tablets Hub",
113
+ "Tablets": "Mobile & Tablets Hub",
114
+ }
115
+
116
+ SUB_TO_CATEGORY = {}
117
+ for type_block in categories_data:
118
+ for cat in type_block.get("categories", []):
119
+ for sub in cat.get("subCategories", []):
120
+ SUB_TO_CATEGORY[sub] = cat["category"]
121
+
122
+ NAME_TO_ROOM = {name.lower(): room for room, name in ROOM_INFO.items()}
123
+
124
+ # ============================================================
125
+ # GRID / CENTROIDS
126
+ # ============================================================
127
+ grid_0 = np.load(GRID_0_FILE)
128
+ grid_1 = np.load(GRID_1_FILE)
129
+ GRID_SHAPE = grid_0.shape
130
+
131
+ room_centroids_f3_grid = {
132
+ 350: (70, 465),
133
+ 351: (70, 435),
134
+ 352: (70, 395),
135
+ 353: (70, 360),
136
+ 354: (70, 320),
137
+ 355: (70, 285),
138
+ 356: (70, 215),
139
+ 357: (70, 160),
140
+ 358: (70, 85),
141
+ }
142
+
143
+ room_numbers_f4 = list(range(450, 459))
144
+ room_centroids_f4_grid = {}
145
+ for old, new in zip(room_centroids_f3_grid.keys(), room_numbers_f4):
146
+ room_centroids_f4_grid[new] = room_centroids_f3_grid[old]
147
+
148
+
149
+ def apply_room_clearance():
150
+ for r, c in room_centroids_f3_grid.values():
151
+ for dr in [-1, 0, 1]:
152
+ for dc in [-1, 0, 1]:
153
+ rr, cc = r + dr, c + dc
154
+ if 0 <= rr < GRID_SHAPE[0] and 0 <= cc < GRID_SHAPE[1]:
155
+ grid_0[rr, cc] = 0
156
+ grid_1[rr, cc] = 0
157
+
158
+
159
+ apply_room_clearance()
160
+
161
+
162
+ def reset_grids():
163
+ global grid_0, grid_1, GRID_SHAPE
164
+ grid_0 = np.load(GRID_0_FILE)
165
+ grid_1 = np.load(GRID_1_FILE)
166
+ GRID_SHAPE = grid_0.shape
167
+ apply_room_clearance()
168
+
169
+ # ============================================================
170
+ # MEMORY LOG
171
+ # ============================================================
172
+ def load_memory():
173
+ try:
174
+ with open(MEMORY_FILE, "r", encoding="utf-8") as f:
175
+ return json.load(f)
176
+ except Exception:
177
+ return []
178
+
179
+
180
+ def save_memory(memory):
181
+ with open(MEMORY_FILE, "w", encoding="utf-8") as f:
182
+ json.dump(memory, f, indent=4)
183
+
184
+
185
+ user_memory = load_memory()
186
+ navigation_counter = len(user_memory) + 1
187
+
188
+
189
+ def save_navigation_mem(start_room, dest_room):
190
+ global navigation_counter
191
+ entry = {
192
+ "nav_id": f"N{navigation_counter}",
193
+ "start_room": start_room,
194
+ "dest_room": dest_room,
195
+ }
196
+ user_memory.append(entry)
197
+ save_memory(user_memory)
198
+ navigation_counter += 1
199
+ return entry
200
+
201
+ # ============================================================
202
+ # OPENROUTER CLIENT
203
+ # ============================================================
204
+ client = openai.OpenAI(
205
+ base_url="https://openrouter.ai/api/v1",
206
+ api_key=OPENROUTER_KEY,
207
+ )
208
+
209
+ SYSTEM_PROMPT = """
210
+ You are an AI assistant for indoor navigation inside a shopping mall.
211
+
212
+ Mall stores:
213
+ 350 Smart Devices Hub
214
+ 351 Gaming & Accessories Hub
215
+ 352 Computer Systems Hub
216
+ 353 Mobile & Tablets Hub
217
+ 354 Health & Personal Care
218
+ 355 Bedroom
219
+ 356 Living Room
220
+ 357 Study/Office
221
+ 358 Kitchen
222
+ 450 Dining Room
223
+ 451 Bathroom
224
+ 452 Furnishings
225
+ 453 Kitchen and Dining
226
+ 454 Home Decor
227
+ 455 Tools and utility
228
+ 456 Lighting and Electricals
229
+ 457 Cleaning and Bath
230
+ 458 Pet and Gardening
231
+
232
+ Rules:
233
+ - If the user asks about a store, answer with store information.
234
+ - If the user asks for navigation, help them navigate.
235
+ - If the user says they are hungry, suggest kitchen / dining / food-related stores.
236
+ - Keep answers concise and helpful.
237
+ """
238
+
239
+ # ============================================================
240
+ # HELPERS / SESSION STATE
241
+ # ============================================================
242
+ def set_session_defaults(session):
243
+ session = session or {}
244
+ defaults = {
245
+ "username": None,
246
+ "gender": "other",
247
+ "low": 500,
248
+ "high": 5000,
249
+ "is_new": True,
250
+ "start_floor": None,
251
+ "start_xy": None,
252
+ "nav_mode": "Normal",
253
+ "recommended_room": None,
254
+ "nav_target_room": None,
255
+ "last_dest_room": None,
256
+ "last_referenced_room": None,
257
+ "selected_subcategory": None,
258
+ "accepted_store": False,
259
+ }
260
+ for k, v in defaults.items():
261
+ session.setdefault(k, v)
262
+ return session
263
+
264
+
265
+ def get_floor(room):
266
+ if 350 <= room <= 358:
267
+ return 0
268
+ if 450 <= room <= 458:
269
+ return 1
270
+ return 0
271
+
272
+
273
+ def get_centroid(room):
274
+ if room in room_centroids_f3_grid:
275
+ return room_centroids_f3_grid[room]
276
+ if room in room_centroids_f4_grid:
277
+ return room_centroids_f4_grid[room]
278
+ return None
279
+
280
+
281
+ def clamp_point(x, y, grid):
282
+ r = int(max(0, min(grid.shape[0] - 1, round(float(x)))))
283
+ c = int(max(0, min(grid.shape[1] - 1, round(float(y)))))
284
+ return r, c
285
+
286
+
287
+ def get_store_info(room):
288
+ info = {
289
+ 350: "Smart devices, wearables, smart home products.",
290
+ 351: "Gaming items, accessories, and related hardware.",
291
+ 352: "Computers, laptops, storage, and peripherals.",
292
+ 353: "Mobile phones, tablets, and accessories.",
293
+ 354: "Health products and personal care items.",
294
+ 355: "Bedroom furniture and decor.",
295
+ 356: "Living room furniture and decor.",
296
+ 357: "Office and study furniture and tools.",
297
+ 358: "Kitchen furniture and kitchen essentials.",
298
+ 450: "Dining room furniture and dining essentials.",
299
+ 451: "Bathroom items and accessories.",
300
+ 452: "Furnishings and home textiles.",
301
+ 453: "Kitchen and dining accessories.",
302
+ 454: "Home decor products.",
303
+ 455: "Tools and utility items.",
304
+ 456: "Lighting and electrical products.",
305
+ 457: "Cleaning and bath products.",
306
+ 458: "Pet and gardening products.",
307
+ }
308
+ name = ROOM_INFO.get(room)
309
+ if not name:
310
+ return None
311
+ return f"🏪 {name} (Room {room})\n📝 {info.get(room, 'Mall store information is available for this store.')}"
312
+
313
+ def find_room_in_text(text):
314
+ text_l = (text or "").lower()
315
+ room_match = re.search(r"\b(3\d{2}|45[0-8])\b", text_l)
316
+ if room_match:
317
+ room = int(room_match.group(1))
318
+ if room in ROOM_INFO:
319
+ return room
320
+
321
+ for name, room in sorted(NAME_TO_ROOM.items(), key=lambda x: len(x[0]), reverse=True):
322
+ if name in text_l:
323
+ return room
324
+ return None
325
+
326
+
327
+ def get_intro_message():
328
+ return (
329
+ "Hello 👋 Welcome to the Mall Navigation Assistant.\n"
330
+ "You can navigate to:\n"
331
+ + "\n".join([f"- {name} ({room})" for room, name in ROOM_INFO.items()])
332
+ + "\nIf you're hungry, you can go to the kitchen/dining or cafeteria-related stores."
333
+ )
334
+
335
+
336
+ def local_chat_reply(user_text):
337
+ low = (user_text or "").lower().strip()
338
+ if not low:
339
+ return "Please type a message or ask about a store."
340
+ if any(word in low for word in ["hi", "hello", "hey", "good morning", "good evening", "how are you"]):
341
+ return "Hello 👋 Welcome to SmartMall. Tell me a store name, a room number, or where you want to go."
342
+ if any(word in low for word in ["thanks", "thank you"]):
343
+ return "You are welcome."
344
+
345
+ room = find_room_in_text(user_text)
346
+ if room is not None and any(k in low for k in ["what", "tell", "about", "info", "describe", "where"]):
347
+ info = get_store_info(room)
348
+ if info:
349
+ return info
350
+
351
+ return None
352
+
353
+
354
+ # ============================================================
355
+ # AUTH / DATABASE OPERATIONS
356
+ # ============================================================
357
+ def save_preference(username, preference):
358
+ if not preference or len(preference) != 3:
359
+ return
360
+ main, cat, sub = preference
361
+ cursor.execute(
362
+ """INSERT INTO user_preferences (username, main_category, category, subcategory)
363
+ VALUES (?, ?, ?, ?)""",
364
+ (username, main, cat, sub),
365
+ )
366
+ conn.commit()
367
+
368
+
369
+ def entry_point():
370
+ print("\n=== Smart Mall System ===")
371
+ while True:
372
+ print("\n1) Signup")
373
+ print("2) Login")
374
+ choice = input("Enter choice: ").strip()
375
+ if choice == "1":
376
+ return "signup"
377
+ if choice == "2":
378
+ return "login"
379
+ print("Invalid input ❌")
380
+
381
+
382
+ def signup():
383
+ print("\n=== SIGNUP ===")
384
+ while True:
385
+ username = input("Choose username: ").strip()
386
+ cursor.execute("SELECT username FROM users WHERE username=?", (username,))
387
+ if cursor.fetchone():
388
+ print("❌ Username exists, try again\n")
389
+ else:
390
+ break
391
+
392
+ password = input("Choose password: ").strip()
393
+ name = input("Enter name: ").strip()
394
+ age = int(input("Enter age: "))
395
+ gender = input("Enter gender (male/female): ").strip().lower()
396
+
397
+ auto = input("Use auto budget? (yes/no): ").strip().lower()
398
+ if auto == "no":
399
+ lower = float(input("Lower budget: "))
400
+ upper = float(input("Upper budget: "))
401
+ else:
402
+ lower, upper = 500, 5000
403
+
404
+ cursor.execute(
405
+ "INSERT INTO users VALUES (?, ?, ?, ?, ?, ?, ?)",
406
+ (username, password, name, age, gender, lower, upper),
407
+ )
408
+ conn.commit()
409
+
410
+ print("\n✅ Signup successful")
411
+ return username, gender, lower, upper, None
412
+
413
+
414
+ def login():
415
+ print("\n=== LOGIN ===")
416
+ while True:
417
+ username = input("Username: ").strip()
418
+ password = input("Password: ").strip()
419
+
420
+ cursor.execute(
421
+ "SELECT * FROM users WHERE username=? AND password=?",
422
+ (username, password),
423
+ )
424
+ user = cursor.fetchone()
425
+
426
+ if user:
427
+ print("✅ Login successful")
428
+ break
429
+ print("❌ Invalid credentials, try again\n")
430
+
431
+ cursor.execute(
432
+ """SELECT main_category, category, subcategory
433
+ FROM user_preferences
434
+ WHERE username=?
435
+ ORDER BY id DESC LIMIT 1""",
436
+ (username,),
437
+ )
438
+ pref = cursor.fetchone()
439
+ preference = pref if pref else None
440
+
441
+ return username, user[4], user[5], user[6], preference
442
+
443
+
444
+ def auth_system():
445
+ while True:
446
+ mode = entry_point()
447
+ if mode == "signup":
448
+ user_data = signup()
449
+ if user_data:
450
+ return user_data, True
451
+ elif mode == "login":
452
+ user_data = login()
453
+ if user_data:
454
+ return user_data, False
455
+ print("❌ Authentication failed, try again...\n")
456
+
457
+
458
+ def get_ble_location():
459
+ print("\n📍 Enter your current location")
460
+ x = float(input("X coordinate: "))
461
+ y = float(input("Y coordinate: "))
462
+ return (x, y)
463
+
464
+
465
+ def save_navigation(username, start_room, dest_room):
466
+ if isinstance(start_room, tuple):
467
+ start_room = f"{start_room[0]},{start_room[1]}"
468
+ cursor.execute(
469
+ "INSERT INTO history (username, start_point, end_store) VALUES (?, ?, ?)",
470
+ (username, start_room, str(dest_room)),
471
+ )
472
+ conn.commit()
473
+
474
+ # ============================================================
475
+ # STORE RECOMMENDATION
476
+ # ============================================================
477
+ def nearest_store(user_pos):
478
+ best_room = None
479
+ best_dist = float("inf")
480
+ for _, room in STORE_CLUSTER_ROOM.items():
481
+ centroid = get_centroid(room)
482
+ if centroid is None:
483
+ continue
484
+ dist = np.sqrt((user_pos[0] - centroid[0]) ** 2 + (user_pos[1] - centroid[1]) ** 2)
485
+ if dist < best_dist:
486
+ best_dist = dist
487
+ best_room = room
488
+ return best_room
489
+
490
+
491
+ def most_visited_store(username):
492
+ cursor.execute(
493
+ """SELECT end_store, COUNT(*) as cnt
494
+ FROM history
495
+ WHERE username=?
496
+ GROUP BY end_store
497
+ ORDER BY cnt DESC""",
498
+ (username,),
499
+ )
500
+ result = cursor.fetchone()
501
+ return int(result[0]) if result else None
502
+
503
+
504
+ # ============================================================
505
+ # PRODUCT RECOMMENDER
506
+ # ============================================================
507
+ def clean_price(price_str):
508
+ digits = "".join(filter(str.isdigit, str(price_str)))
509
+ return int(digits) if digits else 0
510
+
511
+
512
+ def get_categories_for_type(type_name):
513
+ for t in categories_data:
514
+ if t.get("type") == type_name:
515
+ return [c.get("category") for c in t.get("categories", [])]
516
+ return []
517
+
518
+
519
+ def get_subcategories(type_name, category_name):
520
+ for t in categories_data:
521
+ if t.get("type") == type_name:
522
+ for c in t.get("categories", []):
523
+ if c.get("category") == category_name:
524
+ return c.get("subCategories", [])
525
+ return []
526
+
527
+
528
+ def resolve_store_from_category(parent_category):
529
+ store_name = STORE_CLUSTER.get(parent_category, parent_category)
530
+ room = STORE_CLUSTER_ROOM.get(store_name)
531
+ return store_name, room
532
+
533
+
534
+ def category_flow():
535
+ print("\n🧭 Category Selection")
536
+
537
+ print("\nMain categories:")
538
+ for i, c in enumerate(categories_data):
539
+ print(i, c["type"])
540
+ main = int(input("Select main category: "))
541
+ main_cat = categories_data[main]
542
+
543
+ print("\nSub categories:")
544
+ for i, c in enumerate(main_cat["categories"]):
545
+ print(i, c["category"])
546
+ sub = int(input("Select category: "))
547
+ sub_cat = main_cat["categories"][sub]
548
+
549
+ print("\nSub-sub categories:")
550
+ for i, c in enumerate(sub_cat["subCategories"]):
551
+ print(i, c)
552
+ sub_sub = int(input("Select subcategory: "))
553
+
554
+ selected_subcategory = sub_cat["subCategories"][sub_sub]
555
+ if selected_subcategory is None:
556
+ print("❌ No subcategory selected")
557
+ return None, None, None, None
558
+
559
+ main_category = main_cat["type"]
560
+ parent_category = sub_cat["category"]
561
+ _, end_store = resolve_store_from_category(parent_category)
562
+ return selected_subcategory, end_store, parent_category, main_category
563
+
564
+
565
+ def get_budget(default_low, default_high):
566
+ print("\n💰 Budget Selection")
567
+ choice = input("Use auto budget? (yes/no): ").strip().lower()
568
+ if choice == "no":
569
+ lower = float(input("Enter lower budget: "))
570
+ upper = float(input("Enter upper budget: "))
571
+ else:
572
+ lower, upper = default_low, default_high
573
+ return lower, upper
574
+
575
+
576
+ def get_products(subcategory):
577
+ if not subcategory:
578
+ return []
579
+
580
+ result = []
581
+ subcategory = str(subcategory).strip().lower()
582
+
583
+ for type_block in products_data:
584
+ for cat in type_block.get("items", []):
585
+ cat_name = cat.get("category")
586
+ for sub in cat.get("items", []):
587
+ sub_name = sub.get("subCategory")
588
+ if cat_name and cat_name.lower() == subcategory:
589
+ result.extend(sub.get("items", []))
590
+ elif sub_name and sub_name.lower() == subcategory:
591
+ result.extend(sub.get("items", []))
592
+ return result
593
+
594
+
595
+ def filter_by_budget(products, low, high):
596
+ filtered = []
597
+ for p in products:
598
+ price = clean_price(p.get("Price", 0))
599
+ if low <= price <= high:
600
+ item = dict(p)
601
+ item["price_int"] = price
602
+ filtered.append(item)
603
+ return filtered
604
+
605
+
606
+ def gender_filter(products, gender):
607
+ if gender not in ["male", "female"]:
608
+ return products
609
+
610
+ keywords = {
611
+ "male": ["gaming", "tool", "power", "sports", "fitness"],
612
+ "female": ["beauty", "hair", "skin", "cosmetic", "makeup", "fashion"],
613
+ }
614
+
615
+ preferred, others = [], []
616
+ for p in products:
617
+ name = str(p.get("Name", "")).lower()
618
+ if any(k in name for k in keywords[gender]):
619
+ preferred.append(p)
620
+ else:
621
+ others.append(p)
622
+ return preferred + others
623
+
624
+
625
+ def add_promotions(products):
626
+ result = []
627
+ for p in products:
628
+ discount = random.randint(10, 50)
629
+ original = clean_price(p.get("Price", 0))
630
+ discounted = int(original * (1 - discount / 100))
631
+ result.append({
632
+ "Name": p.get("Name", "Unknown Product"),
633
+ "Brand": p.get("Brand", "N/A"),
634
+ "Original": f"{original}",
635
+ "Discounted": f"{discounted}",
636
+ "Discount": discount,
637
+ "Rating": p.get("Rating", 0),
638
+ "Reviews": p.get("No of Reviews", 0),
639
+ })
640
+ return result
641
+
642
+
643
+ def recommend_for_user(selected_subcategory, gender, low_budget, high_budget):
644
+ products = get_products(selected_subcategory)
645
+ if not products:
646
+ return {"store": "Unknown", "room": None, "products": []}
647
+
648
+ products = filter_by_budget(products, low_budget, high_budget)
649
+ if not products:
650
+ products = get_products(selected_subcategory)
651
+
652
+ products = gender_filter(products, gender)
653
+ products = sorted(products, key=lambda x: x.get("Rating", 0), reverse=True)
654
+ top_products = add_promotions(products[:5])
655
+
656
+ parent_category = SUB_TO_CATEGORY.get(selected_subcategory)
657
+ store_name = STORE_CLUSTER.get(parent_category, parent_category) if parent_category else None
658
+ room = STORE_CLUSTER_ROOM.get(store_name) if store_name else None
659
+
660
+ return {"store": store_name or "Unknown", "room": room, "products": top_products}
661
+
662
+ # ============================================================
663
+ # NAVIGATION / A*
664
+ # ============================================================
665
+ def find_nearest_free(grid, r, c):
666
+ free = (grid == 0)
667
+ _, ind = distance_transform_edt(~free, return_indices=True)
668
+ nr, nc = ind[:, r, c]
669
+ return int(nr), int(nc)
670
+
671
+
672
+ def extract_stairs(grid):
673
+ binary = (grid == 2).astype(int)
674
+ labeled, num = label(binary)
675
+ return labeled, num
676
+
677
+
678
+ stairs_0, n0 = extract_stairs(grid_0)
679
+ stairs_1, n1 = extract_stairs(grid_1)
680
+ stairs = {}
681
+ stair_cells = {0: {}, 1: {}}
682
+ for i in range(1, min(n0, n1) + 1):
683
+ sid = f"S{i}"
684
+ stairs[sid] = {"floors": [0, 1]}
685
+ for r, c in np.argwhere(stairs_0 == i):
686
+ stair_cells[0][(int(r), int(c))] = sid
687
+ for r, c in np.argwhere(stairs_1 == i):
688
+ stair_cells[1][(int(r), int(c))] = sid
689
+
690
+ elevator_cells = {0: set(), 1: set()}
691
+ for f in [0, 1]:
692
+ grid = grid_0 if f == 0 else grid_1
693
+ coords = np.argwhere(grid == 3)
694
+ for r, c in coords:
695
+ elevator_cells[f].add((int(r), int(c)))
696
+
697
+ fire_active = False
698
+ fire_room = None
699
+ fire_step = 0
700
+ fire_center = None
701
+ crowded_active = False
702
+ crowded_room = None
703
+ crowded_center = None
704
+
705
+
706
+ def mark_danger(grid, centroid, radius=20):
707
+ r0, c0 = centroid
708
+ for i in range(grid.shape[0]):
709
+ for j in range(grid.shape[1]):
710
+ dist = np.sqrt((i - r0) ** 2 + (j - c0) ** 2)
711
+ if dist < radius:
712
+ grid[i, j] = 1
713
+
714
+
715
+ def mark_crowd(grid, centroid, room_radius=15, corridor_radius=40):
716
+ r0, c0 = centroid
717
+ for i in range(grid.shape[0]):
718
+ for j in range(grid.shape[1]):
719
+ dist = np.sqrt((i - r0) ** 2 + (j - c0) ** 2)
720
+ if dist < room_radius:
721
+ grid[i, j] = 5
722
+ elif dist < corridor_radius and grid[i, j] == 0:
723
+ grid[i, j] = 6
724
+
725
+
726
+ def heuristic(a, b):
727
+ return abs(a[1] - b[1]) + abs(a[2] - b[2]) + abs(a[0] - b[0]) * 10
728
+
729
+
730
+ def neighbors(node, mode):
731
+ floor, r, c = node
732
+ grid = grid_0 if floor == 0 else grid_1
733
+ moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
734
+ result = []
735
+
736
+ for dr, dc in moves:
737
+ nr = r + dr
738
+ nc = c + dc
739
+ if 0 <= nr < grid.shape[0] and 0 <= nc < grid.shape[1]:
740
+ cell = grid[nr, nc]
741
+ if cell == 1:
742
+ continue
743
+
744
+ cost = 1
745
+
746
+ if fire_active and fire_center is not None:
747
+ fr, fc = fire_center
748
+ dist = np.sqrt((nr - fr) ** 2 + (nc - fc) ** 2)
749
+ if dist < 120:
750
+ cost += (120 - dist) * 0.3
751
+
752
+ if cell == 4:
753
+ cost += 8
754
+
755
+ if mode == "Crowded":
756
+ if cell == 5:
757
+ cost += 200
758
+ elif cell == 6:
759
+ cost += 40
760
+
761
+ if mode == "Special Needs" and cell == 4:
762
+ cost += 100
763
+
764
+ result.append(((floor, int(nr), int(nc)), cost))
765
+
766
+ is_stair = (r, c) in stair_cells[floor]
767
+ is_elevator = (r, c) in elevator_cells[floor]
768
+
769
+ if mode == "Special Needs":
770
+ if is_elevator:
771
+ for f in [0, 1]:
772
+ if f != floor:
773
+ result.append(((f, r, c), 10))
774
+ else:
775
+ if is_stair:
776
+ sid = stair_cells[floor][(r, c)]
777
+ cost = 15 if mode != "Crowded" else 40
778
+ if fire_active and fire_center is not None:
779
+ fr, fc = fire_center
780
+ dist = np.sqrt((r - fr) ** 2 + (c - fc) ** 2)
781
+ if dist < 150:
782
+ cost += 200
783
+ for f in stairs[sid]["floors"]:
784
+ if f != floor:
785
+ result.append(((f, r, c), cost))
786
+
787
+ return result
788
+
789
+
790
+ def astar(start, goal, mode="Normal"):
791
+ open_set = []
792
+ heapq.heappush(open_set, (0, start))
793
+ came = {}
794
+ g = {start: 0}
795
+
796
+ while open_set:
797
+ _, cur = heapq.heappop(open_set)
798
+ if cur == goal:
799
+ path = []
800
+ while cur in came:
801
+ path.append(cur)
802
+ cur = came[cur]
803
+ path.append(start)
804
+ return path[::-1]
805
+
806
+ for nxt, cost in neighbors(cur, mode):
807
+ ng = g[cur] + cost
808
+ if nxt not in g or ng < g[nxt]:
809
+ g[nxt] = ng
810
+ heapq.heappush(open_set, (ng + heuristic(nxt, goal), nxt))
811
+ came[nxt] = cur
812
+ return None
813
+
814
+
815
+ def path_to_instructions(path):
816
+ if not path:
817
+ return "No path found."
818
+
819
+ instructions = []
820
+ last_floor, last_r, last_c = path[0]
821
+ current_dir = None
822
+ dist = 0
823
+
824
+ def add_instruction(direction, distance):
825
+ if distance > 0:
826
+ instructions.append(f"Move {direction} {distance * CELL_SIZE:.1f}m.")
827
+
828
+ for i in range(1, len(path)):
829
+ f, r, c = path[i]
830
+ if f != last_floor:
831
+ add_instruction(current_dir, dist)
832
+ instructions.append(f"Take stairs from floor {last_floor + 3} to floor {f + 3}.")
833
+ current_dir = None
834
+ dist = 0
835
+ last_floor = f
836
+
837
+ dr = r - last_r
838
+ dc = c - last_c
839
+ if abs(dr) > abs(dc):
840
+ direction = "down" if dr > 0 else "up"
841
+ step = abs(dr)
842
+ else:
843
+ direction = "right" if dc > 0 else "left"
844
+ step = abs(dc)
845
+
846
+ if direction == current_dir:
847
+ dist += step
848
+ else:
849
+ add_instruction(current_dir, dist)
850
+ current_dir = direction
851
+ dist = step
852
+
853
+ last_r, last_c = r, c
854
+
855
+ add_instruction(current_dir, dist)
856
+ instructions.append("You have arrived at your destination room.")
857
+ return ". ".join(instructions)
858
+
859
+
860
+ def plot_static_path(path):
861
+ fig, axs = plt.subplots(1, 2, figsize=(18, 6))
862
+ grids = [grid_0, grid_1]
863
+
864
+ for i, grid in enumerate(grids):
865
+ axs[i].imshow(grid, cmap="gray_r", origin="upper")
866
+ stairs_pos = np.where(grid == 2)
867
+ axs[i].scatter(stairs_pos[1], stairs_pos[0], s=40, label="Stairs")
868
+
869
+ xs = [p[2] for p in path if p[0] == i]
870
+ ys = [p[1] for p in path if p[0] == i]
871
+ if xs and ys:
872
+ axs[i].plot(xs, ys, linewidth=3, label="Path")
873
+ axs[i].scatter(xs[0], ys[0], s=80, label="Start")
874
+ axs[i].scatter(xs[-1], ys[-1], s=80, label="End")
875
+
876
+ axs[i].set_title(f"Floor {i + 3}")
877
+ axs[i].legend()
878
+
879
+ img_path = "/content/static_path.png"
880
+ plt.savefig(img_path)
881
+ plt.close()
882
+ return img_path
883
+
884
+
885
+ def navigate(start_floor, start_xy, dest_room, mode="Normal"):
886
+ if start_xy is None or dest_room is None:
887
+ return None, "❌ Missing start or destination."
888
+
889
+ sf = int(start_floor)
890
+ sr, sc = clamp_point(start_xy[0], start_xy[1], grid_0 if sf == 0 else grid_1)
891
+ sr, sc = find_nearest_free(grid_0 if sf == 0 else grid_1, sr, sc)
892
+
893
+ df = get_floor(dest_room)
894
+ dc = get_centroid(dest_room)
895
+ if dc is None:
896
+ return None, "❌ Could not find room centroid."
897
+
898
+ gr, gc = clamp_point(dc[0], dc[1], grid_0 if df == 0 else grid_1)
899
+ gr, gc = find_nearest_free(grid_0 if df == 0 else grid_1, gr, gc)
900
+
901
+ path = astar((sf, sr, sc), (df, gr, gc), mode)
902
+ instructions = path_to_instructions(path)
903
+ img_path = plot_static_path(path) if path else None
904
+ return img_path, instructions
905
+
906
+ # ============================================================
907
+ # CHATBOT LOGIC
908
+ # ============================================================
909
+ def chatbot_response_fallback(text):
910
+ local = local_chat_reply(text)
911
+ if local is not None:
912
+ return local
913
+ try:
914
+ response = client.chat.completions.create(
915
+ model=OPENROUTER_MODEL,
916
+ messages=[
917
+ {"role": "system", "content": SYSTEM_PROMPT},
918
+ {"role": "user", "content": text},
919
+ ],
920
+ )
921
+ content = response.choices[0].message.content
922
+ if content:
923
+ return content
924
+ except Exception:
925
+ pass
926
+ return "I am here to help with mall navigation and store information. Try a store name like Electronics Store or room 351."
927
+
928
+
929
+ def extract_rooms(user_text, history):
930
+ lower = (user_text or "").lower().strip()
931
+ room = find_room_in_text(user_text)
932
+
933
+ if room is not None:
934
+ if any(k in lower for k in ["what is", "tell me", "about", "info", "describe", "where is"]):
935
+ return "chat", None, room, False
936
+ if any(k in lower for k in ["take me", "guide me", "go there", "navigate", "go to", "show me"]):
937
+ return "navigation", None, room, True
938
+ return "navigation", None, room, False
939
+
940
+ if any(phrase in lower for phrase in ["take me there", "guide me there", "go there", "navigate me there"]):
941
+ return "navigation", None, None, True
942
+
943
+ conversation = ""
944
+ for u, b in history[-6:]:
945
+ if u:
946
+ conversation += f"User: {u}\n"
947
+ if b:
948
+ conversation += f"Assistant: {b}\n"
949
+
950
+ prompt = f"""
951
+ You are an AI assistant for indoor navigation inside a shopping mall.
952
+
953
+ Extract navigation intent from the conversation.
954
+
955
+ Return JSON only with:
956
+ {{
957
+ "intent": "navigation" or "chat",
958
+ "start_room": null,
959
+ "dest_room": null,
960
+ "confirmed": true or false
961
+ }}
962
+
963
+ Conversation:
964
+ {conversation}
965
+
966
+ User message:
967
+ {user_text}
968
+ """
969
+
970
+ try:
971
+ response = client.chat.completions.create(
972
+ model=OPENROUTER_MODEL,
973
+ messages=[{"role": "user", "content": prompt}],
974
+ response_format={"type": "json_object"},
975
+ )
976
+ data = json.loads(response.choices[0].message.content)
977
+ intent = data.get("intent", "chat")
978
+ start_room = data.get("start_room")
979
+ dest_room = data.get("dest_room")
980
+ confirmed = data.get("confirmed", False)
981
+ if start_room:
982
+ start_room = int(start_room)
983
+ if dest_room:
984
+ dest_room = int(dest_room)
985
+ return intent, start_room, dest_room, confirmed
986
+ except Exception:
987
+ return "chat", None, None, False
988
+
989
+
990
+ def chatbot_respond(user_text, history, session):
991
+ session = set_session_defaults(session)
992
+ history = history or []
993
+ if not history:
994
+ history.append(("", get_intro_message()))
995
+
996
+ lower = (user_text or "").lower().strip()
997
+ local = local_chat_reply(user_text)
998
+ if local is not None and not any(k in lower for k in ["take me", "guide me", "go there", "navigate", "go to", "show me"]):
999
+ history.append((user_text, local))
1000
+ try:
1001
+ gTTS(local).save("/content/voice.mp3")
1002
+ except Exception:
1003
+ pass
1004
+ return history, None, "/content/voice.mp3", session
1005
+
1006
+ intent, _, dest_room, confirmed = extract_rooms(user_text, history)
1007
+
1008
+ info_room = find_room_in_text(user_text)
1009
+ if info_room is not None and any(k in lower for k in ["what", "tell", "about", "info", "describe", "where"]):
1010
+ info = get_store_info(info_room)
1011
+ if info:
1012
+ bot = f"{info}\n\nWould you like navigation to {ROOM_INFO[info_room]} (Room {info_room})?"
1013
+ session["last_referenced_room"] = info_room
1014
+ session["last_dest_room"] = info_room
1015
+ history.append((user_text, bot))
1016
+ try:
1017
+ gTTS(bot).save("/content/voice.mp3")
1018
+ except Exception:
1019
+ pass
1020
+ return history, None, "/content/voice.mp3", session
1021
+
1022
+ if intent == "navigation" and dest_room is not None:
1023
+ store_name = ROOM_INFO.get(dest_room, f"Room {dest_room}")
1024
+ if not confirmed:
1025
+ bot = f"I can guide you to {store_name} (Room {dest_room}). Do you want navigation?"
1026
+ history.append((user_text, bot))
1027
+ session["last_referenced_room"] = dest_room
1028
+ session["last_dest_room"] = dest_room
1029
+ session["nav_target_room"] = dest_room
1030
+ try:
1031
+ gTTS(bot).save("/content/voice.mp3")
1032
+ except Exception:
1033
+ pass
1034
+ return history, None, "/content/voice.mp3", session
1035
+
1036
+ if session.get("start_floor") is None or session.get("start_xy") is None:
1037
+ bot = "Please set your starting location first in the Shop & Navigate tab."
1038
+ history.append((user_text, bot))
1039
+ try:
1040
+ gTTS(bot).save("/content/voice.mp3")
1041
+ except Exception:
1042
+ pass
1043
+ return history, None, "/content/voice.mp3", session
1044
+
1045
+ img_path, instructions = navigate(session["start_floor"], session["start_xy"], dest_room, session.get("nav_mode", "Normal"))
1046
+ save_navigation(session.get("username", "guest"), (session["start_floor"], session["start_xy"]), dest_room)
1047
+ save_navigation_mem(f"floor={session['start_floor']},x={session['start_xy'][0]},y={session['start_xy'][1]}", dest_room)
1048
+
1049
+ bot = f"🗺️ Navigating to {store_name} (Room {dest_room})\n\n{instructions}"
1050
+ history.append((user_text, bot))
1051
+ session["last_dest_room"] = dest_room
1052
+ session["nav_target_room"] = dest_room
1053
+ try:
1054
+ gTTS(f"Navigating to {store_name}. {instructions.replace(chr(10), ' ')}").save("/content/voice.mp3")
1055
+ except Exception:
1056
+ pass
1057
+ return history, img_path, "/content/voice.mp3", session
1058
+
1059
+ if any(phrase in lower for phrase in ["take me there", "guide me there", "go there", "navigate me there"]):
1060
+ dest = (
1061
+ session.get("last_dest_room")
1062
+ or session.get("nav_target_room")
1063
+ or session.get("last_referenced_room")
1064
+ or session.get("recommended_room")
1065
+ )
1066
+ if dest is not None:
1067
+ store_name = ROOM_INFO.get(dest, f"Room {dest}")
1068
+ if session.get("start_floor") is None or session.get("start_xy") is None:
1069
+ bot = f"I can take you to {store_name}, but I need your starting location first in the Shop & Navigate tab."
1070
+ history.append((user_text, bot))
1071
+ try:
1072
+ gTTS(bot).save("/content/voice.mp3")
1073
+ except Exception:
1074
+ pass
1075
+ return history, None, "/content/voice.mp3", session
1076
+
1077
+ img_path, instructions = navigate(session["start_floor"], session["start_xy"], dest, session.get("nav_mode", "Normal"))
1078
+ save_navigation(session.get("username", "guest"), (session["start_floor"], session["start_xy"]), dest)
1079
+ save_navigation_mem(f"floor={session['start_floor']},x={session['start_xy'][0]},y={session['start_xy'][1]}", dest)
1080
+
1081
+ bot = f"🗺️ Navigating to {store_name} (Room {dest})\n\n{instructions}"
1082
+ history.append((user_text, bot))
1083
+ session["last_dest_room"] = dest
1084
+ session["nav_target_room"] = dest
1085
+ try:
1086
+ gTTS(f"Navigating to {store_name}. {instructions.replace(chr(10), ' ')}").save("/content/voice.mp3")
1087
+ except Exception:
1088
+ pass
1089
+ return history, img_path, "/content/voice.mp3", session
1090
+
1091
+ bot = chatbot_response_fallback(user_text)
1092
+ history.append((user_text, bot))
1093
+ try:
1094
+ gTTS(bot).save("/content/voice.mp3")
1095
+ except Exception:
1096
+ pass
1097
+ return history, None, "/content/voice.mp3", session
1098
+
1099
+
1100
+ def speech_to_text(audio):
1101
+ if audio is None:
1102
+ return ""
1103
+ try:
1104
+ sound = AudioSegment.from_file(audio)
1105
+ wav = "/content/temp.wav"
1106
+ sound.export(wav, format="wav")
1107
+ r = sr.Recognizer()
1108
+ with sr.AudioFile(wav) as src:
1109
+ data = r.record(src)
1110
+ return r.recognize_google(data)
1111
+ except Exception:
1112
+ return ""
1113
+
1114
+ # ============================================================
1115
+ # UI HELPERS
1116
+ # ============================================================
1117
+ def format_products_html(promos, store_name, room):
1118
+ if not promos:
1119
+ return "<div style='color:#ff6b6b;padding:20px;text-align:center;font-family:monospace'>❌ No products found in your budget range.</div>"
1120
+
1121
+ cards = ""
1122
+ for p in promos:
1123
+ stars = "⭐" * int(round(float(p.get("Rating", 0))))
1124
+ cards += f"""
1125
+ <div style="background:linear-gradient(135deg,#1a1a2e,#16213e);border:1px solid #00d4ff33;
1126
+ border-radius:12px;padding:16px;margin-bottom:12px;">
1127
+ <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:10px;">
1128
+ <div style="flex:1;">
1129
+ <div style="font-size:11px;color:#888;font-family:monospace;margin-bottom:4px">{p.get('Brand','N/A')}</div>
1130
+ <div style="color:#e0e0e0;font-size:13px;font-family:monospace;line-height:1.4">{p.get('Name','Unknown')}</div>
1131
+ <div style="margin-top:8px;font-size:12px;color:#ffd700">{stars} {p.get('Rating',0)} ({int(p.get('Reviews',0)):,} reviews)</div>
1132
+ </div>
1133
+ <div style="text-align:right;flex-shrink:0;">
1134
+ <div style="color:#ff6b6b;font-size:12px;text-decoration:line-through;font-family:monospace">{p.get('Original','0')} EGP</div>
1135
+ <div style="color:#00ff88;font-size:16px;font-weight:bold;font-family:monospace">{p.get('Discounted','0')} EGP</div>
1136
+ <div style="background:#ff4500;color:white;border-radius:20px;padding:3px 10px;
1137
+ font-size:11px;font-weight:bold;margin-top:4px;font-family:monospace">{p.get('Discount',0)}% OFF 🔥</div>
1138
+ </div>
1139
+ </div>
1140
+ </div>"""
1141
+
1142
+ return f"""
1143
+ <div style="font-family:monospace;max-height:500px;overflow-y:auto;padding:4px;">
1144
+ <div style="color:#00d4ff;font-size:14px;font-weight:bold;margin-bottom:12px;
1145
+ padding-bottom:8px;border-bottom:1px solid #00d4ff33;">
1146
+ 🏪 {store_name} · Room {room}
1147
+ </div>
1148
+ {cards}
1149
+ </div>"""
1150
+
1151
+
1152
+ def set_location_ui(x, y, floor_choice, session):
1153
+ session = set_session_defaults(session)
1154
+ if x is None or y is None:
1155
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Please enter coordinates.</div>", session
1156
+ session["start_floor"] = 0 if str(floor_choice) == "3" else 1
1157
+ session["start_xy"] = (float(x), float(y))
1158
+ session["start_room"] = session["start_xy"]
1159
+ return f"<div style='color:#00ff88;font-family:monospace'>✅ Location saved: floor {int(floor_choice)}, x={x}, y={y}</div>", session
1160
+
1161
+
1162
+ def recommend_store_for_session(session):
1163
+ session = set_session_defaults(session)
1164
+ if not session.get("username"):
1165
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Please login first.</div>", session
1166
+ if session.get("start_xy") is None or session.get("start_floor") is None:
1167
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Please enter your current location first.</div>", session
1168
+
1169
+ if session.get("is_new", True):
1170
+ room = nearest_store(session["start_xy"])
1171
+ msg = "🆕 New user → Nearest store recommended"
1172
+ else:
1173
+ room = most_visited_store(session["username"])
1174
+ if room is None:
1175
+ room = nearest_store(session["start_xy"])
1176
+ msg = "🔁 Returning user → Most visited store recommended"
1177
+
1178
+ store_name = ROOM_INFO.get(room, f"Room {room}")
1179
+ session["recommended_room"] = room
1180
+ session["nav_target_room"] = room
1181
+ session["last_referenced_room"] = room
1182
+ session["last_dest_room"] = room
1183
+
1184
+ html = f"""
1185
+ <div style="background:linear-gradient(135deg,#1a1a2e,#0d2137);border:1px solid #00d4ff55;
1186
+ border-radius:12px;padding:20px;font-family:monospace;">
1187
+ <div style="color:#888;font-size:11px;letter-spacing:2px">{msg}</div>
1188
+ <div style="color:#00d4ff;font-size:22px;font-weight:bold;margin:8px 0">{store_name}</div>
1189
+ <div style="color:#555;font-size:12px">Room {room}</div>
1190
+ </div>"""
1191
+ return html, session
1192
+
1193
+
1194
+ def update_cat(type_name):
1195
+ return gr.update(choices=get_categories_for_type(type_name), value=None)
1196
+
1197
+
1198
+ def update_sub(type_name, cat_name):
1199
+ return gr.update(choices=get_subcategories(type_name, cat_name), value=None)
1200
+
1201
+
1202
+ def handle_agreement(choice, session):
1203
+ session = set_session_defaults(session)
1204
+ session["accepted_store"] = (choice or "").startswith("✅")
1205
+ types = [t["type"] for t in categories_data]
1206
+ if session["accepted_store"]:
1207
+ return (
1208
+ gr.update(visible=False),
1209
+ gr.update(visible=False),
1210
+ gr.update(visible=False),
1211
+ gr.update(choices=types, visible=True),
1212
+ )
1213
+ return (
1214
+ gr.update(choices=types, visible=True),
1215
+ gr.update(visible=True),
1216
+ gr.update(visible=True),
1217
+ gr.update(choices=types, visible=True),
1218
+ )
1219
+
1220
+
1221
+ def do_navigate(choice, type_name, cat_name, sub_name, nav_mode_val, danger_room, session):
1222
+ session = set_session_defaults(session)
1223
+ if not session.get("username"):
1224
+ return "❌ Please login first.", None
1225
+ if session.get("start_xy") is None or session.get("start_floor") is None:
1226
+ return "❌ Please set your location first.", None
1227
+
1228
+ reset_grids()
1229
+
1230
+ if "Yes" in (choice or ""):
1231
+ dest_room = session.get("recommended_room")
1232
+ if dest_room is None:
1233
+ return "❌ Please get a recommendation first.", None
1234
+ else:
1235
+ if not sub_name:
1236
+ return "❌ Please select a sub-category.", None
1237
+ parent = SUB_TO_CATEGORY.get(sub_name, cat_name)
1238
+ store = STORE_CLUSTER.get(parent, parent)
1239
+ dest_room = STORE_CLUSTER_ROOM.get(store)
1240
+ if dest_room is None:
1241
+ return "❌ Could not map the selected category to a store.", None
1242
+
1243
+ session["dest_room"] = dest_room
1244
+ session["nav_mode"] = nav_mode_val
1245
+
1246
+ room_val = None
1247
+ try:
1248
+ if danger_room is not None and str(danger_room).strip() != "":
1249
+ room_val = int(float(danger_room))
1250
+ except Exception:
1251
+ room_val = None
1252
+
1253
+ global fire_active, fire_room, fire_step, fire_center
1254
+ global crowded_active, crowded_room, crowded_center
1255
+
1256
+ fire_active = False
1257
+ crowded_active = False
1258
+ fire_room = None
1259
+ crowded_room = None
1260
+ fire_center = None
1261
+ crowded_center = None
1262
+
1263
+ if nav_mode_val == "Fire" and room_val is not None and room_val in ROOM_INFO:
1264
+ fire_active = True
1265
+ fire_room = room_val
1266
+ fire_center = get_centroid(room_val)
1267
+ if fire_center is not None:
1268
+ fire_step += 1
1269
+ radius = 20 + fire_step * 6
1270
+ if get_floor(room_val) == 0:
1271
+ mark_danger(grid_0, fire_center, radius)
1272
+ else:
1273
+ mark_danger(grid_1, fire_center, radius)
1274
+
1275
+ if nav_mode_val == "Crowded" and room_val is not None and room_val in ROOM_INFO:
1276
+ crowded_active = True
1277
+ crowded_room = room_val
1278
+ crowded_center = get_centroid(room_val)
1279
+ if crowded_center is not None:
1280
+ if get_floor(room_val) == 0:
1281
+ mark_crowd(grid_0, crowded_center)
1282
+ else:
1283
+ mark_crowd(grid_1, crowded_center)
1284
+
1285
+ save_navigation(session["username"], (session["start_floor"], session["start_xy"]), dest_room)
1286
+ save_navigation_mem((session["start_floor"], session["start_xy"]), dest_room)
1287
+
1288
+ img_path, instructions = navigate(session["start_floor"], session["start_xy"], dest_room, nav_mode_val)
1289
+ return instructions, img_path
1290
+
1291
+
1292
+ def do_get_products(type_name, cat_name, sub_name, session):
1293
+ session = set_session_defaults(session)
1294
+ if not session.get("username"):
1295
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Please login first.</div>"
1296
+ if not sub_name:
1297
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Please select a sub-category.</div>"
1298
+
1299
+ if type_name and cat_name and sub_name:
1300
+ save_preference(session["username"], (type_name, cat_name, sub_name))
1301
+
1302
+ gender = session.get("gender", "other")
1303
+ low = session.get("low", 500)
1304
+ high = session.get("high", 5000)
1305
+ result = recommend_for_user(sub_name, gender, low, high)
1306
+ session["selected_subcategory"] = sub_name
1307
+ return format_products_html(result["products"], result["store"], result["room"])
1308
+
1309
+ # ============================================================
1310
+ # MAIN GRADIO APP
1311
+ # ============================================================
1312
+ with gr.Blocks(
1313
+ theme=gr.themes.Base(primary_hue="cyan", secondary_hue="blue", neutral_hue="slate"),
1314
+ css="""
1315
+ body { background: #0a0a14 !important; }
1316
+ .gradio-container { background: #0a0a14 !important; max-width: 1200px !important; }
1317
+ .tab-nav button { font-family: monospace; letter-spacing: 2px; font-size: 12px; }
1318
+ .panel { background: #111827; border: 1px solid #1e3a5f; border-radius: 12px; padding: 20px; }
1319
+ h1, h2, h3 { font-family: monospace !important; letter-spacing: 3px !important; }
1320
+ footer { display: none !important; }
1321
+ """,
1322
+ title="SmartMall AI System",
1323
+ ) as demo:
1324
+
1325
+ session_state = gr.State({})
1326
+
1327
+ gr.HTML("""
1328
+ <div style="text-align:center;padding:24px 0 8px;font-family:monospace;">
1329
+ <div style="font-size:28px;font-weight:900;letter-spacing:6px;
1330
+ background:linear-gradient(90deg,#00d4ff,#00ff88,#ff4466);
1331
+ -webkit-background-clip:text;-webkit-text-fill-color:transparent">
1332
+ SMART MALL AI
1333
+ </div>
1334
+ <div style="color:#555;font-size:11px;letter-spacing:4px;margin-top:6px">
1335
+ NAVIGATION · RECOMMENDATIONS · ASSISTANT
1336
+ </div>
1337
+ </div>
1338
+ """)
1339
+
1340
+ with gr.Tabs():
1341
+ # ----------------------------------------------------
1342
+ # TAB 1 — AUTH
1343
+ # ----------------------------------------------------
1344
+ with gr.Tab("🔐 LOGIN / SIGNUP"):
1345
+ gr.HTML("<div style='color:#00d4ff;font-family:monospace;font-size:13px;margin-bottom:16px'>Enter your credentials to begin</div>")
1346
+ with gr.Row():
1347
+ with gr.Column(scale=1):
1348
+ auth_username = gr.Textbox(label="Username", placeholder="your_username")
1349
+ auth_password = gr.Textbox(label="Password", type="password", placeholder="••••••••")
1350
+ auth_name = gr.Textbox(label="Full Name (signup only)", placeholder="John Doe")
1351
+ auth_age = gr.Number(label="Age (signup only)", value=25)
1352
+ auth_gender = gr.Dropdown(["male", "female", "other"], label="Gender (signup only)", value="male")
1353
+ auth_budget_l = gr.Number(label="Min Budget EGP (signup only)", value=500)
1354
+ auth_budget_h = gr.Number(label="Max Budget EGP (signup only)", value=5000)
1355
+
1356
+ with gr.Row():
1357
+ signup_btn = gr.Button("SIGNUP", variant="primary")
1358
+ login_btn = gr.Button("LOGIN", variant="secondary")
1359
+ auth_status = gr.HTML()
1360
+
1361
+ def do_signup(username, password, name, age, gender, low, high, session):
1362
+ cursor.execute("SELECT username FROM users WHERE username=?", (username,))
1363
+ if cursor.fetchone():
1364
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Username already exists.</div>", session
1365
+ cursor.execute(
1366
+ "INSERT INTO users VALUES (?,?,?,?,?,?,?)",
1367
+ (username, password, name, int(age), gender, low, high),
1368
+ )
1369
+ conn.commit()
1370
+ session = set_session_defaults(session)
1371
+ session.update({"username": username, "gender": gender, "low": low, "high": high, "is_new": True})
1372
+ return f"<div style='color:#00ff88;font-family:monospace'>✅ Welcome, {name}! Account created.</div>", session
1373
+
1374
+ def do_login(username, password, session):
1375
+ cursor.execute("SELECT * FROM users WHERE username=? AND password=?", (username, password))
1376
+ user = cursor.fetchone()
1377
+ if not user:
1378
+ return "<div style='color:#ff6b6b;font-family:monospace'>❌ Invalid credentials.</div>", session
1379
+ session = set_session_defaults(session)
1380
+ session.update({"username": user[0], "gender": user[4], "low": user[5], "high": user[6], "is_new": False})
1381
+ return f"<div style='color:#00ff88;font-family:monospace'>✅ Welcome back, {user[2]}!</div>", session
1382
+
1383
+ signup_btn.click(do_signup, [auth_username, auth_password, auth_name, auth_age, auth_gender, auth_budget_l, auth_budget_h, session_state], [auth_status, session_state])
1384
+ login_btn.click(do_login, [auth_username, auth_password, session_state], [auth_status, session_state])
1385
+
1386
+ # ----------------------------------------------------
1387
+ # TAB 2 — RECOMMENDATION + NAVIGATION
1388
+ # ----------------------------------------------------
1389
+ with gr.Tab("🏪 SHOP & NAVIGATE"):
1390
+ gr.HTML("<div style='color:#00d4ff;font-family:monospace;font-size:13px;margin-bottom:16px'>Get store recommendations and navigate</div>")
1391
+
1392
+ gr.HTML("<div style='color:#888;font-family:monospace;font-size:11px;margin-bottom:8px'>STEP 0 — SET YOUR LOCATION</div>")
1393
+ with gr.Row():
1394
+ loc_x = gr.Number(label="X Coordinate")
1395
+ loc_y = gr.Number(label="Y Coordinate")
1396
+ loc_floor = gr.Dropdown(["3", "4"], label="Floor", value="3")
1397
+ set_loc_btn = gr.Button("📍 Set Location")
1398
+ loc_status = gr.HTML()
1399
+
1400
+ with gr.Row():
1401
+ with gr.Column(scale=1):
1402
+ gr.HTML("<div style='color:#888;font-family:monospace;font-size:11px;margin-bottom:8px'>STEP 1 — GET RECOMMENDATION</div>")
1403
+ recommend_btn = gr.Button("🎯 Get Store Recommendation", variant="primary")
1404
+ rec_result_html = gr.HTML()
1405
+ rec_agree = gr.Radio(["✅ Yes, navigate me there", "❌ No, I'll choose my own"], label="Accept recommendation?")
1406
+
1407
+ gr.HTML("<div style='color:#888;font-family:monospace;font-size:11px;margin-top:20px;margin-bottom:8px'>STEP 2 — CHOOSE CATEGORY (for navigation if rejected, and for products)</div>")
1408
+ cat_type = gr.Dropdown([t["type"] for t in categories_data], label="Main Category")
1409
+ cat_cat = gr.Dropdown([], label="Category")
1410
+ cat_sub = gr.Dropdown([], label="Sub-Category")
1411
+ nav_mode = gr.Dropdown(["Normal", "Fire", "Crowded", "Special Needs"], label="Navigation Mode", value="Normal")
1412
+ danger_room = gr.Textbox(label="Fire/Crowded Room Number", placeholder="Example: 350")
1413
+ navigate_btn = gr.Button("🗺️ Start Navigation", variant="primary")
1414
+
1415
+ with gr.Column(scale=1):
1416
+ nav_instructions = gr.Textbox(label="Navigation Instructions", lines=10, interactive=False)
1417
+ nav_image = gr.Image(label="Mall Map", type="filepath")
1418
+
1419
+ gr.HTML("<div style='color:#888;font-family:monospace;font-size:11px;margin-top:20px;margin-bottom:8px'>STEP 3 — PRODUCT RECOMMENDATIONS</div>")
1420
+ with gr.Row():
1421
+ prod_type = gr.Dropdown([], label="Category Type")
1422
+ prod_cat = gr.Dropdown([], label="Category")
1423
+ prod_sub = gr.Dropdown([], label="Sub-Category")
1424
+ get_products_btn = gr.Button("🛍️ Get Product Recommendations", variant="primary")
1425
+ products_html = gr.HTML()
1426
+
1427
+ set_loc_btn.click(set_location_ui, [loc_x, loc_y, loc_floor, session_state], [loc_status, session_state])
1428
+ recommend_btn.click(recommend_store_for_session, [session_state], [rec_result_html, session_state])
1429
+ rec_agree.change(handle_agreement, [rec_agree, session_state], [cat_type, cat_cat, cat_sub, prod_type])
1430
+ cat_type.change(update_cat, [cat_type], [cat_cat])
1431
+ cat_cat.change(update_sub, [cat_type, cat_cat], [cat_sub])
1432
+ prod_type.change(update_cat, [prod_type], [prod_cat])
1433
+ prod_cat.change(update_sub, [prod_type, prod_cat], [prod_sub])
1434
+
1435
+ navigate_btn.click(
1436
+ do_navigate,
1437
+ [rec_agree, cat_type, cat_cat, cat_sub, nav_mode, danger_room, session_state],
1438
+ [nav_instructions, nav_image],
1439
+ )
1440
+
1441
+ get_products_btn.click(do_get_products, [prod_type, prod_cat, prod_sub, session_state], [products_html])
1442
+
1443
+ def init_dropdowns():
1444
+ types = [t["type"] for t in categories_data]
1445
+ return gr.update(choices=types), gr.update(choices=types)
1446
+
1447
+ demo.load(init_dropdowns, [], [prod_type, cat_type])
1448
+
1449
+ # ----------------------------------------------------
1450
+ # TAB 3 — CHATBOT
1451
+ # ----------------------------------------------------
1452
+ with gr.Tab("🤖 AI ASSISTANT"):
1453
+ gr.HTML("<div style='color:#00d4ff;font-family:monospace;font-size:13px;margin-bottom:16px'>Chat with the mall AI assistant</div>")
1454
+
1455
+ chatbot_widget = gr.Chatbot(
1456
+ label="SmartMall Assistant",
1457
+ height=480,
1458
+ value=[("", "👋 Welcome to SmartMall! I can help you navigate, find products, or answer any questions. How can I help?")],
1459
+ )
1460
+
1461
+ with gr.Row():
1462
+ chat_msg = gr.Textbox(placeholder="Ask me anything about the mall...", label="Message", scale=4)
1463
+ chat_mic = gr.Audio(source="microphone", type="filepath", label="🎤", scale=1)
1464
+
1465
+ chat_audio_out = gr.Audio(label="Voice Response")
1466
+ chat_img_out = gr.Image(label="Navigation Map", type="filepath")
1467
+
1468
+ def chat_respond(msg, history, session):
1469
+ if not msg.strip():
1470
+ return history, None, None, session
1471
+ history, img, audio, session = chatbot_respond(msg, history, session)
1472
+ return history, img, audio, session
1473
+
1474
+ def voice_chat(audio, history, session):
1475
+ text = speech_to_text(audio)
1476
+ if not text:
1477
+ history.append(("🎤 [voice]", "Sorry, I couldn't understand the audio."))
1478
+ return history, None, None, session
1479
+ return chat_respond(text, history, session)
1480
+
1481
+ chat_msg.submit(chat_respond, [chat_msg, chatbot_widget, session_state], [chatbot_widget, chat_img_out, chat_audio_out, session_state])
1482
+ chat_mic.change(voice_chat, [chat_mic, chatbot_widget, session_state], [chatbot_widget, chat_img_out, chat_audio_out, session_state])
1483
+
1484
+ if __name__ == "__main__":
1485
+ demo.launch()
categories.json ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "type": "Electronics",
4
+ "categories": [
5
+ {
6
+ "category": "Audio",
7
+ "subCategories": [
8
+ "Bluetooth Headphones",
9
+ "Wired Headphones",
10
+ "True Wireless Earbuds",
11
+ "Bluetooth Speakers",
12
+ "Soundbars",
13
+ "Home Theatres",
14
+ "TV Streaming Device"
15
+ ]
16
+ },
17
+ {
18
+ "category": "Cameras",
19
+ "subCategories": [
20
+ "DSLR & Mirrorless",
21
+ "Sports & action",
22
+ "Point & Shoot",
23
+ "Instant Cameras",
24
+ "Camcorders",
25
+ "Camera tripods",
26
+ "Camera Lenses",
27
+ "Drone",
28
+ "Flashes",
29
+ "Gimbals",
30
+ "Binoculars"
31
+ ]
32
+ },
33
+ {
34
+ "category": "Computer Peripherals",
35
+ "subCategories": [
36
+ "Printers",
37
+ "Monitors",
38
+ "Projectors",
39
+ "Portable projectors",
40
+ "Ink Catridges",
41
+ "Ink Bottles",
42
+ "Receipt Printers",
43
+ "Lamination Machines",
44
+ "Note Counting Machines",
45
+ "Barcode scanners",
46
+ "Currency Detectors"
47
+ ]
48
+ },
49
+ {
50
+ "category": "Gaming",
51
+ "subCategories": [
52
+ "Gaming Consoles",
53
+ "Gaming Mouse",
54
+ "Gaming Keyboards",
55
+ "Gamepads",
56
+ "Games",
57
+ "Gaming Mousepads",
58
+ "Controllers",
59
+ "Gaming Components"
60
+ ]
61
+ },
62
+ {
63
+ "category": "Health & Personal Care",
64
+ "subCategories": [
65
+ "Trimmers",
66
+ "Hair Straighteners",
67
+ "Hair Dryers",
68
+ "Epilators",
69
+ "Glucometers And Accessories",
70
+ "Blood Pressure Monitors",
71
+ "Digital Thermometers",
72
+ "Weighing Scales",
73
+ "Massagers",
74
+ "Nebulizers",
75
+ "Vaporizers"
76
+ ]
77
+ },
78
+ {
79
+ "category": "Laptop Accessories",
80
+ "subCategories": [
81
+ "Mouse",
82
+ "Laptop Keyboards",
83
+ "Router",
84
+ "Data Cards",
85
+ "UPS",
86
+ "USB Gadgets",
87
+ "Security Software",
88
+ "Laptop Battery",
89
+ "Laptop Adapter",
90
+ "Wireless USB Adapter",
91
+ "Processor"
92
+ ]
93
+ },
94
+ {
95
+ "category": "Laptop & Desktop",
96
+ "subCategories": [
97
+ "Laptops",
98
+ "Gaming Laptops",
99
+ "Desktop PCs",
100
+ "All in One PCs",
101
+ "Mini PCs",
102
+ "Tower PCs",
103
+ "PC Finder"
104
+ ]
105
+ },
106
+ {
107
+ "category": "Smart Home Automation",
108
+ "subCategories": [
109
+ "Smart Assistants",
110
+ "Smart Lights",
111
+ "Smart Cameras",
112
+ "Smart Switches",
113
+ "Smart Door Locks",
114
+ "Sensors & Alarms"
115
+ ]
116
+ },
117
+ {
118
+ "category": "Smart Wearables",
119
+ "subCategories": [
120
+ "Smart Watches",
121
+ "Smart Bands",
122
+ "Smart Glasses"
123
+ ]
124
+ },
125
+ {
126
+ "category": "Storage",
127
+ "subCategories": [
128
+ "Mobile Memory Card",
129
+ "Computer Storage Pendrive",
130
+ "Mobile Storage Pendrive",
131
+ "External Hard Drive",
132
+ "Internal Hard Drive"
133
+ ]
134
+ },
135
+ {
136
+ "category": "Tabelts",
137
+ "subCategories": ["Wifi", "Wifi + Cellular"]
138
+ },
139
+ {
140
+ "category": "Mobiles",
141
+ "subCategories": [
142
+ "Mi mobiles",
143
+ "Realme mobiles",
144
+ "Samsung mobiles",
145
+ "Infinix mobiles",
146
+ "OPPO mobiles",
147
+ "Apple mobiles",
148
+ "Vivo mobiles",
149
+ "Honor mobiles",
150
+ "Asus mobiles"
151
+ ]
152
+ }
153
+ ]
154
+ },
155
+ {
156
+ "type": "Furniture",
157
+ "categories": [
158
+ {
159
+ "category": "Bedroom",
160
+ "subCategories": [
161
+ "Beds",
162
+ "Mattresses",
163
+ "Wardrobes",
164
+ "Bedding",
165
+ "Side tables",
166
+ "Mirrors",
167
+ "Pillows"
168
+ ]
169
+ },
170
+ {
171
+ "category": "Living Room",
172
+ "subCategories": [
173
+ "Sofa",
174
+ "Coffee table",
175
+ "TV Stand",
176
+ "cabinets",
177
+ "Curtains",
178
+ "Lighting"
179
+ ]
180
+ },
181
+ {
182
+ "category": "Study/Office",
183
+ "subCategories": [
184
+ "Desk",
185
+ "Laptop tables",
186
+ "Office chair",
187
+ "Drawer units",
188
+ "Shelves"
189
+ ]
190
+ },
191
+ {
192
+ "category": "Kitchen",
193
+ "subCategories": [
194
+ "Kitchen chairs",
195
+ "Kitchen trolleys",
196
+ "Kitchen appliances",
197
+ "Kitchen sinks",
198
+ "Kitchen taps"
199
+ ]
200
+ },
201
+ {
202
+ "category": "Dining Room",
203
+ "subCategories": [
204
+ "Dining tables",
205
+ "Dining chairs",
206
+ "Dining sets",
207
+ "Cutlery units"
208
+ ]
209
+ },
210
+ {
211
+ "category": "Bathroom",
212
+ "subCategories": [
213
+ "Vanities",
214
+ "Showers",
215
+ "Bathroom storage",
216
+ "Taps",
217
+ "Sinks"
218
+ ]
219
+ }
220
+ ]
221
+ },
222
+ {
223
+ "type": "Household",
224
+ "categories": [
225
+ {
226
+ "category": "Furnishings",
227
+ "subCategories": [
228
+ "Bed Linens",
229
+ "Bedsheets",
230
+ "Blankets",
231
+ "Curtains",
232
+ "Bath linen",
233
+ "Pillows",
234
+ "Kitchen linen"
235
+ ]
236
+ },
237
+ {
238
+ "category": "Kitchen and Dining",
239
+ "subCategories": [
240
+ "Cookware",
241
+ "Lunchboxes, bottles",
242
+ "Knives,Choppers",
243
+ "Gas stove",
244
+ "Dinnerware",
245
+ "Kitchen storage",
246
+ "Bakeware"
247
+ ]
248
+ },
249
+ {
250
+ "category": "Home Decor",
251
+ "subCategories": [
252
+ "Lighting",
253
+ "Wallpapers",
254
+ "Paintings and posters",
255
+ "Clocks",
256
+ "Decoratives",
257
+ "Vases",
258
+ "Home fragrances",
259
+ "Photo frames",
260
+ "Wall decor"
261
+ ]
262
+ },
263
+ {
264
+ "category": "Tools and utility",
265
+ "subCategories": [
266
+ "Hand tools",
267
+ "Power tools",
268
+ "Measuring tools",
269
+ "Home storage",
270
+ "Umbrellas",
271
+ "Lock and security",
272
+ "Paint equipment"
273
+ ]
274
+ },
275
+ {
276
+ "category": "Lighting and Electricals",
277
+ "subCategories": [
278
+ "Bulbs",
279
+ "Emergency lights",
280
+ "Torches",
281
+ "Tube lights",
282
+ "Extension cords",
283
+ "Batteries",
284
+ "Solar lights"
285
+ ]
286
+ },
287
+ {
288
+ "category": "Cleaning and Bath",
289
+ "subCategories": [
290
+ "Mops",
291
+ "Cleaning Supplies",
292
+ "Air freshners",
293
+ "Household supplies",
294
+ "Liquid Detergents",
295
+ "Taps and Faucets",
296
+ "Bathroom shelves",
297
+ "Shower heads",
298
+ "Bathroom Accesories"
299
+ ]
300
+ },
301
+ {
302
+ "category": "Pet and Gardening",
303
+ "subCategories": [
304
+ "Plant seeds",
305
+ "Manure",
306
+ "Pots",
307
+ "Garden tools set",
308
+ "Watering equipment",
309
+ "Dog essentials",
310
+ "Cat essentials",
311
+ "Fish and aquatic",
312
+ "Pet hygiene",
313
+ "Pet toys"
314
+ ]
315
+ }
316
+ ]
317
+ }
318
+ ]
floor_0_grid.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c5371819d353037135d8ec5f47937c8857c7e02c17e3c4eda3e0bdb12dc8876d
3
+ size 42581
mall.db ADDED
Binary file (24.6 kB). View file
 
navigation_memory.json ADDED
File without changes
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio==3.39.0
2
+ gradio-client==0.3.0
3
+ numpy
4
+ scipy
5
+ matplotlib
6
+ openai
7
+ gTTS
8
+ SpeechRecognition
9
+ pydub
sort_data.json ADDED
The diff for this file is too large to render. See raw diff