Partial fix for mypy issues

This commit is contained in:
Ian Renton
2026-09-20 20:42:16 +01:00
parent 93ea27510f
commit 203758fa2d
25 changed files with 244 additions and 147 deletions
@@ -4,6 +4,9 @@ from typing import Any
import requests
from fastkml import kml
from fastkml.containers import Document, Folder
from fastkml.features import Placemark
from fastkml.geometry import Point
from pyhamtools.locator import latlong_to_locator
from core.enums import ActivityRefType
@@ -26,13 +29,19 @@ class ParksNPeaksKMLActivityRefDataProvider(FileDownloadActivityRefDataProvider)
def _http_response_to_data(self, http_response: requests.Response) -> list[ActivityRef]:
new_data: list[ActivityRef] = []
k = kml.KML.from_string(http_response.content)
# KML content may carry an XML encoding declaration, which lxml's parser (used internally here) refuses to
# accept as a decoded str, so bytes must be passed even though the type stub only declares str.
k = kml.KML.from_string(http_response.content) # type: ignore[arg-type]
for document in k.features:
# noinspection unresolved-references
if not isinstance(document, Document):
continue
for folder in document.features:
# noinspection unresolved-references
if not isinstance(folder, Folder):
continue
for placemark in folder.features:
if not isinstance(placemark, Placemark):
continue
description = placemark.description or ""
match = self.REF_PATTERN.search(description)
if not match:
@@ -40,6 +49,9 @@ class ParksNPeaksKMLActivityRefDataProvider(FileDownloadActivityRefDataProvider)
continue
ref_id = match.group(0)
if not isinstance(placemark.geometry, Point):
# Not a point location (e.g. a boundary polygon) - skip it, we can't get a single lat/lon from it
continue
longitude, latitude = placemark.geometry.x, placemark.geometry.y
ref = ActivityRef(
+1
View File
@@ -39,6 +39,7 @@ class AlertProvider:
self._add_alert(alert)
def _add_alert(self, alert: Alert) -> None:
assert alert.id is not None, "infer_missing() always assigns an id"
if not alert.expired():
self._alerts.set(alert.id, alert)
+2
View File
@@ -135,7 +135,9 @@ class QRZ(APIQueryCallsignDataProvider):
# functions can't deal with multiple calls this way.
if isinstance(data, list):
data = data[0]
assert isinstance(data, dict)
callsign = data["call"]
assert isinstance(data, dict)
# Get a name
name = None
+2
View File
@@ -128,6 +128,8 @@ class ParksNPeaks(HTTPSpotProvider):
raise ValueError(
"Parks N Peaks user ID and API key are required. Get yours from your Parks N Peaks account."
)
if not spot.freq:
raise RuntimeError("The Parks N Peaks API requires a frequency to be set.")
ref_id = spot.sig_refs[0].id if spot.sig_refs else ""
body = {
"actClass": spot.sig or "",
+2
View File
@@ -64,6 +64,8 @@ class POTA(HTTPSpotProvider):
def submit_spot(self, spot: Spot, credentials: dict[str, str]) -> None:
sig_ref = spot.sig_refs[0].id if spot.sig_refs else None
if sig_ref:
if not spot.freq:
raise RuntimeError("The POTA API requires a frequency to be set.")
body = {
"activator": spot.dx_call,
"spotter": spot.de_call,
+7 -4
View File
@@ -94,21 +94,24 @@ class SOTA(HTTPSpotProvider):
raise ValueError("SOTA API tokens are required. Please log into SOTA in order to spot to it.")
sig_ref = spot.sig_refs[0].id if spot.sig_refs else ""
if sig_ref:
if not spot.freq:
raise ValueError("SOTA API requires a frequency to be set.")
# Split reference into association and summit codes
ref_split = sig_ref.split("/")
# Figure out a valid mode. Borrowed this from PoLo :)
# https://github.com/ham2k/app-polo/blob/main/src/extensions/activities/sota/SOTAPostSelfSpot.js
mode = spot.mode
if mode and mode not in self.VALID_MODES:
mode = "Data"
mode_str = spot.mode.value if spot.mode else ""
if spot.mode and spot.mode not in self.VALID_MODES:
mode_str = "Data"
body = {
"activatorCallsign": spot.dx_call,
"associationCode": ref_split[0],
"summitCode": ref_split[1],
"frequency": spot.freq / 1000000.0,
"mode": mode or "",
"mode": mode_str,
"callsign": spot.de_call,
"comments": spot.comment or "",
"type": "TEST", # todo replatce with NORMAL/QRT once testing complete
+4 -3
View File
@@ -37,12 +37,12 @@ class SpotProvider:
# off to SSE listeners.
spots = sorted(spots, key=lambda s: s.time if s and s.time else 0)
for spot in spots:
if datetime.fromtimestamp(spot.time, pytz.UTC) > self.last_spot_time:
if datetime.fromtimestamp(spot.time or 0, pytz.UTC) > self.last_spot_time:
# Fill in any blanks and add to the list
spot.infer_missing()
self._add_spot(spot)
if spots:
self.last_spot_time = datetime.fromtimestamp(max(s.time for s in spots), pytz.UTC)
self.last_spot_time = datetime.fromtimestamp(max(s.time or 0 for s in spots), pytz.UTC)
def _submit(self, spot: Spot) -> None:
"""Submit a single spot retrieved from the provider. This will be added to the list regardless of its age. Spots
@@ -52,9 +52,10 @@ class SpotProvider:
# Fill in any blanks and add to the list
spot.infer_missing()
self._add_spot(spot)
self.last_spot_time = datetime.fromtimestamp(spot.time, pytz.UTC)
self.last_spot_time = datetime.fromtimestamp(spot.time or 0, pytz.UTC)
def _add_spot(self, spot: Spot) -> None:
assert spot.id is not None, "infer_missing() always assigns an id"
if not spot.expired():
self._spots.set(spot.id, spot)
+11 -8
View File
@@ -90,22 +90,25 @@ class Tiles(HTTPSpotProvider):
def submit_spot(self, spot: Spot, credentials: dict[str, str]) -> None:
# Tiles on the air currently only supports *self* spots
if spot.dx_call == spot.de_call:
if not spot.freq:
raise RuntimeError("The Tiles on the Air API requires a frequency to be set.")
# Figure out a valid mode. Borrowed this from PoLo :)
# https://github.com/ham2k/app-polo/blob/main/src/extensions/activities/sota/SOTAPostSelfSpot.js
if spot.mode:
mode = spot.mode
if mode not in self.VALID_MODES:
if mode == "OLIVIA":
mode = "Olivia"
elif mode == "JS8":
mode = "JS8Call"
mode_str: str = spot.mode.value
if spot.mode not in self.VALID_MODES:
if spot.mode == "OLIVIA":
mode_str = "Olivia"
elif spot.mode == "JS8":
mode_str = "JS8Call"
else:
mode = "Other"
mode_str = "Other"
body = {
"call_sign": spot.dx_call,
"frequency": str(spot.freq / 1000000.0),
"mode": mode or "",
"mode": mode_str or "",
"grid": spot.dx_grid or "",
"comment": spot.comment or "",
"lat": spot.dx_latitude or None,