# app.py import gradio as gr import requests import google.generativeai as genai import json import os import re # Configurazione da variabili d'ambiente GEMINI_KEY = os.getenv("GEMINI") UNSPLASH_KEY = os.getenv("UNSPLASH_ACCESS") WEATHER_KEY = os.getenv("OPENWEATHER") # Inizializzazione Gemini genai.configure(api_key=GEMINI_KEY) model = genai.GenerativeModel('gemini-pro') CSS = """ body { font-family: 'Arial', sans-serif; background: #f5f7fa; margin: 0; } .travel-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .header { text-align: center; color: #2d3436; margin-bottom: 30px; } .section { background: white; padding: 20px; border-radius: 15px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .card { border: 1px solid #e0e0e0; padding: 15px; margin: 10px 0; border-radius: 8px; transition: transform 0.2s; } .card:hover { transform: translateY(-3px); } .gallery { display: flex; flex-wrap: wrap; gap: 10px; margin: 15px 0; } .gallery img { width: 30%; flex-grow: 1; border-radius: 8px; height: 200px; object-fit: cover; } .weather-widget { background: #e3f2fd !important; padding: 15px !important; } .price-tag { color: #2e7d32; font-weight: bold; } .reasons-list { padding-left: 20px; color: #666; } .loading { text-align: center; padding: 30px; color: #555; } .error { background: #ffebee; color: #d32f2f; padding: 20px; border-radius: 8px; } """ def get_weather(city): try: url = "http://api.openweathermap.org/data/2.5/weather" params = { "q": city, "appid": WEATHER_KEY, "units": "metric", "lang": "it" } response = requests.get(url, params=params, timeout=10) data = response.json() return f"""

⛅ Meteo Attuale

🌡️ Temperatura: {data['main']['temp']}°C

☁️ Condizioni: {data['weather'][0]['description'].capitalize()}

💧 Umidità: {data['main']['humidity']}%

🌬️ Vento: {data['wind']['speed']} m/s

""" except Exception as e: print(f"Errore meteo: {str(e)}") return "" def get_city_photos(city): try: url = "https://api.unsplash.com/search/photos" headers = {"Authorization": f"Client-ID {UNSPLASH_KEY}"} params = {"query": f"{city} city", "per_page": 3} response = requests.get(url, headers=headers, params=params, timeout=10) photos = [img['urls']['regular'] for img in response.json()['results']] return f"""

📸 Foto di {city.capitalize()}

""" except Exception as e: print(f"Errore foto: {str(e)}") return "" def generate_recommendations(city, category): prompt = f""" GENERA SOLO JSON VALIDO. Niente testo prima o dopo. Genera 5 consigli per {category} a {city} in formato JSON con struttura: {{ "recommendations": [ {{ "name": "Nome", "description": "Descrizione (max 40 parole)", "price_range": "€/€€/€€€", "address": "Zona", "reasons": ["motivo1", "motivo2", "motivo3"], "tip": "Consiglio pratico" }} ] }} """ try: response = model.generate_content(prompt) return response.text except Exception as e: print(f"Errore Gemini: {str(e)}") return None def parse_response(response): try: # Pulizia e validazione JSON json_str = re.sub(r'[\x00-\x1F]+', '', response) # Rimuove caratteri non stampabili json_str = re.search(r'\{.*\}', json_str, re.DOTALL).group() data = json.loads(json_str) # Validazione struttura if not isinstance(data.get('recommendations', []), list): return [] return data['recommendations'][:5] # Limita a 5 risultati except Exception as e: print(f"Errore parsing: {str(e)}") return [] with gr.Blocks(css=CSS) as app: gr.Markdown("# 🌍 Travel Assistant Pro") city_input = gr.Textbox(label="Inserisci città", placeholder="Es: Roma, Parigi...") search_btn = gr.Button("Cerca") output = gr.HTML() @search_btn.click(inputs=city_input, outputs=output) def search(city): if not city.strip(): return "
🚨 Inserisci una città valida
" yield "
🔍 Ricerca in corso... ⏳
" try: # Dati base weather = get_weather(city) photos = get_city_photos(city) # Genera raccomandazioni sections = [] for cat in ["mangiare", "visitare", "dormire"]: response = generate_recommendations(city, cat) items = parse_response(response) if response else [] cards = [] for item in items: cards.append(f"""

{item.get('name', '')}

📍 {item.get('address', 'Indirizzo non disponibile')}

💰 {item.get('price_range', '€')}

{item.get('description', '')}

{"".join([f"

✔️ {r}

" for r in item.get('reasons', [])])}

💡 {item.get('tip', '')}

""") sections.append(f"""

🌟 {cat.capitalize()}

{"".join(cards) if cards else '

Nessun risultato trovato

'}
""") # Costruisci output content = f"""

{city.capitalize()}

{weather} {photos} {"".join(sections)}
""" yield content except Exception as e: error_msg = f"""

❌ Errore grave

{str(e)}

Prova a ricaricare la pagina o riprova più tardi

""" yield error_msg app.launch()