| | 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""" |
| |
|
| | |
| | if is_selected: |
| | border_color = "#30892F" |
| | background_color = "#f0f8f0" |
| | tick_icon = "✓" |
| | |
| | overlay_style = "background-color: rgba(0, 0, 0, 0.15);" |
| | |
| | content_bg = "#e8f5e8" |
| | |
| | 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" |
| |
|
| | |
| | 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> |
| | """ |
| |
|
| | |
| | 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>" |
| |
|
| | |
| | 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 |
| |
|