turist / app.py
AlessandroArgiolas02's picture
Update app.py
e680c05 verified
# app.py
import gradio as gr
import requests
import os
from dotenv import load_dotenv
load_dotenv()
# Configurazione API
API_KEYS = {
"GOOGLE_PLACES": "AIzaSyAZpcC5nIdI-S-3Y28_giY4ZQiGDQ1ialY",
"UNSPLASH_ACCESS": "OqNHNAQYxq5VKPciSZpdKCvr2_wlKAvOegC2SR4N57M",
"OPENWEATHER": "a78fd47d30bd59926106c97c2f7a6fe2"
}
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(-2px); }
.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; }
.rating { color: #f9a825; }
.error { color: #d32f2f; background: #ffebee; padding: 15px; border-radius: 8px; }
.loading { color: #1976d2; padding: 20px; text-align: center; }
.card-content { display: flex; flex-direction: column; gap: 8px; }
.details { display: flex; gap: 15px; margin: 10px 0; }
.status { color: #1976d2; font-weight: 500; }
.website-link { color: #1565c0 !important; text-decoration: none; }
.website-link:hover { text-decoration: underline; }
"""
def get_coordinates(city):
try:
url = "https://maps.googleapis.com/maps/api/geocode/json"
params = {
"key": API_KEYS["GOOGLE_PLACES"],
"address": city,
"language": "it"
}
response = requests.get(url, params=params, timeout=10)
data = response.json()
if data['results']:
location = data['results'][0]['geometry']['location']
return (location['lat'], location['lng'])
return (None, None)
except Exception as e:
print(f"Errore geocoding: {str(e)}")
return (None, None)
def fetch_places(city, category):
try:
lat, lng = get_coordinates(city)
if not lat or not lng:
return []
place_types = {
"mangiare": "restaurant",
"visitare": "tourist_attraction",
"dormire": "lodging"
}
url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
params = {
"key": API_KEYS["GOOGLE_PLACES"],
"location": f"{lat},{lng}",
"radius": 10000,
"type": place_types[category],
"language": "it",
"rankby": "prominence"
}
response = requests.get(url, params=params, timeout=15)
response.raise_for_status()
results = response.json().get("results", [])[:8]
places = []
for place in results:
details_url = "https://maps.googleapis.com/maps/api/place/details/json"
details_params = {
"key": API_KEYS["GOOGLE_PLACES"],
"place_id": place['place_id'],
"fields": "name,vicinity,rating,price_level,types,website,opening_hours,formatted_phone_number",
"language": "it"
}
details_response = requests.get(details_url, params=details_params)
details = details_response.json().get('result', {})
price_level = "€" * details.get("price_level", 1) if details.get("price_level") else "💸 Variabile"
rating = details.get("rating", "⭐ Non disponibile")
opening_status = "🕒 Aperto ora" if details.get('opening_hours', {}).get('open_now') else "🕒 Chiuso"
places.append({
"name": details.get("name", "Nome non disponibile"),
"address": details.get("vicinity", "Indirizzo non disponibile"),
"rating": f"{rating}/5" if isinstance(rating, (int, float)) else rating,
"price": price_level,
"types": ", ".join([t.replace("_", " ").title() for t in details.get("types", [])]),
"opening": opening_status,
"website": details.get("website", "#"),
"phone": details.get("formatted_phone_number", "📞 Non disponibile")
})
return places
except Exception as e:
print(f"Errore fetch_places: {str(e)}")
return []
def get_city_photos(city):
try:
url = "https://api.unsplash.com/search/photos"
headers = {"Authorization": f"Client-ID {API_KEYS['UNSPLASH_ACCESS']}"}
params = {"query": f"{city} city", "per_page": 3}
response = requests.get(url, headers=headers, params=params, timeout=10)
response.raise_for_status()
photos = [img['urls']['regular'] for img in response.json()['results']]
return f"""
<div class='section'>
<h3>📸 Foto di {city.capitalize()}</h3>
<div class='gallery'>
{"".join([f"<img src='{photo}'>" for photo in photos])}
</div>
</div>
"""
except Exception as e:
print(f"Errore Unsplash: {str(e)}")
return ""
def get_weather(city):
try:
url = "http://api.openweathermap.org/data/2.5/weather"
params = {
"q": city,
"appid": API_KEYS["OPENWEATHER"],
"units": "metric",
"lang": "it"
}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
return f"""
<div class='section weather-widget'>
<h3>⛅ Meteo Attuale</h3>
<p>🌡️ Temperatura: {data['main']['temp']}°C</p>
<p>☁️ Condizioni: {data['weather'][0]['description'].capitalize()}</p>
<p>💧 Umidità: {data['main']['humidity']}%</p>
<p>🌬️ Vento: {data['wind']['speed']} m/s</p>
</div>
"""
except Exception as e:
print(f"Errore meteo: {str(e)}")
return ""
def generate_info_section(title, icon, items):
if not items:
return f"""
<div class='section'>
<h2>{icon} {title}</h2>
<div class='card error'>
<p>🔍 Nessun risultato trovato. Prova con:</p>
<ul>
<li>Un nome città più specifico</li>
<li>Verifica la correttezza del nome</li>
<li>Prova una città più grande</li>
</ul>
</div>
</div>
"""
cards = []
for item in items:
cards.append(f"""
<div class='card'>
<h3>{item['name']}</h3>
<div class='card-content'>
<p>📍 {item['address']}</p>
<p class='status'>{item['opening']}</p>
<div class='details'>
<p class='rating'>{item['rating']}</p>
<p class='price-tag'>{item['price']}</p>
</div>
<p>🏷️ {item['types']}</p>
<p>{item['phone']}</p>
{f"<a href='{item['website']}' target='_blank' class='website-link'>🌐 Sito Web</a>" if item['website'] != "#" else ""}
</div>
</div>
""")
return f"""
<div class='section'>
<h2>{icon} {title}</h2>
{"".join(cards)}
</div>
"""
def get_city_info(city):
try:
if not city.strip():
return "<div class='error'>🚨 Inserisci il nome di una città</div>"
yield """
<div class='loading'>
<h3>🔎 Ricerca informazioni in corso...</h3>
<p>Stiamo analizzando più di 15 fonti diverse</p>
<p>⏱️ Tempo stimato: 5-10 secondi</p>
</div>
"""
weather_html = get_weather(city)
photos_html = get_city_photos(city)
places_to_eat = fetch_places(city, "mangiare")
places_to_visit = fetch_places(city, "visitare")
places_to_stay = fetch_places(city, "dormire")
content = f"""
<div class='travel-container'>
<h1 class='header'>🌍 {city.capitalize()}</h1>
{weather_html}
{photos_html}
{generate_info_section("Dove Mangiare", "🍽️", places_to_eat)}
{generate_info_section("Cosa Visitare", "🏛️", places_to_visit)}
{generate_info_section("Dove Dormire", "🛏️", places_to_stay)}
</div>
"""
yield content
except Exception as e:
print(f"Errore: {str(e)}")
yield f"""
<div class='error'>
<h3>❌ Errore grave</h3>
<p>Motivo: {str(e)}</p>
<p>Prova a:</p>
<ul>
<li>Controllare la connessione internet</li>
<li>Riprova tra qualche minuto</li>
<li>Contatta il supporto se il problema persiste</li>
</ul>
</div>
"""
with gr.Blocks(css=CSS) as app:
gr.Markdown("# 🧳 Travel Assistant Pro")
with gr.Row():
city_input = gr.Textbox(
label="Inserisci la città da esplorare",
placeholder="Es: Roma, Tokyo, Barcellona...",
scale=4
)
search_btn = gr.Button("Avvia Ricerca", variant="primary", scale=1)
info_output = gr.HTML()
search_btn.click(
fn=get_city_info,
inputs=city_input,
outputs=info_output
)
if __name__ == "__main__":
app.launch()