mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-21 06:47:42 +00:00
Partial fix for mypy issues
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user