mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-21 06:47:42 +00:00
Autogenerated type safety parameterisation of all methods
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from threading import Event, Thread
|
||||
from typing import Any
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
@@ -31,17 +34,17 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
Designed to run alongside KC2GProp even though they produce similar data. GIRO has more stations and includes LUF
|
||||
data, but is less reliable and often offline."""
|
||||
|
||||
def __init__(self, provider_config):
|
||||
def __init__(self, provider_config: dict[str, Any]) -> None:
|
||||
super().__init__("GIRO Ionosonde Data", provider_config)
|
||||
self._stations = self._load_stations()
|
||||
self._thread = None
|
||||
self._stop_event = Event()
|
||||
self._stations: list[dict[str, str]] = self._load_stations()
|
||||
self._thread: Thread | None = None
|
||||
self._stop_event: Event = Event()
|
||||
|
||||
# Pre-populate ionosonde_data with known station names for stations not already present,
|
||||
# so the station dropdown is available before the first poll. Does not overwrite existing
|
||||
# entries so KC2G cache data is preserved.
|
||||
existing = self._solar_conditions.ionosonde_data or {}
|
||||
new_entries = {
|
||||
existing: dict[str, Any] = self._solar_conditions.ionosonde_data or {}
|
||||
new_entries: dict[str, Any] = {
|
||||
s["ursi"]: {
|
||||
"ursi": s["ursi"],
|
||||
"name": s["name"],
|
||||
@@ -57,27 +60,27 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
self.update_data({"ionosonde_data": {**existing, **new_entries}})
|
||||
|
||||
@staticmethod
|
||||
def _load_stations():
|
||||
stations = []
|
||||
def _load_stations() -> list[dict[str, str]]:
|
||||
stations: list[dict[str, str]] = []
|
||||
with open(STATIONS_INDEX, newline="") as f:
|
||||
for row in csv.reader(f):
|
||||
if len(row) >= 2:
|
||||
stations.append({"ursi": row[0].strip(), "name": row[1].strip()})
|
||||
return stations
|
||||
|
||||
def start(self):
|
||||
def start(self) -> None:
|
||||
logger.info(f"Set up query of GIRO ionosonde data API every {POLL_INTERVAL} seconds.")
|
||||
self._thread = Thread(target=self._run, name="GIROIonosondeDataProvider", daemon=True)
|
||||
self._thread.start()
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
self._stop_event.set()
|
||||
if self._thread:
|
||||
self._thread.join(timeout=12)
|
||||
if self._thread.is_alive():
|
||||
logger.warning("GIRO ionosonde worker thread did not exit on time and will be killed.")
|
||||
|
||||
def _run(self):
|
||||
def _run(self) -> None:
|
||||
# Real interval at which we poll is the "once per hour" divided by the number of stations, so each one gets
|
||||
# polled once per hour, just not all at once
|
||||
interval = POLL_INTERVAL / len(self._stations)
|
||||
@@ -88,7 +91,7 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
if self._stop_event.wait(timeout=interval):
|
||||
break
|
||||
|
||||
def _poll_station(self, station):
|
||||
def _poll_station(self, station: dict[str, str]) -> None:
|
||||
ursi = station["ursi"]
|
||||
name = station["name"]
|
||||
try:
|
||||
@@ -139,7 +142,9 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
self.status = "Error"
|
||||
logger.exception(f"Exception fetching GIRO ionosonde data for {ursi} ({name})")
|
||||
|
||||
def _fetch_station_data(self, ursi, from_time, to_time):
|
||||
def _fetch_station_data(
|
||||
self, ursi: str, from_time: datetime, to_time: datetime
|
||||
) -> tuple[dict[float, float] | None, dict[float, float] | None, dict[float, float] | None]:
|
||||
"""Fetch foF2, MUF and LUF readings for a station. Returns (fof2_dict, muf_dict, luf_dict) keyed by UNIX timestamp."""
|
||||
|
||||
from_str = from_time.strftime("%Y.%m.%d+%H:%M:%S")
|
||||
@@ -159,12 +164,12 @@ class GIROIonosonde(SolarConditionsProvider):
|
||||
return None, None, None
|
||||
|
||||
@staticmethod
|
||||
def _parse_all(text):
|
||||
def _parse_all(text: str) -> tuple[dict[float, float], dict[float, float], dict[float, float]]:
|
||||
"""Parse web server response and return (fof2_dict, muf_dict, luf_dict) keyed by UNIX timestamp."""
|
||||
|
||||
fof2_data = {}
|
||||
muf_data = {}
|
||||
luf_data = {}
|
||||
fof2_data: dict[float, float] = {}
|
||||
muf_data: dict[float, float] = {}
|
||||
luf_data: dict[float, float] = {}
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
|
||||
Reference in New Issue
Block a user