ArslanFOX's picture
Update app.py
a1926e1 verified
import os
import datetime
import pytz
import requests
from dotenv import load_dotenv
from langdetect import detect
from geopy.geocoders import Nominatim
from timezonefinder import TimezoneFinder
import gradio as gr
import re
load_dotenv()
def geocode_city(city: str, lang: str = "en"):
"""
Geocode city with fallback to simpler name and language.
"""
geolocator = Nominatim(user_agent="weather_assistant")
location = geolocator.geocode(city, language=lang)
if not location:
# fallback: first word
simple = city.split()[0]
location = geolocator.geocode(simple, language=lang)
return location
def get_timezone_by_city(city: str) -> str:
"""
Get timezone for a city.
"""
location = geocode_city(city)
if not location:
return f"❌ Не удалось найти населенный пункт '{city}'."
tz = TimezoneFinder().timezone_at(lat=location.latitude, lng=location.longitude)
if not tz:
return f"❌ Не удалось определить часовой пояс для '{city}'."
return tz
def get_current_time_in_timezone(timezone: str) -> str:
"""
Get local current time in a timezone.
"""
try:
now = datetime.datetime.now(pytz.timezone(timezone))
return now.strftime("%Y-%m-%d %H:%M:%S")
except Exception as e:
return f"❌ Ошибка получения времени: {e}"
def get_air_quality(city: str, lang: str = "en") -> str:
"""
Get translated air quality info.
"""
location = geocode_city(city)
if not location:
return f"❌ Не удалось найти населенный пункт '{city}'."
api_key = os.environ.get("OPENWEATHERMAP_API_KEY")
if not api_key:
return "❌ Отсутствует API-ключ OpenWeatherMap."
url = (f"http://api.openweathermap.org/data/2.5/air_pollution?"
f"lat={location.latitude}&lon={location.longitude}&appid={api_key}")
resp = requests.get(url)
if resp.status_code != 200:
return f"❌ Ошибка API качества воздуха: {resp.status_code}"
data = resp.json().get("list", [])
if not data:
return "❌ Нет данных о качестве воздуха."
aqi = data[0]["main"]["aqi"]
comps = data[0]["components"]
translations = {
"en": {
"title": "😷 Air Quality",
"aqi_desc": {1: "Good", 2: "Fair", 3: "Moderate", 4: "Poor", 5: "Very Poor"},
"pm2_5": "Fine particles (PM2.5)",
"pm10": "Coarse particles (PM10)",
"co": "Carbon monoxide (CO)",
},
"ru": {
"title": "😷 Качество воздуха",
"aqi_desc": {1: "Хорошее", 2: "Удовлетворительное", 3: "Среднее", 4: "Плохое", 5: "Очень плохое"},
"pm2_5": "Мелкие частицы (PM2.5)",
"pm10": "Крупные частицы (PM10)",
"co": "Углекислый газ (CO)",
},
"hi": {
"title": "😷 वायु गुणवत्ता",
"aqi_desc": {1: "अच्छा", 2: "संतोषजनक", 3: "मध्यम", 4: "खराब", 5: "बहुत खराब"},
"pm2_5": "सूक्ष्म कण (PM2.5)",
"pm10": "मोटे कण (PM10)",
"co": "कार्बन मोनोऑक्साइड (CO)",
},
"zh": {
"title": "😷 空气质量",
"aqi_desc": {1: "良好", 2: "一般", 3: "中等", 4: "较差", 5: "很差"},
"pm2_5": "细颗粒物 (PM2.5)",
"pm10": "可吸入颗粒物 (PM10)",
"co": "一氧化碳 (CO)",
},
"es": {
"title": "😷 Calidad del aire",
"aqi_desc": {1: "Buena", 2: "Aceptable", 3: "Moderada", 4: "Pobre", 5: "Muy mala"},
"pm2_5": "Partículas finas (PM2.5)",
"pm10": "Partículas gruesas (PM10)",
"co": "Monóxido de carbono (CO)",
},
"fr": {
"title": "😷 Qualité de l'air",
"aqi_desc": {1: "Bonne", 2: "Passable", 3: "Moyenne", 4: "Mauvaise", 5: "Très mauvaise"},
"pm2_5": "Particules fines (PM2.5)",
"pm10": "Particules grossières (PM10)",
"co": "Monoxyde de carbone (CO)",
},
"ko": {
"title": "😷 대기질",
"aqi_desc": {1: "좋음", 2: "보통", 3: "보통 이상", 4: "나쁨", 5: "매우 나쁨"},
"pm2_5": "미세먼지 (PM2.5)",
"pm10": "먼지 (PM10)",
"co": "일산화탄소 (CO)",
},
}
t = translations.get(lang, translations["en"])
emoji = "🌱" if aqi <= 2 else "😷" if aqi <= 4 else "☣️"
return (
f"{t['title']}: AQI {aqi} ({t['aqi_desc'][aqi]}) {emoji}\n"
f"{t['pm2_5']}: {comps['pm2_5']} µg/m³\n"
f"{t['pm10']}: {comps['pm10']} µg/m³\n"
f"{t['co']}: {comps['co']} µg/m³"
)
def get_weather(city: str, lang: str = "en") -> str:
"""
Get translated weather info.
"""
location = geocode_city(city)
if not location:
return f"❌ Не удалось найти населенный пункт '{city}'."
api_key = os.environ.get("OPENWEATHERMAP_API_KEY")
if not api_key:
return "❌ Отсутствует API-ключ OpenWeatherMap."
url = (f"http://api.openweathermap.org/data/2.5/weather?"
f"lat={location.latitude}&lon={location.longitude}"
f"&appid={api_key}&units=metric")
resp = requests.get(url)
if resp.status_code != 200:
return f"❌ Ошибка API погоды: {resp.status_code}"
d = resp.json()
cond = d["weather"][0]["main"]
temp = d["main"]["temp"]
hum = d["main"]["humidity"]
translations = {
"en": {
"title": "🌡️ Weather",
"temp": "Temperature",
"humidity": "Humidity",
"conditions": {
"Clear": "Clear ☀️", "Clouds": "Cloudy ☁️", "Rain": "Rain 🌧",
"Snow": "Snow ❄️", "Thunderstorm": "Storm ⛈", "Drizzle": "Drizzle 🌦",
"Mist": "Mist 🌫", "Haze": "Haze 🌫"
}
},
"ru": {
"title": "🌡️ Погода",
"temp": "Температура",
"humidity": "Влажность",
"conditions": {
"Clear": "Ясно ☀️", "Clouds": "Облачно ☁️", "Rain": "Дождь 🌧",
"Snow": "Снег ❄️", "Thunderstorm": "Гроза ⛈", "Drizzle": "Морось 🌦",
"Mist": "Туман 🌫", "Haze": "Дымка 🌫"
}
},
"hi": {
"title": "🌡️ मौसम",
"temp": "तापमान",
"humidity": "नमी",
"conditions": {
"Clear": "साफ ☀️", "Clouds": "बादल ☁️", "Rain": "बारिश 🌧",
"Snow": "बर्फ ❄️", "Thunderstorm": "तूफान ⛈", "Drizzle": "बूंदाबांदी 🌦",
"Mist": "कोहरा 🌫", "Haze": "धुंध 🌫"
}
},
"zh": {
"title": "🌡️ 天气",
"temp": "温度",
"humidity": "湿度",
"conditions": {
"Clear": "晴 ☀️", "Clouds": "多云 ☁️", "Rain": "雨 🌧",
"Snow": "雪 ❄️", "Thunderstorm": "雷暴 ⛈", "Drizzle": "毛毛雨 🌦",
"Mist": "雾 🌫", "Haze": "霾 🌫"
}
},
"es": {
"title": "🌡️ Clima",
"temp": "Temperatura",
"humidity": "Humedad",
"conditions": {
"Clear": "Despejado ☀️", "Clouds": "Nublado ☁️", "Rain": "Lluvia 🌧",
"Snow": "Nieve ❄️", "Thunderstorm": "Tormenta ⛈", "Drizzle": "Llovizna 🌦",
"Mist": "Niebla 🌫", "Haze": "Calina 🌫"
}
},
"fr": {
"title": "🌡️ Météo",
"temp": "Température",
"humidity": "Humidité",
"conditions": {
"Clear": "Dégagé ☀️", "Clouds": "Nuageux ☁️", "Rain": "Pluie 🌧",
"Snow": "Neige ❄️", "Thunderstorm": "Orage ⛈", "Drizzle": "Bruine 🌦",
"Mist": "Brume 🌫", "Haze": "Brume légère 🌫"
}
},
"ko": {
"title": "🌡️ 날씨",
"temp": "온도",
"humidity": "습도",
"conditions": {
"Clear": "맑음 ☀️", "Clouds": "흐림 ☁️", "Rain": "비 🌧",
"Snow": "눈 ❄️", "Thunderstorm": "뇌우 ⛈", "Drizzle": "이슬비 🌦",
"Mist": "안개 🌫", "Haze": "실안개 🌫"
}
},
}
t = translations.get(lang, translations["en"])
desc = t["conditions"].get(cond, cond)
return (
f"{t['title']}: {desc}\n"
f"{t['temp']}: {temp}°C\n"
f"{t['humidity']}: {hum}%"
)
def process_input(user_input: str) -> str:
lang = detect(user_input)
if lang not in ["en","ru","hi","zh","es","fr","ko"]:
if re.search(r'[\u0400-\u04FF]', user_input):
lang = "ru"
else:
lang = "en"
city = user_input.strip()
loc_disp = geocode_city(city, lang)
if not loc_disp:
return f"❌ Не удалось найти населенный пункт '{city}'."
parts = loc_disp.raw.get("display_name", "").split(", ")
city_disp = parts[0]
region_disp = parts[1] if len(parts) > 2 else ""
country_disp = parts[-1] if parts else ""
location_labels = {
"en": "📍 Location",
"ru": "📍 Местоположение",
"hi": "📍 स्थान",
"zh": "📍 位置",
"es": "📍 Ubicación",
"fr": "📍 Emplacement",
"ko": "📍 위치",
}
time_labels = {
"en": "🕒 Time",
"ru": "🕒 Время",
"hi": "🕒 समय",
"zh": "🕒 时间",
"es": "🕒 Hora",
"fr": "🕒 Heure",
"ko": "🕒 시간",
}
location_line = f"{location_labels.get(lang,'📍 Location')}: {city_disp}"
if region_disp:
location_line += f", {region_disp}"
if country_disp:
location_line += f", {country_disp}"
tz = get_timezone_by_city(city)
if tz.startswith("❌"):
time_line = tz
else:
ct = get_current_time_in_timezone(tz)
time_line = f"{time_labels.get(lang,'🕒 Time')}: {ct}"
aq = get_air_quality(city, lang)
w = get_weather(city, lang)
return f"{location_line}\n\n{time_line}\n\n{aq}\n\n{w}"
if __name__ == "__main__":
gr.Interface(
fn=process_input,
inputs="text",
outputs="text",
title="Weather Assistant / Погодный ассистент",
description="Введите свой населенный пункт — получите время, погоду и качество воздуха."
).launch()