jackmedda
Add category to recommendations
037d8ad
import json
import os
from utils.utils import get_aversion_color, get_qualitative_aversion
RESOURCE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
with open(os.path.join(RESOURCE_DIR, "aversions.json"), "r") as f:
aversions_data = json.load(f)
AVERSIONS = {item["Name"]: item for item in aversions_data}
def create_poi_html(poi: dict, user_id: str) -> str:
"""Create HTML representation of a POI with sensory features"""
html = f"""
<div style="border: 1px solid #ddd; padding: 15px; border-radius: 10px; margin-bottom: 20px;">
<div style="display: flex; flex-direction: row; gap: 20px;">
<div style="flex: 0 0 300px;">
<img src="{poi["image_url:token_seq"]}" style="width: 100%; height: auto; border-radius: 8px; object-fit: cover;" alt="{poi["name:token_seq"]}">
</div>
<div style="flex: 1;">
<h3 style="margin-top: 0;">{poi["name:token_seq"]}</h3>
<p><strong>Descrizione:</strong> {poi["description:token_seq"]}</p>
<p><strong>Luogo:</strong> {poi["address:token_seq"]}</p>
<div style="margin: 15px 0;">
<h4>Caratteristiche Sensoriali:</h4>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #4a4a4a; color: white;">
<th style="text-align: left; padding: 8px; border: 1px solid #ddd; width: 35%;">Caratteristica</th>
<th style="text-align: center; padding: 8px; border: 1px solid #ddd; width: 65%;">Livello (1-5)</th>
</tr>
</thead>
<tbody>
"""
for feature, level in poi["sensory_features:token"].items():
icon = AVERSIONS[feature]["Icon"]
bar_color = get_aversion_color(level, AVERSIONS[feature]["Type"])
html += f"""
<tr>
<td style="text-align: left; padding: 8px; border: 1px solid #ddd;">
<div style="display: flex; align-items: center;">
<span style="font-size: 18px; margin-right: 8px;">{icon}</span>
{feature}
</div>
</td>
<td style="text-align: left; padding: 8px; border: 1px solid #ddd;">
<div style="position: relative; background-color: #f0f0f0; width: 100%; height: 24px; border-radius: 12px;">
<div style="position: absolute; background-color: {bar_color}; width: {level * 20}%; height: 100%; border-radius: 12px; display: flex; align-items: center;">
<span style="position: absolute; left: 50%; color: 'black'; font-weight: bold; text-shadow: 0px 0px 2px black;">{level}</span>
</div>
<div style="position: absolute; width: 100%; height: 100%; display: flex; justify-content: space-between; align-items: center; padding: 0 10px; box-sizing: border-box; pointer-events: none;">
<span style="font-size: 10px; opacity: 0.9; color: black">Basso</span>
<span style="font-size: 10px; opacity: 0.9; color: black">Alto</span>
</div>
</div>
</td>
</tr>
"""
html += f""" </tbody>
</table>
<div style="margin: 15px 0; padding: 10px; background-color: #3a3a3a; color: white; border-left: 5px solid #7FB77E; border-radius: 5px;" --explanation_type="{poi.get("explanation_type", -1)}">
<h3>Spiegazione della raccomandazione: ti suggeriamo questo luogo perché ...</h3>
<p>{poi["explanation"]}</p>
</div>
</div>
</div>
</div>
</div>
"""
return html
def create_recommendation_item(rec, index, selected=False) -> str:
"""Create HTML for a single recommendation item"""
poi_category = ' - '.join(rec["category:token_seq"]).replace('_', ' ')
return f"""
<div onclick="document.getElementById('select-button-{index}').click();"
style="display: flex; align-items: center; padding: 10px; margin-bottom: 10px; border: 1px solid #ddd;
border-radius: 10px; cursor: pointer; transition: background-color 0.3s; background-color: {'#888888' if selected else 'transparent'};"
onmouseover="this.style.backgroundColor='{'#888888' if selected else '#393939'}';"
onmouseout="this.style.backgroundColor='{'#888888' if selected else 'transparent'}';">
<div style="width: 80px; height: 80px; overflow: hidden; border-radius: 8px; margin-right: 15px;">
<img src="{rec["image_url:token_seq"]}" style="width: 100%; height: 100%; object-fit: cover;" alt="{rec["name:token_seq"]}">
</div>
<div>
<h3 style="margin: 0 0 5px 0;">{rec["name:token_seq"]}</h3>
<p style="margin: 0 0 5px 0; font-size: 0.9em;">{rec["description:token_seq"] or ''}</p>
<p style="margin: 0 0 5px 0; font-size: 0.93m; line-height: 1.4;">{'🏷️ ' + poi_category if poi_category else ''}</p>
</div>
<div style="margin-left: 50px; font-size: 40px; color: white; font-weight: bold; z-index: 10; text-shadow: 0 0 4px white;">
{"✓" if selected else ""}
</div>
</div>
"""
def create_poi_card_selectable(poi: dict, is_selected: bool = False) -> str:
"""Create HTML representation of a selectable POI card for search interface"""
# Enhanced visual feedback based on selection state
if is_selected:
border_color = "#30892F"
background_color = "#f0f8f0" # Slightly darker green tint
tick_icon = "✓"
# Dark overlay for selected state
overlay_style = "background-color: rgba(0, 0, 0, 0.15);"
# Darker content background
content_bg = "#e8f5e8"
# Darker text colors for selected state
title_color = "#2c5530"
location_color = "#4a5c4d"
category_color = "#5a6b5d"
else:
border_color = "#ddd"
background_color = "white"
tick_icon = ""
overlay_style = ""
content_bg = "white"
title_color = "#333"
location_color = "#666"
category_color = "#777"
# Create compact sensory features display
sensory_html = ""
if poi.get("sensory_features"):
sensory_items = []
for feature, level in poi["sensory_features"].items():
icon = AVERSIONS[feature]["Icon"]
color = get_aversion_color(level, AVERSIONS[feature]["Type"])
qualitative_level = get_qualitative_aversion(
level, AVERSIONS[feature]["Type"]
)
qualitative_level = {"GOOD": "Basso", "ACCEPTABLE": "Medio", "BAD": "Alto"}[
qualitative_level
]
sensory_items.append(
f'<span style="color: {color}; margin-right: 8px;" title="{feature}: {level}/5">{icon} {feature}: {qualitative_level}</span>'
)
sensory_html = f"""
<div style="margin: 8px 0; padding: 8px; background: white; border-radius: 6px; border-left: 3px solid #7FB77E;">
<div style="font-size: 12px; color: #666; margin-bottom: 4px;">Caratteristiche Sensoriali:</div>
<div style="font-size: 14px;"><ul><li>{"</li><li>".join(sensory_items)}</li></ul></div>
</div>
"""
# Escape single quotes in POI name for JavaScript
poi_id_escaped = poi["poi_id:token"].replace(" ", "-").replace("'", "").replace('"', "")
poi_category = ' - '.join(poi["category:token_seq"]).replace('_', ' ')
html = f"""
<div class="poi-card" data-poi-name="{poi["name:token_seq"]}"
style="border: 2px solid {border_color};
background-color: {background_color};
padding: 0;
border-radius: 12px;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);"
onclick="document.getElementById('poi-btn-{poi_id_escaped}').click();"
onmouseover="this.style.transform='scale(1.02)'; this.style.boxShadow='0 4px 12px rgba(0,0,0,0.15)';"
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)';">
<!-- Dark overlay for selected state -->
<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; {overlay_style} pointer-events: none;"></div>
<div style="position: absolute; top: 15px; right: 15px; font-size: 28px; color: #7FB77E; font-weight: bold; z-index: 10; text-shadow: 0 0 4px white;">
{tick_icon}
</div>
<!-- Image filling horizontally -->
<div style="width: 100%; height: 200px; overflow: hidden; position: relative;">
<img src="{poi["image_url:token_seq"]}"
style="width: 100%; height: 100%; object-fit: cover;"
alt="{poi["name:token_seq"]}">
</div>
<!-- Content below image -->
<div style="padding: 15px; background-color: {content_bg}; position: relative; z-index: 2;">
<h3 style="margin: 0 0 8px 0; color: {title_color}; font-size: 18px;">{poi["name:token_seq"]}</h3>
<p style="margin: 0 0 8px 0; color: {location_color}; font-size: 14px; font-weight: 500;">
📍 {poi["address:token_seq"]}
</p>
<p style="margin: 0 0 12px 0; color: {category_color}; font-size: 14px; line-height: 1.4;">
{'🏷️ ' + poi_category if poi_category else ''}
</p>
{sensory_html}
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 12px;">
<span style="display: inline-block; padding: 6px 12px; background-color: {"#d4edda" if is_selected else "#e8f4e8"}; color: {title_color}; border-radius: 20px; font-size: 12px; font-weight: bold;">
{("✓ Selezionato" if is_selected else "Clicca per selezionare")}
</span>
</div>
</div>
</div>
"""
return html
def create_poi_cards_list(pois: list, selected_pois: set) -> str:
if not pois:
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>"
# Genera HTML per i POI filtrati
poi_cards_html = []
for poi in pois:
is_selected = (poi.get("poi_id:token"), poi.get("name:token_seq")) in selected_pois
card_html = create_poi_card_selectable(poi, is_selected)
poi_cards_html.append(card_html)
html = f"""
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; margin: 20px 0;">
{"".join(poi_cards_html)}
</div>
<div style='text-align: center; color: black; margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 8px;'>
<strong style='color: black;'>Trovati {len(pois)} punti di interesse</strong> • <span style='color: #7FB77E;'>{len(selected_pois)} selezionati</span>
</div>
"""
return html