import os import requests from loguru import logger from dotenv import load_dotenv load_dotenv() # ── City config ────────────────────────────────────────────────────────────── # These cities have active temperature markets on Polymarket TARGET_CITIES = { "New York": {"lat": 40.7128, "lon": -74.0060, "apify": "New York, New York"}, "London": {"lat": 51.5074, "lon": -0.1278, "apify": "London, England"}, "Miami": {"lat": 25.7617, "lon": -80.1918, "apify": "Miami, Florida"}, "Tokyo": {"lat": 35.6762, "lon": 139.6503, "apify": "Tokyo, Japan"}, "Los Angeles": {"lat": 34.0522, "lon": -118.2437, "apify": "Los Angeles, California"}, } # ── Apify fetcher ──────────────────────────────────────────────────────────── def fetch_weather_apify(cities: dict = TARGET_CITIES) -> dict: """ Fetch 10-day weather forecast via Apify's weather-scraper actor. Returns a dict keyed by city name. """ token = os.getenv("APIFY_API_TOKEN") if not token: raise ValueError("APIFY_API_TOKEN not set in .env") from apify_client import ApifyClient client = ApifyClient(token) locations = [info["apify"] for info in cities.values()] logger.info(f"Fetching Apify weather for: {locations}") run_input = { "locations": locations, "timeFrame": "ten_day", "units": "imperial", "maxItems": len(locations) * 10, "proxyConfiguration": {"useApifyProxy": True}, } run = client.actor("epctex/weather-scraper").call(run_input=run_input) items = list(client.dataset(run["defaultDatasetId"]).iterate_items()) # Map back by city name results = {} for city_name in cities: city_data = [ item for item in items if cities[city_name]["apify"].split(",")[0].lower() in str(item.get("city", "")).lower() ] if city_data: results[city_name] = city_data logger.success(f" ✓ {city_name}: {len(city_data)} forecast records") else: logger.warning(f" ✗ {city_name}: no data from Apify, falling back") results[city_name] = _fetch_openmeteo_single(city_name, cities[city_name]) return results # ── Open-Meteo fallback ────────────────────────────────────────────────────── def _fetch_openmeteo_single(city_name: str, city_info: dict) -> list: """ Free fallback: Open-Meteo gives hourly/daily temp forecasts. Returns list of daily dicts matching Apify output shape. """ url = "https://api.open-meteo.com/v1/forecast" params = { "latitude": city_info["lat"], "longitude": city_info["lon"], "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,weathercode", "temperature_unit": "fahrenheit", "forecast_days": 10, "timezone": "auto", } resp = requests.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json()["daily"] records = [] for i, date in enumerate(data["time"]): records.append({ "city": city_name, "date": date, "temperature": f"{data['temperature_2m_max'][i]}/{data['temperature_2m_min'][i]}", "high_f": data["temperature_2m_max"][i], "low_f": data["temperature_2m_min"][i], "precipitation": data["precipitation_sum"][i], "source": "open-meteo", }) logger.info(f" Open-Meteo fallback: {city_name} → {len(records)} days") return records def fetch_weather_openmeteo(cities: dict = TARGET_CITIES) -> dict: """Fetch all cities directly from Open-Meteo (no Apify needed).""" results = {} for city_name, info in cities.items(): results[city_name] = _fetch_openmeteo_single(city_name, info) return results def get_today_high(city_name: str, weather_data: dict) -> float | None: """Extract today's high temperature for a city from fetched weather data.""" records = weather_data.get(city_name, []) if not records: return None today = records[0] # First record = today # Handle Apify format: "temperature": "24/16" if isinstance(today.get("temperature"), str) and "/" in today["temperature"]: try: return float(today["temperature"].split("/")[0]) except ValueError: pass # Handle Open-Meteo format return today.get("high_f") def summarize_weather(weather_data: dict) -> str: """ Returns a human-readable weather summary string for the LLM agent prompt. """ lines = ["=== Current Weather Forecasts ==="] for city, records in weather_data.items(): if not records: lines.append(f" {city}: No data available") continue today = records[0] temp = today.get("temperature", "N/A") if today.get("high_f"): temp = f"{today['high_f']}°F high / {today.get('low_f', '?')}°F low" forecast = today.get("forecast", today.get("weathercode", "N/A")) lines.append(f" {city}: {temp} | Conditions: {forecast}") return "\n".join(lines)