profile_save. Route handler or application helper. This docstring was expanded to make future maintenance easier. Returns: Varies.
()
| 10912 | raw = cached[1] if cached and now - cached[0] < _WEATHER_TTL else {} |
| 10913 | if not raw: |
| 10914 | params = { |
| 10915 | "latitude": f"{latitude:.6f}", "longitude": f"{longitude:.6f}", "timezone": "auto", "forecast_days": 14, |
| 10916 | "temperature_unit": "celsius", "wind_speed_unit": "kmh", "precipitation_unit": "mm", |
| 10917 | "current": "temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,rain,showers,snowfall,weather_code,cloud_cover,surface_pressure,wind_speed_10m,wind_direction_10m,wind_gusts_10m", |
| 10918 | "daily": "weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,precipitation_sum,precipitation_probability_max,wind_speed_10m_max,wind_gusts_10m_max,uv_index_max", |
| 10919 | } |
| 10920 | try: |
| 10921 | raw = _weather_json("https://api.open-meteo.com/v1/forecast?" + urllib.parse.urlencode(params)) |
| 10922 | with _WEATHER_LOCK: |
| 10923 | _WEATHER_CACHE[cache_key] = (time.time(), raw) |
| 10924 | if len(_WEATHER_CACHE) > 250: |
| 10925 | oldest = sorted(_WEATHER_CACHE.items(), key=lambda item: item[1][0])[:50] |
| 10926 | for old_key, _ in oldest: |
| 10927 | _WEATHER_CACHE.pop(old_key, None) |
| 10928 | except Exception as exc: |
| 10929 | log.exception("Weather forecast failed: %s", exc) |
| 10930 | return jsonify({"ok": False, "error": "weather_unavailable"}), 502 |
| 10931 | current = raw.get("current") if isinstance(raw.get("current"), dict) else {} |
| 10932 | description, icon = _weather_code(current.get("weather_code"), lang, bool(current.get("is_day", 1))) |
| 10933 | normalized_current = { |
| 10934 | "time": current.get("time"), "temperature": current.get("temperature_2m"), "apparent_temperature": current.get("apparent_temperature"), |
| 10935 | "humidity": current.get("relative_humidity_2m"), "precipitation": current.get("precipitation"), "rain": current.get("rain"), |
| 10936 | "showers": current.get("showers"), "snowfall": current.get("snowfall"), "cloud_cover": current.get("cloud_cover"), |
| 10937 | "pressure": current.get("surface_pressure"), "wind_speed": current.get("wind_speed_10m"), "wind_direction": current.get("wind_direction_10m"), |
| 10938 | "wind_gusts": current.get("wind_gusts_10m"), "weather_code": current.get("weather_code"), "description": description, "icon": icon, |
| 10939 | } |
| 10940 | return jsonify({ |
| 10941 | "ok": True, |
| 10942 | "location": {"label": label, "latitude": latitude, "longitude": longitude, "timezone": raw.get("timezone") or "", "elevation": raw.get("elevation")}, |
| 10943 | "current": normalized_current, |
| 10944 | "daily": _weather_daily_rows(raw, lang), |
| 10945 | "generated_at": now_z(), |
| 10946 | "source": "Open-Meteo", |
| 10947 | }) |
| 10948 | |
| 10949 | |
| 10950 | # --------------------------- |
| 10951 | # World News (RSS aggregation; cached) |
| 10952 | # --------------------------- |
| 10953 | |
| 10954 | # Cache is per-language (English/Greek), refreshed every _NEWS_TTL seconds. |
| 10955 | _NEWS_CACHE = {"ts": {"en": 0.0, "el": 0.0}, "items": {"en": [], "el": []}} # in-memory cache |
| 10956 | _NEWS_LOCK = threading.Lock() |
| 10957 | _NEWS_TTL = 20 * 60 # seconds |
| 10958 | |
| 10959 | # Topics the user asked for (plus similar). We use Google News RSS search to get "worldwide" coverage. |
| 10960 | NEWS_TOPICS = [ |
| 10961 | ("cybersecurity", {"en": "Cybersecurity", "el": "Κυβερνοασφάλεια"}, |
| 10962 | {"en": "cybersecurity OR infosec OR \"data breach\"", "el": "κυβερνοασφάλεια OR ασφάλεια πληροφοριών OR \"διαρροή δεδομένων\""}), |
| 10963 | |
| 10964 | ("hacking", {"en": "Hacking", "el": "Χάκινγκ"}, |
| 10965 | {"en": "hacking OR hacker OR \"zero day\"", "el": "χάκινγκ OR χάκερ OR \"μηδενικής ημέρας\""}), |
| 10966 | |
| 10967 | ("termux", {"en": "Termux", "el": "Termux"}, |
| 10968 | {"en": "Termux", "el": "Termux"}), |
| 10969 | |
| 10970 | ("dedsec", {"en": "DedSec Project", "el": "DedSec Project"}, |
| 10971 | {"en": "\"DedSec\" OR \"DedSec Project\"", "el": "\"DedSec\" OR \"DedSec Project\""}), |
nothing calls this directly
no test coverage detected