Spaces:
Sleeping
Sleeping
| 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() |