Strict adherence to canonical ActivityNames, to avoid the problem where Spothole was giving out activities like ["Towers", "TOWERS"]. All uses of ActivityName should now be canonical not arbitrary strings, and attempts to convert unknown strings to activitynames will be logged for me to check out.

This commit is contained in:
Ian Renton
2026-09-25 09:16:53 +01:00
parent 7417140a3d
commit 760c2412d3
17 changed files with 144 additions and 145 deletions
+28 -42
View File
@@ -68,15 +68,6 @@ class GMA(HTTPSpotProvider):
# Filter out some weird mode strings
mode=Mode.from_name(source_spot["MODE"].upper()) if "<>" not in source_spot["MODE"] else None,
comment=source_spot["TEXT"],
activity_refs=[
ActivityRef(
id=source_spot["REF"],
activity="",
name=source_spot["NAME"],
latitude=lat,
longitude=lon,
)
],
time=time,
dx_latitude=lat,
dx_longitude=lon,
@@ -98,57 +89,52 @@ class GMA(HTTPSpotProvider):
and ref_response.text != "\n"
):
ref_info = ref_response.json()
if spot.activity_refs and ref_info and "reftype" in ref_info:
if ref_info and "reftype" in ref_info:
activity: ActivityName | None = None
ref_type: ActivityRefType | None = None
match ref_info["reftype"]:
case "Summit":
# Summits are a bit complicated, they can be SOTA or GMA depending on the
# separate "sota" field:
if "sota" in ref_info and ref_info["sota"] != "":
spot.activity_refs[0].activity = ActivityName.SOTA
spot.activity_refs[0].ref_type = ActivityRefType.SUMMIT
spot.add_activity(ActivityName.SOTA)
activity, ref_type = ActivityName.SOTA, ActivityRefType.SUMMIT
else:
spot.activity_refs[0].activity = ActivityName.GMA
spot.activity_refs[0].ref_type = ActivityRefType.SUMMIT
spot.add_activity(ActivityName.GMA)
activity, ref_type = ActivityName.GMA, ActivityRefType.SUMMIT
case "POTA":
spot.activity_refs[0].activity = ActivityName.POTA
spot.activity_refs[0].ref_type = ActivityRefType.PARK
spot.add_activity(ActivityName.POTA)
activity, ref_type = ActivityName.POTA, ActivityRefType.PARK
case "WWFF":
spot.activity_refs[0].activity = ActivityName.WWFF
spot.activity_refs[0].ref_type = ActivityRefType.PARK
spot.add_activity(ActivityName.WWFF)
activity, ref_type = ActivityName.WWFF, ActivityRefType.PARK
case "IOTA Island":
spot.activity_refs[0].activity = ActivityName.IOTA
spot.activity_refs[0].ref_type = ActivityRefType.ISLAND
spot.add_activity(ActivityName.IOTA)
activity, ref_type = ActivityName.IOTA, ActivityRefType.ISLAND
case "GMA Island":
spot.activity_refs[0].activity = ActivityName.GMA_ISLANDS
spot.activity_refs[0].ref_type = ActivityRefType.ISLAND
spot.add_activity(ActivityName.GMA_ISLANDS)
activity, ref_type = ActivityName.GMA_ISLANDS, ActivityRefType.ISLAND
case "Lighthouse (ILLW)":
spot.activity_refs[0].activity = ActivityName.ILLW
spot.activity_refs[0].ref_type = ActivityRefType.LIGHTHOUSE
spot.add_activity(ActivityName.ILLW)
activity, ref_type = ActivityName.ILLW, ActivityRefType.LIGHTHOUSE
case "Lighthouse (ARLHS)":
spot.activity_refs[0].activity = ActivityName.ARLHS
spot.activity_refs[0].ref_type = ActivityRefType.LIGHTHOUSE
spot.add_activity(ActivityName.ARLHS)
activity, ref_type = ActivityName.ARLHS, ActivityRefType.LIGHTHOUSE
case "Castle":
spot.activity_refs[0].activity = ActivityName.WCA
spot.activity_refs[0].ref_type = ActivityRefType.CASTLE
spot.add_activity(ActivityName.WCA)
activity, ref_type = ActivityName.WCA, ActivityRefType.CASTLE
case "Mill":
spot.activity_refs[0].activity = ActivityName.MOTA
spot.activity_refs[0].ref_type = ActivityRefType.MILL
spot.add_activity(ActivityName.MOTA)
activity, ref_type = ActivityName.MOTA, ActivityRefType.MILL
case _:
logger.warning(
f"GMA spot found with ref type {ref_info['reftype']}, developer needs to add support for this!"
)
spot.activity_refs[0].activity = ref_info["reftype"]
spot.add_activity(ref_info["reftype"])
# Now we know the activity, add the reference to the spot. If it's an activity we
# don't know, there's no ActivityName for it, so we can't add a reference.
if activity is not None:
spot.activity_refs = [
ActivityRef(
id=source_spot["REF"],
activity=activity,
ref_type=ref_type,
name=source_spot["NAME"],
latitude=lat,
longitude=lon,
)
]
spot.add_activity(activity)
elif not ref_response.from_cache:
if not ref_response.ok:
+7 -20
View File
@@ -6,6 +6,7 @@ from typing import ClassVar
import pytz
import requests
from core.activity_utils import get_activity_by_name
from core.constants import HTTP_HEADERS
from core.enums import ActivityName, Mode
from data.activity_ref import ActivityRef
@@ -67,16 +68,19 @@ class ParksNPeaks(HTTPSpotProvider):
# Record activity information
activity = source_spot["actClass"].upper()
found_activity = get_activity_by_name(activity)
ref_id = source_spot["actSiteID"]
if activity:
spot.add_activity(activity)
if found_activity is not None:
spot.add_activity(found_activity.name)
if ref_id:
# We can only add a reference if we know the activity it's for
if ref_id and found_activity is not None:
activity_refs = [
ActivityRef(
id=ref_id,
activity=activity,
activity=found_activity.name,
# Free text location is not present in all spots, so only add it if it's set
name=source_spot["actLocation"]
if "actLocation" in source_spot and source_spot["actLocation"] != ""
@@ -96,23 +100,6 @@ class ParksNPeaks(HTTPSpotProvider):
):
spot.comment = source_spot["actLocation"]
# Log a warning for the developer if PnP gives us an unknown programme we've never seen before
if activity not in [
ActivityName.POTA,
ActivityName.SOTA,
ActivityName.WWFF,
ActivityName.HEMA,
ActivityName.SIOTA,
ActivityName.ZLOTA,
ActivityName.KRMNPA,
ActivityName.SANPCPA,
ActivityName.LLOTA,
ActivityName.QRP,
]:
logger.warning(
f"PNP spot found with activity {activity}, developer needs to add support for this!"
)
# Add new spot to the list
new_spots.append(spot)
return new_spots
+20 -11
View File
@@ -1,13 +1,17 @@
import json
import logging
from datetime import datetime
import pytz
from core.enums import Mode
from core.activity_utils import get_activity_by_name
from core.enums import ActivityName, Mode
from data.activity_ref import ActivityRef
from data.spot import Spot
from providers.spot.websocket_spot_provider import WebsocketSpotProvider
logger = logging.getLogger(__name__)
class XOTA(WebsocketSpotProvider):
"""Spot provider for servers based on the "xOTA" software at https://github.com/nischu/xOTA/
@@ -17,12 +21,17 @@ class XOTA(WebsocketSpotProvider):
is why we also provide an activity_ref_prefix in our config. This is applied to the reference ID, so e.g. "T-01"
at C3 might become "C3 T-01". This allows us to provide location lookups for TOTA at several conferences."""
ACTIVITY = None
ACTIVITY: ActivityName | None = None
def __init__(self, provider_config):
name = provider_config.get("name", "xOTA")
super().__init__(name, provider_config, provider_config["url"])
self.ACTIVITY = str(provider_config["activity"]) if "activity" in provider_config else None
found_activity = get_activity_by_name(provider_config.get("activity"))
self.ACTIVITY = found_activity.name if found_activity else None
if not self.ACTIVITY:
logger.error(
"XOTA provider has no activity reference, this is a config problem - your config needs to specify a known activity type!"
)
self._activity_ref_prefix = (
str(provider_config["activity_ref_prefix"]) if "activity_ref_prefix" in provider_config else ""
)
@@ -31,20 +40,20 @@ class XOTA(WebsocketSpotProvider):
string = b.decode("utf-8")
source_spot = json.loads(string)
ref_id = f"{self._activity_ref_prefix} {source_spot['reference']['title']}"
activity = self.ACTIVITY
activities = []
activity_refs = []
if activity is not None:
activities = [activity]
activity_refs = [ActivityRef(id=ref_id, activity=activity, url=source_spot["reference"]["website"])]
spot = Spot(
source=self.name,
source_id=source_spot["id"],
dx_call=source_spot["stationCallSign"].upper(),
freq=float(source_spot["freq"]) * 1000,
mode=Mode.from_name(source_spot["mode"].upper()),
activities=[self.ACTIVITY] if self.ACTIVITY else [],
activity_refs=[
ActivityRef(
id=ref_id,
activity=self.ACTIVITY or "",
url=source_spot["reference"]["website"],
)
],
activities=activities,
activity_refs=activity_refs,
time=datetime.now(pytz.UTC).timestamp(),
qrt=source_spot["state"] != "active",
)