Backup automatico script del 2026-02-08 07:00
This commit is contained in:
@@ -47,6 +47,7 @@ MODEL_NAMES = {
|
||||
"icon_d2": "ICON-D2",
|
||||
"gfs_global": "GFS",
|
||||
"ecmwf_ifs04": "ECMWF",
|
||||
"ecmwf_ifs": "ECMWF IFS",
|
||||
"jma_msm": "JMA MSM",
|
||||
"metno_nordic": "Yr.no",
|
||||
"ukmo_global": "UK MetOffice",
|
||||
@@ -57,26 +58,18 @@ MODEL_NAMES = {
|
||||
def choose_models_by_country(cc, is_home=False):
|
||||
"""
|
||||
Seleziona modelli meteo ottimali.
|
||||
- Per Casa: usa AROME Seamless e ICON-D2 (alta risoluzione)
|
||||
- Per Italia: usa italia_meteo_arpae_icon_2i (include snow_depth quando > 0)
|
||||
- Per altre località: usa best match di Open-Meteo (senza specificare models)
|
||||
- Per Casa e Italia: solo ICON Italia (ARPAE 2i); AROME HD non copre San Marino.
|
||||
- Per altre località: usa best match di Open-Meteo (senza specificare models).
|
||||
Ritorna (short_term_models, long_term_models)
|
||||
"""
|
||||
cc = cc.upper() if cc else "UNKNOWN"
|
||||
|
||||
# Modelli a lungo termine (sempre globali, funzionano ovunque)
|
||||
long_term_default = ["gfs_global", "ecmwf_ifs04"]
|
||||
|
||||
if is_home:
|
||||
# Per Casa, usa AROME Seamless, ICON-D2 e ICON Italia (alta risoluzione europea)
|
||||
# ICON Italia include snow_depth quando disponibile (> 0)
|
||||
return ["meteofrance_seamless", "icon_d2", "italia_meteo_arpae_icon_2i"], long_term_default
|
||||
elif cc == "IT":
|
||||
# Per Italia, usa ICON Italia (ARPAE 2i) che include snow_depth quando disponibile
|
||||
return ["italia_meteo_arpae_icon_2i"], long_term_default
|
||||
if is_home or cc == "IT":
|
||||
# ICON Italia (0–72h) + ECMWF IFS per i giorni successivi (dove Icon Italia non arriva)
|
||||
return ["italia_meteo_arpae_icon_2i"], ["ecmwf_ifs"]
|
||||
else:
|
||||
# Per query worldwide, usa best match (Open-Meteo sceglie automaticamente)
|
||||
# Ritorna None per indicare best match
|
||||
return None, long_term_default
|
||||
|
||||
def get_bot_token():
|
||||
@@ -127,9 +120,9 @@ def get_weather_multi_model(lat, lon, short_term_models, long_term_models, forec
|
||||
url = "https://api.open-meteo.com/v1/forecast"
|
||||
params = {
|
||||
"latitude": lat, "longitude": lon,
|
||||
"hourly": "temperature_2m,precipitation_probability,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm",
|
||||
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,precipitation_probability_max,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max",
|
||||
"timezone": timezone if timezone else TZ_STR, "forecast_days": min(forecast_days, 3) # Limita a 3 giorni per modelli ad alta risoluzione
|
||||
"hourly": "temperature_2m,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm",
|
||||
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max",
|
||||
"timezone": timezone if timezone else TZ_STR, "forecast_days": min(forecast_days, 3)
|
||||
}
|
||||
try:
|
||||
resp = open_meteo_get(url, params=params, timeout=(5, 20))
|
||||
@@ -163,14 +156,19 @@ def get_weather_multi_model(lat, lon, short_term_models, long_term_models, forec
|
||||
# Modelli specifici (per Casa: AROME + ICON, per Italia: ICON ARPAE)
|
||||
for model in short_term_models:
|
||||
url = "https://api.open-meteo.com/v1/forecast"
|
||||
# Per italia_meteo_arpae_icon_2i, includi sempre snow_depth (supportato quando > 0)
|
||||
hourly_params = "temperature_2m,precipitation_probability,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm"
|
||||
|
||||
# ICON Italia (ARPAE 2i): parametri come da API, senza precipitation_probability
|
||||
if model == "italia_meteo_arpae_icon_2i":
|
||||
hourly_params = "rain,showers,snowfall,snow_depth,precipitation,temperature_2m,weathercode,windspeed_10m,winddirection_10m"
|
||||
daily_params = "temperature_2m_max,temperature_2m_min,showers_sum,rain_sum,snowfall_sum,precipitation_sum,precipitation_hours,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max"
|
||||
else:
|
||||
hourly_params = "temperature_2m,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm"
|
||||
daily_params = "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max"
|
||||
params = {
|
||||
"latitude": lat, "longitude": lon,
|
||||
"hourly": hourly_params,
|
||||
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,precipitation_probability_max,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max",
|
||||
"timezone": timezone if timezone else TZ_STR, "models": model, "forecast_days": min(forecast_days, 3) # Limita a 3 giorni per modelli ad alta risoluzione
|
||||
"daily": daily_params,
|
||||
"timezone": timezone if timezone else TZ_STR, "models": model,
|
||||
"forecast_days": min(forecast_days, 7) if model == "italia_meteo_arpae_icon_2i" else min(forecast_days, 3)
|
||||
}
|
||||
try:
|
||||
resp = open_meteo_get(url, params=params, timeout=(5, 20))
|
||||
@@ -220,13 +218,20 @@ def get_weather_multi_model(lat, lon, short_term_models, long_term_models, forec
|
||||
except:
|
||||
results[model] = None
|
||||
|
||||
# Recupera modelli a lungo termine (globale, fino a 10 giorni)
|
||||
for model in long_term_models:
|
||||
# Recupera modelli a lungo termine (dopo 72h, dove Icon Italia non arriva)
|
||||
for model in (long_term_models or []):
|
||||
url = "https://api.open-meteo.com/v1/forecast"
|
||||
# ECMWF IFS: parametri come da API (rain, showers, snowfall) + campi necessari per il report
|
||||
if model == "ecmwf_ifs":
|
||||
hourly_params = "rain,showers,snowfall,precipitation,temperature_2m,weathercode,windspeed_10m,winddirection_10m"
|
||||
daily_params = "temperature_2m_max,temperature_2m_min,showers_sum,rain_sum,snowfall_sum,precipitation_sum,precipitation_hours,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max"
|
||||
else:
|
||||
hourly_params = "temperature_2m,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm"
|
||||
daily_params = "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max"
|
||||
params = {
|
||||
"latitude": lat, "longitude": lon,
|
||||
"hourly": "temperature_2m,precipitation_probability,precipitation,snowfall,rain,snow_depth,weathercode,windspeed_10m,windgusts_10m,winddirection_10m,dewpoint_2m,relative_humidity_2m,cloud_cover,soil_temperature_0cm",
|
||||
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours,precipitation_probability_max,snowfall_sum,showers_sum,rain_sum,weathercode,winddirection_10m_dominant,windspeed_10m_max,windgusts_10m_max",
|
||||
"hourly": hourly_params,
|
||||
"daily": daily_params,
|
||||
"timezone": timezone if timezone else TZ_STR, "models": model, "forecast_days": forecast_days
|
||||
}
|
||||
try:
|
||||
@@ -269,11 +274,11 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
"temperature_2m_min": [],
|
||||
"precipitation_sum": [],
|
||||
"precipitation_hours": [],
|
||||
"precipitation_probability_max": [],
|
||||
"snowfall_sum": [],
|
||||
"showers_sum": [],
|
||||
"rain_sum": [],
|
||||
"weathercode": [],
|
||||
"winddirection_10m_dominant": [],
|
||||
"windspeed_10m_max": [],
|
||||
"windgusts_10m_max": []
|
||||
},
|
||||
@@ -288,7 +293,6 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
"windspeed_10m": [],
|
||||
"winddirection_10m": [],
|
||||
"dewpoint_2m": [],
|
||||
"precipitation_probability": [],
|
||||
"cloud_cover": [],
|
||||
"soil_temperature_0cm": []
|
||||
},
|
||||
@@ -358,36 +362,37 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
model_display = "Best Match"
|
||||
else:
|
||||
model_display = MODEL_NAMES.get(short_term_model, short_term_model)
|
||||
short_daily = short_term_data.get("daily", {})
|
||||
short_hourly = short_term_data.get("hourly", {})
|
||||
# Prendi dati daily: tutti i giorni se è l'unico modello, altrimenti primi cutoff_day+1
|
||||
short_daily_times_all = short_daily.get("time", [])
|
||||
short_daily_times = short_daily_times_all[:cutoff_day+1] if long_term_data else short_daily_times_all
|
||||
|
||||
# Verifica se ICON Italia ha dati di snow_depth (controllo diretto, non solo il flag)
|
||||
has_icon_snow_depth = False
|
||||
if icon_italia_data:
|
||||
icon_hourly = icon_italia_data.get("hourly", {})
|
||||
icon_snow_depth = icon_hourly.get("snow_depth", []) if icon_hourly else []
|
||||
# Verifica se ci sono dati non-null di snow_depth
|
||||
if icon_snow_depth:
|
||||
for sd in icon_snow_depth[:72]: # Controlla prime 72h
|
||||
if sd is not None:
|
||||
try:
|
||||
if float(sd) > 0: # Anche valori piccoli
|
||||
if float(sd) > 0:
|
||||
has_icon_snow_depth = True
|
||||
break
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# Se ICON Italia ha dati di snow_depth, aggiungilo ai modelli usati
|
||||
num_days = len(short_daily_times)
|
||||
if has_icon_snow_depth or (icon_italia_data and icon_italia_data.get("has_snow_depth_data")):
|
||||
icon_display = MODEL_NAMES.get("italia_meteo_arpae_icon_2i", "ICON Italia")
|
||||
merged["models_used"].append(f"{model_display} + {icon_display} (snow_depth) (0-{cutoff_day+1}d)")
|
||||
merged["models_used"].append(f"{model_display} + {icon_display} (snow_depth) (0-{num_days}d)")
|
||||
else:
|
||||
merged["models_used"].append(f"{model_display} (0-{cutoff_day+1}d)")
|
||||
short_daily = short_term_data.get("daily", {})
|
||||
short_hourly = short_term_data.get("hourly", {})
|
||||
merged["models_used"].append(f"{model_display} (0-{num_days}d)")
|
||||
|
||||
# Prendi dati daily dai primi giorni del modello a breve termine
|
||||
short_daily_times = short_daily.get("time", [])[:cutoff_day+1]
|
||||
for i, day_time in enumerate(short_daily_times):
|
||||
merged["daily"]["time"].append(day_time)
|
||||
for key in ["temperature_2m_max", "temperature_2m_min", "precipitation_sum", "precipitation_hours", "precipitation_probability_max", "snowfall_sum", "showers_sum", "rain_sum", "weathercode", "windspeed_10m_max", "windgusts_10m_max"]:
|
||||
for key in ["temperature_2m_max", "temperature_2m_min", "precipitation_sum", "precipitation_hours", "snowfall_sum", "showers_sum", "rain_sum", "weathercode", "winddirection_10m_dominant", "windspeed_10m_max", "windgusts_10m_max"]:
|
||||
val = short_daily.get(key, [])[i] if i < len(short_daily.get(key, [])) else None
|
||||
merged["daily"][key].append(val)
|
||||
|
||||
@@ -409,10 +414,10 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
cutoff_hour = (cutoff_day + 1) * 24
|
||||
cutoff_hour = (cutoff_day + 1) * 24 if long_term_data else len(short_hourly_times)
|
||||
for i, hour_time in enumerate(short_hourly_times[:cutoff_hour]):
|
||||
merged["hourly"]["time"].append(hour_time)
|
||||
for key in ["temperature_2m", "precipitation", "snowfall", "rain", "weathercode", "windspeed_10m", "winddirection_10m", "dewpoint_2m", "precipitation_probability", "cloud_cover", "soil_temperature_0cm"]:
|
||||
for key in ["temperature_2m", "precipitation", "snowfall", "rain", "weathercode", "windspeed_10m", "winddirection_10m", "dewpoint_2m", "cloud_cover", "soil_temperature_0cm"]:
|
||||
val = short_hourly.get(key, [])[i] if i < len(short_hourly.get(key, [])) else None
|
||||
merged["hourly"][key].append(val)
|
||||
# Per snow_depth: usa ICON Italia se disponibile (corrispondenza per timestamp), altrimenti modello principale
|
||||
@@ -455,7 +460,7 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
|
||||
for i in range(start_idx, min(len(long_daily_times), forecast_days)):
|
||||
merged["daily"]["time"].append(long_daily_times[i])
|
||||
for key in ["temperature_2m_max", "temperature_2m_min", "precipitation_sum", "precipitation_hours", "precipitation_probability_max", "snowfall_sum", "showers_sum", "rain_sum", "weathercode", "windspeed_10m_max", "windgusts_10m_max"]:
|
||||
for key in ["temperature_2m_max", "temperature_2m_min", "precipitation_sum", "precipitation_hours", "snowfall_sum", "showers_sum", "rain_sum", "weathercode", "winddirection_10m_dominant", "windspeed_10m_max", "windgusts_10m_max"]:
|
||||
val = long_daily.get(key, [])[i] if i < len(long_daily.get(key, [])) else None
|
||||
merged["daily"][key].append(val)
|
||||
|
||||
@@ -468,7 +473,7 @@ def merge_multi_model_forecast(models_data, forecast_days=10):
|
||||
start_hour_idx = current_hourly_count
|
||||
for i in range(start_hour_idx, min(len(long_hourly_times), needed_hours)):
|
||||
merged["hourly"]["time"].append(long_hourly_times[i])
|
||||
for key in ["temperature_2m", "precipitation", "snowfall", "snow_depth", "rain", "weathercode", "windspeed_10m", "winddirection_10m", "dewpoint_2m", "precipitation_probability", "cloud_cover", "soil_temperature_0cm"]:
|
||||
for key in ["temperature_2m", "precipitation", "snowfall", "snow_depth", "rain", "weathercode", "windspeed_10m", "winddirection_10m", "dewpoint_2m", "cloud_cover", "soil_temperature_0cm"]:
|
||||
val = long_hourly.get(key, [])[i] if i < len(long_hourly.get(key, [])) else None
|
||||
merged["hourly"][key].append(val)
|
||||
|
||||
@@ -792,16 +797,13 @@ def analyze_daily_events(times, codes, probs, precip, winds, temps, dewpoints, s
|
||||
block_precip = precip[start_idx:end_idx] if end_idx <= len(precip) else precip[start_idx:]
|
||||
block_precip_clean = [p for p in block_precip if p is not None]
|
||||
tot_mm = sum(block_precip_clean)
|
||||
prob_block = probs[start_idx:end_idx] if end_idx <= len(probs) else probs[start_idx:]
|
||||
prob_block_clean = [p for p in prob_block if p is not None]
|
||||
max_prob = max(prob_block_clean) if prob_block_clean else 0
|
||||
start_time = times[start_idx].split("T")[1][:5]
|
||||
end_time = times[end_idx].split("T")[1][:5] if end_idx < len(times) else times[-1].split("T")[1][:5]
|
||||
avg_intensity = tot_mm / len(block_precip) if block_precip else 0
|
||||
|
||||
events.append(
|
||||
f"{current_rain_type} ({get_intensity_label(avg_intensity)}):\n"
|
||||
f" 🕒 {start_time}-{end_time} | 💧 {tot_mm:.1f}mm | Prob: {max_prob}%"
|
||||
f" 🕒 {start_time}-{end_time} | 💧 {tot_mm:.1f}mm"
|
||||
)
|
||||
start_idx = i
|
||||
current_rain_type = new_type
|
||||
@@ -813,16 +815,13 @@ def analyze_daily_events(times, codes, probs, precip, winds, temps, dewpoints, s
|
||||
tot_mm = sum(block_precip_clean)
|
||||
|
||||
if tot_mm > 0:
|
||||
prob_block = probs[start_idx:end_idx] if end_idx <= len(probs) else probs[start_idx:]
|
||||
prob_block_clean = [p for p in prob_block if p is not None]
|
||||
max_prob = max(prob_block_clean) if prob_block_clean else 0
|
||||
start_time = times[start_idx].split("T")[1][:5]
|
||||
end_time = times[min(end_idx-1, len(times)-1)].split("T")[1][:5]
|
||||
avg_intensity = tot_mm / len(block_precip) if block_precip else 0
|
||||
|
||||
events.append(
|
||||
f"{current_rain_type} ({get_intensity_label(avg_intensity)}):\n"
|
||||
f" 🕒 {start_time}-{end_time} | 💧 {tot_mm:.1f}mm | Prob: {max_prob}%"
|
||||
f" 🕒 {start_time}-{end_time} | 💧 {tot_mm:.1f}mm"
|
||||
)
|
||||
|
||||
# 3. VENTO
|
||||
@@ -1225,12 +1224,7 @@ def format_weather_context_report(models_data, location_name, country_code):
|
||||
wcode = int(wcode) if wcode is not None else 0
|
||||
|
||||
# Calcola probabilità e tipo precipitazione per il giorno (PRIMA di determinare icona)
|
||||
precip_prob = 0
|
||||
precip_type = None
|
||||
if d_probs and any(p is not None for p in d_probs):
|
||||
prob_values = [p for p in d_probs if p is not None]
|
||||
precip_prob = max(prob_values) if prob_values else 0
|
||||
|
||||
# Determina tipo precipitazione usando dati daily (più affidabili)
|
||||
# Usa snowfall_sum, rain_sum, showers_sum per caratterizzazione precisa
|
||||
# PRIORITÀ: Se snow_depth > 0 (modello italia_meteo_arpae_icon_2i), usa questi dati per determinare neve
|
||||
@@ -1369,14 +1363,6 @@ def format_weather_context_report(models_data, location_name, country_code):
|
||||
else:
|
||||
weather_icon = "☁️" # Nuvoloso
|
||||
|
||||
# Recupera probabilità max daily se disponibile
|
||||
prob_max_list = daily.get('precipitation_probability_max', [])
|
||||
precip_prob_max = None
|
||||
if count < len(prob_max_list) and prob_max_list[count] is not None:
|
||||
precip_prob_max = int(prob_max_list[count])
|
||||
elif precip_prob > 0:
|
||||
precip_prob_max = int(precip_prob)
|
||||
|
||||
# Calcola spessore manto nevoso (snow_depth) per questo giorno
|
||||
# Nota: snow_depth è disponibile solo per alcuni modelli (es. ICON Italia, ICON-D2)
|
||||
# Se il modello non supporta snow_depth, tutti i valori saranno None o mancanti
|
||||
@@ -1449,7 +1435,6 @@ def format_weather_context_report(models_data, location_name, country_code):
|
||||
"t_min": t_min,
|
||||
"t_max": t_max,
|
||||
"precip_sum": precip_sum,
|
||||
"precip_prob": precip_prob_max if precip_prob_max is not None else precip_prob,
|
||||
"precip_type": precip_type,
|
||||
"snowfall_sum": snowfall_sum,
|
||||
"rain_sum": rain_sum,
|
||||
@@ -1496,13 +1481,6 @@ def format_weather_context_report(models_data, location_name, country_code):
|
||||
precip_parts.append(f"{precip_symbol} {day_info['precip_sum']:.1f}mm")
|
||||
|
||||
line += f" | {' + '.join(precip_parts)}"
|
||||
|
||||
# Aggiungi probabilità se disponibile
|
||||
if day_info['precip_prob'] and day_info['precip_prob'] > 0:
|
||||
line += f" ({int(day_info['precip_prob'])}%)"
|
||||
elif day_info['precip_prob'] > 50:
|
||||
# Probabilità alta ma nessuna precipitazione prevista (può essere un errore del modello)
|
||||
line += f" | 💧 Possibile ({int(day_info['precip_prob'])}%)"
|
||||
|
||||
# Aggiungi vento (sempre se disponibile, formattato come direzione intensità)
|
||||
if day_info['wind_max'] > 0:
|
||||
|
||||
Reference in New Issue
Block a user