jackmedda commited on
Commit ·
3dad338
1
Parent(s): fc91734
Handled duplicates, replaced description with category
Browse files- html_items.py +10 -9
- main.py +20 -19
- utils/poi_search_utils.py +20 -18
html_items.py
CHANGED
|
@@ -110,7 +110,7 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 110 |
# Darker text colors for selected state
|
| 111 |
title_color = "#2c5530"
|
| 112 |
location_color = "#4a5c4d"
|
| 113 |
-
|
| 114 |
else:
|
| 115 |
border_color = "#ddd"
|
| 116 |
background_color = "white"
|
|
@@ -119,7 +119,7 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 119 |
content_bg = "white"
|
| 120 |
title_color = "#333"
|
| 121 |
location_color = "#666"
|
| 122 |
-
|
| 123 |
|
| 124 |
# Create compact sensory features display
|
| 125 |
sensory_html = ""
|
|
@@ -147,7 +147,8 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 147 |
"""
|
| 148 |
|
| 149 |
# Escape single quotes in POI name for JavaScript
|
| 150 |
-
|
|
|
|
| 151 |
|
| 152 |
html = f"""
|
| 153 |
<div class="poi-card" data-poi-name="{poi["name:token_seq"]}"
|
|
@@ -161,7 +162,7 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 161 |
position: relative;
|
| 162 |
overflow: hidden;
|
| 163 |
box-shadow: 0 2px 8px rgba(0,0,0,0.1);"
|
| 164 |
-
onclick="document.getElementById('poi-btn-{
|
| 165 |
onmouseover="this.style.transform='scale(1.02)'; this.style.boxShadow='0 4px 12px rgba(0,0,0,0.15)';"
|
| 166 |
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)';">
|
| 167 |
|
|
@@ -185,8 +186,8 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 185 |
<p style="margin: 0 0 8px 0; color: {location_color}; font-size: 14px; font-weight: 500;">
|
| 186 |
📍 {poi["address:token_seq"]}
|
| 187 |
</p>
|
| 188 |
-
<p style="margin: 0 0 12px 0; color: {
|
| 189 |
-
{
|
| 190 |
</p>
|
| 191 |
|
| 192 |
{sensory_html}
|
|
@@ -203,14 +204,14 @@ def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
|
|
| 203 |
return html
|
| 204 |
|
| 205 |
|
| 206 |
-
def create_poi_cards_list(pois: list,
|
| 207 |
if not pois:
|
| 208 |
return "<div style='text-align:center; color:#666; margin: 40px; padding: 40px; background: #f8f9fa; border-radius: 12px;'><h3>🔍 Nessun punto di interesse trovato</h3><p>Prova con termini di ricerca diversi</p></div>"
|
| 209 |
|
| 210 |
# Genera HTML per i POI filtrati
|
| 211 |
poi_cards_html = []
|
| 212 |
for poi in pois:
|
| 213 |
-
is_selected = poi.get("name:token_seq") in
|
| 214 |
card_html = create_poi_card_selectable(poi, is_selected)
|
| 215 |
poi_cards_html.append(card_html)
|
| 216 |
|
|
@@ -219,7 +220,7 @@ def create_poi_cards_list(pois: list, selected_names: set) -> str:
|
|
| 219 |
{"".join(poi_cards_html)}
|
| 220 |
</div>
|
| 221 |
<div style='text-align: center; color: black; margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 8px;'>
|
| 222 |
-
<strong style='color: black;'>Trovati {len(pois)} punti di interesse</strong> • <span style='color: #7FB77E;'>{len(
|
| 223 |
</div>
|
| 224 |
"""
|
| 225 |
|
|
|
|
| 110 |
# Darker text colors for selected state
|
| 111 |
title_color = "#2c5530"
|
| 112 |
location_color = "#4a5c4d"
|
| 113 |
+
category_color = "#5a6b5d"
|
| 114 |
else:
|
| 115 |
border_color = "#ddd"
|
| 116 |
background_color = "white"
|
|
|
|
| 119 |
content_bg = "white"
|
| 120 |
title_color = "#333"
|
| 121 |
location_color = "#666"
|
| 122 |
+
category_color = "#777"
|
| 123 |
|
| 124 |
# Create compact sensory features display
|
| 125 |
sensory_html = ""
|
|
|
|
| 147 |
"""
|
| 148 |
|
| 149 |
# Escape single quotes in POI name for JavaScript
|
| 150 |
+
poi_id_escaped = poi["poi_id:token"].replace(" ", "-").replace("'", "").replace('"', "")
|
| 151 |
+
poi_category = ' - '.join(poi["category:token_seq"]).replace('_', ' ')
|
| 152 |
|
| 153 |
html = f"""
|
| 154 |
<div class="poi-card" data-poi-name="{poi["name:token_seq"]}"
|
|
|
|
| 162 |
position: relative;
|
| 163 |
overflow: hidden;
|
| 164 |
box-shadow: 0 2px 8px rgba(0,0,0,0.1);"
|
| 165 |
+
onclick="document.getElementById('poi-btn-{poi_id_escaped}').click();"
|
| 166 |
onmouseover="this.style.transform='scale(1.02)'; this.style.boxShadow='0 4px 12px rgba(0,0,0,0.15)';"
|
| 167 |
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)';">
|
| 168 |
|
|
|
|
| 186 |
<p style="margin: 0 0 8px 0; color: {location_color}; font-size: 14px; font-weight: 500;">
|
| 187 |
📍 {poi["address:token_seq"]}
|
| 188 |
</p>
|
| 189 |
+
<p style="margin: 0 0 12px 0; color: {category_color}; font-size: 14px; line-height: 1.4;">
|
| 190 |
+
{'🏷️ ' + poi_category if poi_category else ''}
|
| 191 |
</p>
|
| 192 |
|
| 193 |
{sensory_html}
|
|
|
|
| 204 |
return html
|
| 205 |
|
| 206 |
|
| 207 |
+
def create_poi_cards_list(pois: list, selected_pois: set) -> str:
|
| 208 |
if not pois:
|
| 209 |
return "<div style='text-align:center; color:#666; margin: 40px; padding: 40px; background: #f8f9fa; border-radius: 12px;'><h3>🔍 Nessun punto di interesse trovato</h3><p>Prova con termini di ricerca diversi</p></div>"
|
| 210 |
|
| 211 |
# Genera HTML per i POI filtrati
|
| 212 |
poi_cards_html = []
|
| 213 |
for poi in pois:
|
| 214 |
+
is_selected = (poi.get("poi_id:token"), poi.get("name:token_seq")) in selected_pois
|
| 215 |
card_html = create_poi_card_selectable(poi, is_selected)
|
| 216 |
poi_cards_html.append(card_html)
|
| 217 |
|
|
|
|
| 220 |
{"".join(poi_cards_html)}
|
| 221 |
</div>
|
| 222 |
<div style='text-align: center; color: black; margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 8px;'>
|
| 223 |
+
<strong style='color: black;'>Trovati {len(pois)} punti di interesse</strong> • <span style='color: #7FB77E;'>{len(selected_pois)} selezionati</span>
|
| 224 |
</div>
|
| 225 |
"""
|
| 226 |
|
main.py
CHANGED
|
@@ -169,7 +169,7 @@ def create_main_app(self):
|
|
| 169 |
# Stati globali dell'applicazione
|
| 170 |
app_user_id = gr.State("")
|
| 171 |
current_page = gr.State("login") # "login", "poi_selection", "sensory_config", "recommendations"
|
| 172 |
-
|
| 173 |
recommendations_state = gr.State([])
|
| 174 |
selected_poi_index = gr.State(-1)
|
| 175 |
completed_survey_pois = gr.State(set())
|
|
@@ -250,14 +250,15 @@ def create_main_app(self):
|
|
| 250 |
# Create hidden buttons for each POI
|
| 251 |
poi_buttons = {}
|
| 252 |
for poi in pois_list:
|
|
|
|
| 253 |
poi_name = poi["name:token_seq"]
|
| 254 |
-
|
| 255 |
-
|
| 256 |
)
|
| 257 |
button = gr.Button(
|
| 258 |
-
poi_name, visible=False, elem_id=f"poi-btn-{
|
| 259 |
)
|
| 260 |
-
poi_buttons[poi_name] = button
|
| 261 |
|
| 262 |
with gr.Column(
|
| 263 |
visible=False, elem_classes="sensory-config-main-container"
|
|
@@ -442,19 +443,19 @@ def create_main_app(self):
|
|
| 442 |
login_status, # login_status
|
| 443 |
)
|
| 444 |
|
| 445 |
-
def handle_poi_click(poi_name,
|
| 446 |
"""Handle POI click - toggle selection"""
|
| 447 |
print(f"🔥 POI Button clicked: {poi_name}")
|
| 448 |
-
return toggle_poi_selection(poi_name,
|
| 449 |
-
|
| 450 |
-
def handle_proceed_to_sensory_config(user_id,
|
| 451 |
"""Handle proceeding to next step"""
|
| 452 |
-
if not
|
| 453 |
gr.Warning("⚠️ Seleziona la card di almeno un Punto di Interesse per continuare!")
|
| 454 |
return
|
| 455 |
|
| 456 |
-
update_user_selected_pois(user_id,
|
| 457 |
-
gr.Info(f"✅ Procedendo con {len(
|
| 458 |
return navigate_to_sensory_config()
|
| 459 |
|
| 460 |
def navigate_to_poi_selection():
|
|
@@ -537,28 +538,28 @@ def create_main_app(self):
|
|
| 537 |
# Dynamic search - triggers on every keystroke
|
| 538 |
search_input.change(
|
| 539 |
fn=search_pois,
|
| 540 |
-
inputs=[search_input,
|
| 541 |
outputs=[poi_display],
|
| 542 |
)
|
| 543 |
|
| 544 |
# Set up click handlers for each POI button
|
| 545 |
-
for poi_name, button in poi_buttons.items():
|
| 546 |
button.click(
|
| 547 |
-
fn=partial(handle_poi_click, poi_name),
|
| 548 |
-
inputs=[
|
| 549 |
-
outputs=[
|
| 550 |
)
|
| 551 |
|
| 552 |
# Clear all selections
|
| 553 |
clear_button.click(
|
| 554 |
fn=clear_all_selections,
|
| 555 |
inputs=[search_input],
|
| 556 |
-
outputs=[
|
| 557 |
)
|
| 558 |
|
| 559 |
continue_to_sensory_btn.click(
|
| 560 |
fn=handle_proceed_to_sensory_config,
|
| 561 |
-
inputs=[app_user_id,
|
| 562 |
outputs=[
|
| 563 |
login_page,
|
| 564 |
poi_selection_page,
|
|
|
|
| 169 |
# Stati globali dell'applicazione
|
| 170 |
app_user_id = gr.State("")
|
| 171 |
current_page = gr.State("login") # "login", "poi_selection", "sensory_config", "recommendations"
|
| 172 |
+
current_selected_pois = gr.State(set())
|
| 173 |
recommendations_state = gr.State([])
|
| 174 |
selected_poi_index = gr.State(-1)
|
| 175 |
completed_survey_pois = gr.State(set())
|
|
|
|
| 250 |
# Create hidden buttons for each POI
|
| 251 |
poi_buttons = {}
|
| 252 |
for poi in pois_list:
|
| 253 |
+
poi_id = poi["poi_id:token"]
|
| 254 |
poi_name = poi["name:token_seq"]
|
| 255 |
+
safe_id = (
|
| 256 |
+
poi_id.replace(" ", "-").replace("'", "").replace('"', "")
|
| 257 |
)
|
| 258 |
button = gr.Button(
|
| 259 |
+
poi_name, visible=False, elem_id=f"poi-btn-{safe_id}"
|
| 260 |
)
|
| 261 |
+
poi_buttons[(poi_id, poi_name)] = button
|
| 262 |
|
| 263 |
with gr.Column(
|
| 264 |
visible=False, elem_classes="sensory-config-main-container"
|
|
|
|
| 443 |
login_status, # login_status
|
| 444 |
)
|
| 445 |
|
| 446 |
+
def handle_poi_click(poi_id, poi_name, selected_pois, query):
|
| 447 |
"""Handle POI click - toggle selection"""
|
| 448 |
print(f"🔥 POI Button clicked: {poi_name}")
|
| 449 |
+
return toggle_poi_selection(poi_id, poi_name, selected_pois, query)
|
| 450 |
+
|
| 451 |
+
def handle_proceed_to_sensory_config(user_id, selected_pois):
|
| 452 |
"""Handle proceeding to next step"""
|
| 453 |
+
if not selected_pois:
|
| 454 |
gr.Warning("⚠️ Seleziona la card di almeno un Punto di Interesse per continuare!")
|
| 455 |
return
|
| 456 |
|
| 457 |
+
update_user_selected_pois(user_id, selected_pois)
|
| 458 |
+
gr.Info(f"✅ Procedendo con {len(selected_pois)} Punti di Interesse selezionati")
|
| 459 |
return navigate_to_sensory_config()
|
| 460 |
|
| 461 |
def navigate_to_poi_selection():
|
|
|
|
| 538 |
# Dynamic search - triggers on every keystroke
|
| 539 |
search_input.change(
|
| 540 |
fn=search_pois,
|
| 541 |
+
inputs=[search_input, current_selected_pois],
|
| 542 |
outputs=[poi_display],
|
| 543 |
)
|
| 544 |
|
| 545 |
# Set up click handlers for each POI button
|
| 546 |
+
for (poi_id, poi_name), button in poi_buttons.items():
|
| 547 |
button.click(
|
| 548 |
+
fn=partial(handle_poi_click, poi_id, poi_name),
|
| 549 |
+
inputs=[current_selected_pois, search_input],
|
| 550 |
+
outputs=[current_selected_pois, poi_display, selection_summary],
|
| 551 |
)
|
| 552 |
|
| 553 |
# Clear all selections
|
| 554 |
clear_button.click(
|
| 555 |
fn=clear_all_selections,
|
| 556 |
inputs=[search_input],
|
| 557 |
+
outputs=[current_selected_pois, poi_display, selection_summary],
|
| 558 |
)
|
| 559 |
|
| 560 |
continue_to_sensory_btn.click(
|
| 561 |
fn=handle_proceed_to_sensory_config,
|
| 562 |
+
inputs=[app_user_id, current_selected_pois],
|
| 563 |
outputs=[
|
| 564 |
login_page,
|
| 565 |
poi_selection_page,
|
utils/poi_search_utils.py
CHANGED
|
@@ -10,9 +10,11 @@ try:
|
|
| 10 |
with open(os.path.join(RESOURCE_DIR, "turin_autism.item.json"), "r") as f:
|
| 11 |
pois_list = json.load(f)
|
| 12 |
for poi in pois_list:
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
except FileNotFoundError:
|
| 17 |
with open(os.path.join(RESOURCE_DIR, "mock_data.json"), "r") as f:
|
| 18 |
LOADED_MOCK_POIS = json.load(f)
|
|
@@ -20,7 +22,7 @@ except FileNotFoundError:
|
|
| 20 |
check_image_url(pois_list)
|
| 21 |
|
| 22 |
|
| 23 |
-
def search_pois(search_query: str,
|
| 24 |
"""Cerca POI basandosi sulla query di ricerca e restituisce HTML"""
|
| 25 |
|
| 26 |
# Filtra i POI basandosi sulla query
|
|
@@ -40,26 +42,26 @@ def search_pois(search_query: str, selected_names: set) -> str:
|
|
| 40 |
):
|
| 41 |
filtered_pois.append(poi)
|
| 42 |
|
| 43 |
-
return create_poi_cards_list(filtered_pois,
|
| 44 |
|
| 45 |
|
| 46 |
def toggle_poi_selection(
|
| 47 |
-
poi_name: str,
|
| 48 |
) -> tuple[set, str, str]:
|
| 49 |
"""Alterna la selezione di un POI e aggiorna il display"""
|
| 50 |
|
| 51 |
# Copia il set per evitare modifiche dirette
|
| 52 |
-
new_selected =
|
| 53 |
|
| 54 |
-
print(f"DEBUG: Toggling POI '{poi_name}'")
|
| 55 |
-
print(f"DEBUG: Current selected: {
|
| 56 |
|
| 57 |
-
if poi_name in new_selected:
|
| 58 |
-
new_selected.remove(poi_name)
|
| 59 |
-
print(f"DEBUG: Removed '{poi_name}' from selection")
|
| 60 |
else:
|
| 61 |
-
new_selected.add(poi_name)
|
| 62 |
-
print(f"DEBUG: Added '{poi_name}' to selection")
|
| 63 |
|
| 64 |
print(f"DEBUG: New selected: {new_selected}")
|
| 65 |
|
|
@@ -73,13 +75,13 @@ def toggle_poi_selection(
|
|
| 73 |
return new_selected, updated_html, summary
|
| 74 |
|
| 75 |
|
| 76 |
-
def get_selection_summary(
|
| 77 |
"""Restituisce un riassunto dei POI selezionati"""
|
| 78 |
-
if not
|
| 79 |
return "**Punti di Interesse selezionati: 0** - Seleziona i luoghi di tuo interesse cliccando sulle card"
|
| 80 |
|
| 81 |
-
names_list =
|
| 82 |
-
return f"**Punti di Interesse selezionati: {len(
|
| 83 |
|
| 84 |
|
| 85 |
def clear_all_selections(current_query: str) -> tuple[set, str, str]:
|
|
|
|
| 10 |
with open(os.path.join(RESOURCE_DIR, "turin_autism.item.json"), "r") as f:
|
| 11 |
pois_list = json.load(f)
|
| 12 |
for poi in pois_list:
|
| 13 |
+
for col in ["description:token_seq", "address:token_seq", "coordinates:float_seq"]:
|
| 14 |
+
if poi[col] is None:
|
| 15 |
+
poi[col] = ""
|
| 16 |
+
if poi["category:token_seq"] is None:
|
| 17 |
+
poi["category:token_seq"] = [""]
|
| 18 |
except FileNotFoundError:
|
| 19 |
with open(os.path.join(RESOURCE_DIR, "mock_data.json"), "r") as f:
|
| 20 |
LOADED_MOCK_POIS = json.load(f)
|
|
|
|
| 22 |
check_image_url(pois_list)
|
| 23 |
|
| 24 |
|
| 25 |
+
def search_pois(search_query: str, selected_pois: set) -> str:
|
| 26 |
"""Cerca POI basandosi sulla query di ricerca e restituisce HTML"""
|
| 27 |
|
| 28 |
# Filtra i POI basandosi sulla query
|
|
|
|
| 42 |
):
|
| 43 |
filtered_pois.append(poi)
|
| 44 |
|
| 45 |
+
return create_poi_cards_list(filtered_pois, selected_pois)
|
| 46 |
|
| 47 |
|
| 48 |
def toggle_poi_selection(
|
| 49 |
+
poi_id, poi_name: str, selected_pois: set, current_query: str
|
| 50 |
) -> tuple[set, str, str]:
|
| 51 |
"""Alterna la selezione di un POI e aggiorna il display"""
|
| 52 |
|
| 53 |
# Copia il set per evitare modifiche dirette
|
| 54 |
+
new_selected = selected_pois.copy()
|
| 55 |
|
| 56 |
+
print(f"DEBUG: Toggling POI '{poi_name} ({poi_id})' selection")
|
| 57 |
+
print(f"DEBUG: Current selected: {selected_pois}")
|
| 58 |
|
| 59 |
+
if (poi_id, poi_name) in new_selected:
|
| 60 |
+
new_selected.remove((poi_id, poi_name))
|
| 61 |
+
print(f"DEBUG: Removed '{poi_name} ({poi_id})' from selection")
|
| 62 |
else:
|
| 63 |
+
new_selected.add((poi_id, poi_name))
|
| 64 |
+
print(f"DEBUG: Added '{poi_name} ({poi_id})' to selection")
|
| 65 |
|
| 66 |
print(f"DEBUG: New selected: {new_selected}")
|
| 67 |
|
|
|
|
| 75 |
return new_selected, updated_html, summary
|
| 76 |
|
| 77 |
|
| 78 |
+
def get_selection_summary(selected_pois: set) -> str:
|
| 79 |
"""Restituisce un riassunto dei POI selezionati"""
|
| 80 |
+
if not selected_pois:
|
| 81 |
return "**Punti di Interesse selezionati: 0** - Seleziona i luoghi di tuo interesse cliccando sulle card"
|
| 82 |
|
| 83 |
+
names_list = [name for _, name in selected_pois]
|
| 84 |
+
return f"**Punti di Interesse selezionati: {len(names_list)}** => {' - '.join(names_list[:3])}{' e altri...' if len(names_list) > 3 else ''}"
|
| 85 |
|
| 86 |
|
| 87 |
def clear_all_selections(current_query: str) -> tuple[set, str, str]:
|