Autogenerated type safety parameterisation of all methods

This commit is contained in:
Ian Renton
2026-09-20 20:02:19 +01:00
parent 6037e742cc
commit 324dd1414b
132 changed files with 1228 additions and 706 deletions
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
from core.enums import ActivityName, ActivityRefType, ActivityType
from data.activity import Activity
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
from dataclasses import dataclass, field
from core.enums import ActivityName, ActivityRefType, ActivityType
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
from dataclasses import dataclass
from core.enums import ActivityRefType
+10 -6
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import hashlib
import json
import logging
@@ -11,6 +13,8 @@ from core.activity_utils import get_icon_for_activity
from core.call_lookup_helper import get_call_info
from core.enums import Continent
from core.utils import get_flag_for_dxcc
from data.activity_ref import ActivityRef
from data.lookup_credentials import LookupCredentials
logger = logging.getLogger(__name__)
@@ -25,9 +29,9 @@ class Alert:
# DX (alerting) operator info
# Callsigns of the operators that has been alerted
dx_calls: list | None = None
dx_calls: list[str] | None = None
# Names of the operators that has been alerted
dx_names: list | None = None
dx_names: list[str | None] | None = None
# Country of the DX operator
dx_country: str | None = None
# Country flag of the DX operator
@@ -64,7 +68,7 @@ class Alert:
sig: str | None = None
# Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. Still named
# "sig_refs" for API backwards compatibility.
sig_refs: list = field(default_factory=list)
sig_refs: list[ActivityRef] = field(default_factory=list)
# Timing info
@@ -87,7 +91,7 @@ class Alert:
# Icon to use when displaying this alert in the web UI. Chosen from the Font Awesome set.
icon: str | None = None
def infer_missing(self, credentials=None):
def infer_missing(self, credentials: LookupCredentials | None = None) -> None:
"""Infer missing parameters where possible"""
try:
@@ -160,12 +164,12 @@ class Alert:
except Exception:
logger.exception("Exception while inferring missing data from spot")
def to_json(self):
def to_json(self) -> str:
"""JSON serialise"""
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
def expired(self):
def expired(self) -> bool:
"""Decide if this alert has expired (in which case it should not be added to the system in the first place, and not
returned by the web server if later requested, and removed by the cleanup functions). "Expired" is defined as
either having an end_time in the past, or if it only has a start_time, then that start time was more than 3 hours
+3 -1
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
from dataclasses import dataclass
from core.enums import Continent, LocationSourceForCallsign
@@ -40,7 +42,7 @@ class Callsign:
# Location source
location_source: LocationSourceForCallsign | None = None
def fully_populated(self):
def fully_populated(self) -> bool:
"""Utility method to indicate that the callsign data is fully populated. Multiple providers can return data for
a callsign, and we try them in sequence until we have all the data we can, in which case there's no point
querying any other providers."""
+5 -1
View File
@@ -1,5 +1,9 @@
from __future__ import annotations
from dataclasses import dataclass
from tornado.httputil import HTTPHeaders
@dataclass
class LookupCredentials:
@@ -13,7 +17,7 @@ class LookupCredentials:
hamqth_session_id: str = "" # alternative to username/password
def extract_credentials(headers):
def extract_credentials(headers: HTTPHeaders) -> LookupCredentials | None:
"""Build a LookupCredentials from HTTP request headers; returns None if no usable credentials are present."""
creds = LookupCredentials(
qrz_username=headers.get("X-QRZ-Username", ""),
+16 -11
View File
@@ -1,5 +1,10 @@
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import Any, TypeVar
T = TypeVar("T")
# Lookup tables for derived text descriptions.
# Each threshold-based table is a list of (min_value, description) pairs in descending order;
@@ -71,7 +76,7 @@ ELECTRON_FLUX_DESCRIPTIONS = [
]
def _xray_blackout_scale(xray):
def _xray_blackout_scale(xray: str | None) -> int:
"""Return the NOAA Radio Blackout scale number (R0-R5) for the given X-ray flux class string
(e.g. "M4.5", "X12")."""
@@ -93,7 +98,7 @@ def _xray_blackout_scale(xray):
return 0
def _lookup_by_threshold(value, table, default=None):
def _lookup_by_threshold(value: int | None, table: list[tuple[int, T]], default: T | None = None) -> T | None:
"""Return the description from a threshold table for the given numeric value.
The table is a list of (min_value, description) pairs in descending order."""
@@ -150,20 +155,20 @@ class SolarConditions:
# Geomagnetic background noise level, e.g. "S0", "S1", "S2"
geomag_noise: str | None = None
# HF band propagation conditions, keyed by "{band}-{time}" e.g. "80m-40m-day"
hf_conditions: dict | None = None
hf_conditions: dict[str, str] | None = None
# VHF propagation conditions, keyed by condition name
vhf_conditions: dict | None = None
vhf_conditions: dict[str, str | None] | None = None
# NOAA Kp index 3-day forecast, keyed by UNIX timestamp of the start of each 3-hour UTC period
k_index_forecast: dict | None = None
k_index_forecast: dict[float, float] | None = None
# NOAA Solar Radiation Storm (S1 or greater) probability forecast, keyed by UNIX timestamp of start of day UTC
solar_storm_forecast: dict | None = None
solar_storm_forecast: dict[float, int] | None = None
# NOAA Radio Blackout (R1-R2) probability forecast, keyed by UNIX timestamp of start of day UTC
blackout_forecast_r1r2: dict | None = None
blackout_forecast_r1r2: dict[float, int] | None = None
# NOAA Radio Blackout (R3 or greater) probability forecast, keyed by UNIX timestamp of start of day UTC
blackout_forecast_r3_or_greater: dict | None = None
blackout_forecast_r3_or_greater: dict[float, int] | None = None
# Ionosonde measurements, dict keyed by URSI code, values are dicts with keys: ursi, name, fof2, muf, luf,
# band_states. Populated by GIROIonosonde or KC2GProp providers.
ionosonde_data: dict | None = None
ionosonde_data: dict[str, Any] | None = None
# Derived values (populated by infer_descriptions())
# HF radio blackout risk description, derived from xray
@@ -183,7 +188,7 @@ class SolarConditions:
# Electron flux description, derived from electron_flux
electron_flux_desc: str | None = None
def infer_descriptions(self):
def infer_descriptions(self) -> None:
"""Populate derived text description fields from the current numeric/raw field values."""
if self.xray and len(self.xray) > 0:
@@ -196,7 +201,7 @@ class SolarConditions:
self.band_conditions_desc = _lookup_by_threshold(self.sfi, BAND_CONDITIONS_DESCRIPTIONS)
self.electron_flux_desc = _lookup_by_threshold(self.electron_flux, ELECTRON_FLUX_DESCRIPTIONS)
def to_json(self):
def to_json(self) -> str:
"""JSON serialise. Dict key order is insertion order (Python 3.7+ guarantee), so callers receive
fields in a predictable, logical sequence without relying on sort_keys."""
+9 -6
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import hashlib
import json
import logging
@@ -32,6 +34,7 @@ from core.utils import (
)
from data.activities import ACTIVITIES
from data.activity_ref import ActivityRef
from data.lookup_credentials import LookupCredentials
logger = logging.getLogger(__name__)
@@ -129,7 +132,7 @@ class Spot:
sig: str | None = None
# Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. Still named
# "sig_refs" for API backwards compatibility.
sig_refs: list = field(default_factory=list)
sig_refs: list[ActivityRef] = field(default_factory=list)
# Timing info
@@ -156,7 +159,7 @@ class Spot:
# Icon to use when displaying this spot in the web UI. Chosen from the Font Awesome set.
icon: str | None = None
def __post_init__(self):
def __post_init__(self) -> None:
"""Normalise fields that don't survive a plain dict to Spot conversion. This is used in the "add spot" API
endpoint where the client is submitting JSON, and we want to recreate a full Spot object, including nested
objects such as the sig_refs list.."""
@@ -167,7 +170,7 @@ class Spot:
for activity_ref in self.sig_refs
]
def infer_missing(self, credentials=None):
def infer_missing(self, credentials: LookupCredentials | None = None) -> None:
"""Infer missing parameters where possible"""
try:
@@ -538,12 +541,12 @@ class Spot:
except Exception:
logger.exception("Exception while inferring missing data from spot")
def to_json(self):
def to_json(self) -> str:
"""JSON serialise"""
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
def _append_activity_ref_if_missing(self, new_activity_ref):
def _append_activity_ref_if_missing(self, new_activity_ref: ActivityRef) -> None:
"""Append an activity ref to the list, so long as it's not already there."""
new_activity_ref.id = new_activity_ref.id.strip().upper()
@@ -555,7 +558,7 @@ class Spot:
return
self.sig_refs.append(new_activity_ref)
def expired(self):
def expired(self) -> bool:
"""Decide if this spot has expired (in which case it should not be added to the system in the first place, and not
returned by the web server if later requested, and removed by the cleanup functions). "Expired" is defined as
either having a time further ago than the server's MAX_SPOT_AGE. If it somehow doesn't have a time either, it is