From d91fa706556039fc5dc0bfe72f4c439b8b3594a2 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Thu, 24 Sep 2026 07:09:37 +0100 Subject: [PATCH] First attempt at converting "sig" to "activity" for v3 --- config-example.yml | 14 +- core/activity_lookup_helper.py | 6 +- core/config.py | 12 ++ core/constants.py | 2 +- core/data_providers.py | 10 +- core/enums.py | 2 +- core/status_reporter.py | 8 +- data/activities.py | 86 ++++----- data/activity.py | 18 +- data/activity_ref.py | 4 +- data/alert.py | 21 ++- data/spot.py | 99 +++++----- .../activity_ref_data_provider.py | 11 +- providers/activityrefdata/arlhs.py | 2 +- providers/activityrefdata/cota.py | 2 +- providers/activityrefdata/dce.py | 7 +- providers/activityrefdata/defe.py | 7 +- providers/activityrefdata/dme.py | 2 +- providers/activityrefdata/dmue.py | 7 +- providers/activityrefdata/dmve.py | 6 +- providers/activityrefdata/dtmba.py | 4 +- providers/activityrefdata/fea.py | 12 +- ...ile_download_activity_ref_data_provider.py | 30 ++-- providers/activityrefdata/gma.py | 2 +- providers/activityrefdata/illw.py | 2 +- providers/activityrefdata/iota.py | 2 +- providers/activityrefdata/llota.py | 2 +- .../local_file_activity_ref_data_provider.py | 10 +- providers/activityrefdata/mota.py | 2 +- providers/activityrefdata/pga.py | 2 +- .../pnp_kml_activity_ref_data_provider.py | 6 +- providers/activityrefdata/pota.py | 2 +- providers/activityrefdata/siota.py | 2 +- providers/activityrefdata/sota.py | 2 +- providers/activityrefdata/toilets.py | 2 +- providers/activityrefdata/towers.py | 2 +- providers/activityrefdata/wca.py | 2 +- providers/activityrefdata/wota.py | 2 +- providers/activityrefdata/wwbota.py | 2 +- providers/activityrefdata/wwff.py | 2 +- providers/activityrefdata/zlota.py | 2 +- providers/alert/bota.py | 4 +- providers/alert/hamsat.py | 6 +- providers/alert/ng3k.py | 2 +- providers/alert/parksnpeaks.py | 6 +- providers/alert/pota.py | 6 +- providers/alert/rsgb_ical_alert_provider.py | 2 +- providers/alert/sota.py | 6 +- providers/alert/wa7bnm.py | 2 +- providers/alert/wota.py | 2 +- providers/alert/wwff.py | 4 +- providers/spot/gma.py | 70 ++++---- providers/spot/hema.py | 6 +- providers/spot/llota.py | 6 +- providers/spot/parksnpeaks.py | 10 +- providers/spot/pota.py | 12 +- providers/spot/sota.py | 12 +- providers/spot/tiles.py | 6 +- providers/spot/towers.py | 6 +- providers/spot/wota.py | 10 +- providers/spot/wwbota.py | 6 +- providers/spot/wwff.py | 6 +- providers/spot/xota.py | 16 +- providers/spot/zlota.py | 6 +- pyproject.toml | 2 +- static/apidocs/openapi.yml | 116 +++++++----- static/js/add-spot.js | 26 +-- static/js/alerts.js | 28 +-- static/js/bands.js | 10 +- static/js/common.js | 33 +++- static/js/conditions.js | 4 +- static/js/map.js | 26 +-- static/js/spots.js | 24 +-- static/js/status.js | 8 +- templates/add-spot.html | 8 +- templates/help/usage/clients.html | 8 +- templates/help/usage/embedding.html | 6 +- templates/status.html | 2 +- webserver/handlers/api/addspot.py | 24 +-- webserver/handlers/api/alerts.py | 18 +- webserver/handlers/api/dxstats.py | 2 +- webserver/handlers/api/lookups.py | 18 +- webserver/handlers/api/options.py | 4 +- webserver/handlers/api/solar_conditions.py | 2 +- webserver/handlers/api/spots.py | 24 +-- webserver/handlers/api/status.py | 2 +- webserver/handlers/api/v1_addspot.py | 23 +-- webserver/handlers/api/v1_spots.py | 12 +- webserver/handlers/api/v2_compatibility.py | 169 ++++++++++++++++++ webserver/webserver.py | 87 +++++++-- 90 files changed, 822 insertions(+), 496 deletions(-) create mode 100644 webserver/handlers/api/v2_compatibility.py diff --git a/config-example.yml b/config-example.yml index 0cd0072..54d338d 100644 --- a/config-example.yml +++ b/config-example.yml @@ -130,25 +130,25 @@ spot_providers: url: "wss://39c3.totawatch.de/api/spot/live" # For the "XOTA" provider, an activity must be set manually here because xOTA is a generic backend for xOTA # programmes and so different URLs potentially provide different programmes. - sig: "Toilets" + activity: "Toilets" # For Toilets on the Air, we prefix the activity references (T-01 etc) with some characters that define the # conference: C3, EH or HOPE - so we can look up the correct locations in our database, because each conference # starts from T-01 but refers to a toilet in a different building (or continent!) - sig_ref_prefix: "C3" + activity_ref_prefix: "C3" - class: "XOTA" name: "EH23 TOTA" enabled: false url: "wss://eh23.totawatch.de/api/spot/live" - sig: "Toilets" - sig_ref_prefix: "EH" + activity: "Toilets" + activity_ref_prefix: "EH" - class: "XOTA" name: "HOPE26 TOTA" enabled: false url: "wss://hope-26.totawatch.de/api/spot/live" - sig: "Toilets" - sig_ref_prefix: "HOPE" + activity: "Toilets" + activity_ref_prefix: "HOPE" # Alert providers to use. Same setup as the spot providers list above. @@ -218,7 +218,7 @@ static_data_providers: # Activity reference data providers to use. These allow Spothole to download, for example, the WWFF directory that # maps WWFF park IDs to their name and location. -sig_ref_data_providers: +activity_ref_data_providers: - class: "POTA" enabled: true diff --git a/core/activity_lookup_helper.py b/core/activity_lookup_helper.py index 6b6ff78..1d28a1e 100644 --- a/core/activity_lookup_helper.py +++ b/core/activity_lookup_helper.py @@ -27,7 +27,7 @@ def get_activity_ref_info(activity_name, ref_id): ref_id = ref_id.replace(" ", "-") # Prepare the object to be returned - activity_ref = ActivityRef(sig=activity_name, id=ref_id) + activity_ref = ActivityRef(activity=activity_name, id=ref_id) # We can always get the reference type and the icon from the activity itself activity = get_activity_by_name(activity_name) @@ -141,11 +141,11 @@ def get_activity_ref_info(activity_name, ref_id): def populate_missing_activity_ref_info(activity_ref): """Look up details of an activity reference (e.g. POTA park) such as name, lat/lon, and grid. Takes in an - activity_ref object which must at minimum have a "sig" and an "id". The rest of the object will be populated + activity_ref object which must at minimum have an "activity" and an "id". The rest of the object will be populated and returned. Any data currently in the object will be kept, only missing data in the object will be populated if it can be determined.""" - lookup_data = get_activity_ref_info(activity_ref.sig, activity_ref.id) + lookup_data = get_activity_ref_info(activity_ref.activity, activity_ref.id) if lookup_data: # Copy new activity ref data into existing object where data was previously missing diff --git a/core/config.py b/core/config.py index 9218951..7940525 100644 --- a/core/config.py +++ b/core/config.py @@ -19,6 +19,18 @@ with open("config.yml") as f: config = yaml.safe_load(f) logger.info("Loaded config.") +# Warn about config keys that were renamed from "sig" to "activity" in Spothole v3, as these will otherwise be silently +# ignored. +if "sig_ref_data_providers" in config: + logger.warning( + 'Your config file contains "sig_ref_data_providers", which was renamed to "activity_ref_data_providers" in Spothole v3. Please update your config.yml, otherwise no activity reference data will be loaded.' + ) +for _entry in config.get("spot_providers", []): + if "sig" in _entry or "sig_ref_prefix" in _entry: + logger.warning( + f'Your config file contains "sig" or "sig_ref_prefix" for the {_entry.get("class")} spot provider. These were renamed to "activity" and "activity_ref_prefix" in Spothole v3. Please update your config.yml.' + ) + BASE_URL = config.get("base_url", "http://localhost:8080") MAX_SPOT_AGE = config.get("max_spot_age_sec", 3600) MAX_ALERT_AGE = config.get("max_alert_age_sec", 604800) diff --git a/core/constants.py b/core/constants.py index 6af12de..4b5b4fa 100644 --- a/core/constants.py +++ b/core/constants.py @@ -2,7 +2,7 @@ from core.config import SERVER_OWNER_CALLSIGN from data.band import Band # General software -SOFTWARE_VERSION = "2.2" +SOFTWARE_VERSION = "3.0-pre" # HTTP headers used for spot providers that use HTTP HTTP_HEADERS = {"User-Agent": f"Spothole v{SOFTWARE_VERSION} (operated by {SERVER_OWNER_CALLSIGN})"} diff --git a/core/data_providers.py b/core/data_providers.py index fe65a74..cd40349 100644 --- a/core/data_providers.py +++ b/core/data_providers.py @@ -15,7 +15,7 @@ class DataProviders: self.alert_providers = [] self.solar_condition_providers = [] self.static_data_providers = [] - self.sig_ref_data_providers = [] + self.activity_ref_data_providers = [] self.callsign_data_providers = [] self._startup_timers = [] @@ -28,8 +28,8 @@ class DataProviders: self.solar_condition_providers.append(create_provider_from_config("providers.solarconditions", entry)) for entry in config.get("static_data_providers", []): self.static_data_providers.append(create_provider_from_config("providers.staticdata", entry)) - for entry in config.get("sig_ref_data_providers", []): - self.sig_ref_data_providers.append(create_provider_from_config("providers.activityrefdata", entry)) + for entry in config.get("activity_ref_data_providers", []): + self.activity_ref_data_providers.append(create_provider_from_config("providers.activityrefdata", entry)) for entry in config.get("callsign_data_providers", []): self.callsign_data_providers.append(create_provider_from_config("providers.callsigndata", entry)) @@ -54,7 +54,7 @@ class DataProviders: 25.0, lambda: self.start_providers(self.solar_condition_providers, "solar condition"), ), - threading.Timer(30.0, lambda: self.start_providers(self.sig_ref_data_providers, "activity ref data")), + threading.Timer(30.0, lambda: self.start_providers(self.activity_ref_data_providers, "activity ref data")), ] for t in self._startup_timers: t.daemon = True @@ -72,7 +72,7 @@ class DataProviders: self.spot_providers + self.alert_providers + self.solar_condition_providers - + self.sig_ref_data_providers + + self.activity_ref_data_providers + self.static_data_providers + self.callsign_data_providers ) diff --git a/core/enums.py b/core/enums.py index ed498a4..7b23f57 100644 --- a/core/enums.py +++ b/core/enums.py @@ -77,7 +77,7 @@ class LocationSourceForSpot(str, Enum): """Where the location data came from in a spot.""" SPOT = "SPOT" - SIG_REF_LOOKUP = "SIG REF LOOKUP" + ACTIVITY_REF_LOOKUP = "ACTIVITY REF LOOKUP" GRID = "GRID" HOME_QTH = "HOME QTH" DXCC = "DXCC" diff --git a/core/status_reporter.py b/core/status_reporter.py index 58c8b36..3fd1ff2 100644 --- a/core/status_reporter.py +++ b/core/status_reporter.py @@ -111,9 +111,11 @@ class StatusReporter: } for p in DATA_PROVIDERS.static_data_providers ] - DATA_STORE.status.get()["sig_ref_data_providers"] = [ + # Remove the old name for this key if it's still present in status data persisted by Spothole v2 and earlier + DATA_STORE.status.get().pop("sig_ref_data_providers", None) + DATA_STORE.status.get()["activity_ref_data_providers"] = [ { - "sig_name": p.sig_name, + "activity_name": p.activity_name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() @@ -121,7 +123,7 @@ class StatusReporter: else 0, "reference_count": p.reference_count, } - for p in DATA_PROVIDERS.sig_ref_data_providers + for p in DATA_PROVIDERS.activity_ref_data_providers ] DATA_STORE.status.get()["callsign_data_providers"] = [ { diff --git a/data/activities.py b/data/activities.py index c65925d..c1b68c6 100644 --- a/data/activities.py +++ b/data/activities.py @@ -5,7 +5,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.CONTEST: Activity( name=ActivityName.CONTEST, description="Contest", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, # No sensible way to determine *which* contest, but if we set comment_names=["CONTEST"] then at least @@ -17,7 +17,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DXPEDITION: Activity( name=ActivityName.DXPEDITION, description="Radio expedition to a remote location", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, comment_names=["DXPEDITION"], @@ -27,7 +27,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.SATELLITE: Activity( name=ActivityName.SATELLITE, description="Amateur Radio Satellite", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, # Satellite "references" are the names of the satellites themselves. This is not an exhaustive list, it just # matches some of the most commonly used amateur radio satellites so they can be picked out of spot comments. has_refs=True, @@ -41,7 +41,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.EME: Activity( name=ActivityName.EME, description="Earth-Moon-Earth (Moonbounce)", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, comment_names=[], @@ -50,7 +50,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.AERONAUTICAL_MOBILE: Activity( name=ActivityName.AERONAUTICAL_MOBILE, description="Aeronautical Mobile", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, # Don't pick /AM out of comments, spot.py will handle picking it out of the callsign @@ -60,7 +60,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.MARITIME_MOBILE: Activity( name=ActivityName.MARITIME_MOBILE, description="Maritime Mobile", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, # Don't pick /MM out of comments, spot.py will handle picking it out of the callsign @@ -70,7 +70,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.QRP: Activity( name=ActivityName.QRP, description="Low power", - sig_type=ActivityType.TRADITIONAL, + activity_type=ActivityType.TRADITIONAL, has_refs=False, refs_globally_unique=False, comment_names=["QRP"], @@ -80,7 +80,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.POTA: Activity( name=ActivityName.POTA, description="Parks on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.PARK, @@ -92,7 +92,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.SOTA: Activity( name=ActivityName.SOTA, description="Summits on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.SUMMIT, @@ -104,7 +104,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WWFF: Activity( name=ActivityName.WWFF, description="World Wide Flora & Fauna", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.PARK, @@ -116,7 +116,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.GMA: Activity( name=ActivityName.GMA, description="Global Mountain Activity", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.SUMMIT, @@ -127,7 +127,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WWBOTA: Activity( name=ActivityName.WWBOTA, description="Worldwide Bunkers on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.BUNKER, @@ -138,7 +138,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.HEMA: Activity( name=ActivityName.HEMA, description="HuMPs Excluding Marilyns Award", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.SUMMIT, @@ -150,7 +150,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.IOTA: Activity( name=ActivityName.IOTA, description="Islands on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.ISLAND, @@ -161,7 +161,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.GMA_ISLANDS: Activity( name=ActivityName.GMA_ISLANDS, description="Global Mountain Activity - Islands", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.ISLAND, @@ -172,7 +172,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.ARLHS: Activity( name=ActivityName.ARLHS, description="Amateur Radio Lighthouse Society", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.LIGHTHOUSE, @@ -183,7 +183,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.ILLW: Activity( name=ActivityName.ILLW, description="International Lighthouse & Lightship Weekend", - sig_type=ActivityType.EVENT, + activity_type=ActivityType.EVENT, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.LIGHTHOUSE, @@ -194,7 +194,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.MOTA: Activity( name=ActivityName.MOTA, description="Mills on the Air", - sig_type=ActivityType.EVENT, + activity_type=ActivityType.EVENT, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.MILL, @@ -205,7 +205,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.SIOTA: Activity( name=ActivityName.SIOTA, description="Silos on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.SILO, @@ -217,7 +217,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WCA: Activity( name=ActivityName.WCA, description="World Castles Award", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.CASTLE, @@ -228,7 +228,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.ZLOTA: Activity( name=ActivityName.ZLOTA, description="New Zealand on the Air", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=None, @@ -241,7 +241,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WOTA: Activity( name=ActivityName.WOTA, description="Wainwrights on the Air", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.SUMMIT, @@ -254,7 +254,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.BOTA: Activity( name=ActivityName.BOTA, description="Beaches on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.BEACH, @@ -265,7 +265,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.KRMNPA: Activity( name=ActivityName.KRMNPA, description="Keith Roget Memorial National Parks Award", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.PARK, @@ -278,7 +278,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.SANPCPA: Activity( name=ActivityName.SANPCPA, description="South Australian National Parks and Conservation Parks Award", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.PARK, @@ -291,7 +291,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.LLOTA: Activity( name=ActivityName.LLOTA, description="Lagos y Lagunas on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.LAKE, @@ -303,7 +303,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.TOWERS: Activity( name=ActivityName.TOWERS, description="Towers on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.TOWER, @@ -314,7 +314,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.TILES: Activity( name=ActivityName.TILES, description="Tiles on the Air", - sig_type=ActivityType.ADVENTURE, + activity_type=ActivityType.ADVENTURE, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.GRID, @@ -325,7 +325,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.RADAR_RALLY: Activity( name=ActivityName.RADAR_RALLY, description="RaDAR Rally", - sig_type=ActivityType.EVENT, + activity_type=ActivityType.EVENT, has_refs=False, refs_globally_unique=False, comment_names=["RaDAR"], @@ -334,7 +334,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WAB: Activity( name=ActivityName.WAB, description="Worked All Britain", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.GRID, @@ -346,7 +346,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.WAI: Activity( name=ActivityName.WAI, description="Worked All Ireland", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.GRID, @@ -358,7 +358,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DMF: Activity( name=ActivityName.DMF, description="Diplôme des Moulins de France", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.MILL, @@ -369,7 +369,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DME: Activity( name=ActivityName.DME, description="Diploma Municipios de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.TOWN, @@ -381,7 +381,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.FEA: Activity( name=ActivityName.FEA, description="Diploma Faros de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.LIGHTHOUSE, @@ -396,7 +396,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DMUE: Activity( name=ActivityName.DMUE, description="Diploma Museos de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.BUILDING, @@ -408,7 +408,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DMVE: Activity( name=ActivityName.DMVE, description="Diploma Monumentos y Vestigios de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.BUILDING, @@ -420,7 +420,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DCE: Activity( name=ActivityName.DCE, description="Diploma Castillos de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.CASTLE, @@ -432,7 +432,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DEFE: Activity( name=ActivityName.DEFE, description="Diploma Estaciones de Ferrocarril de España", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.BUILDING, @@ -444,7 +444,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.DTMBA: Activity( name=ActivityName.DTMBA, description="Diploma Teatri Musei e Belle Arti", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.BUILDING, @@ -456,7 +456,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.BIWOTA: Activity( name=ActivityName.BIWOTA, description="British Inland Waterways on the Air", - sig_type=ActivityType.EVENT, + activity_type=ActivityType.EVENT, has_refs=False, refs_globally_unique=False, ref_type=ActivityRefType.WATERWAY, @@ -467,7 +467,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.COTA: Activity( name=ActivityName.COTA, description="Castles on the Air", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.CASTLE, @@ -479,7 +479,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.PGA: Activity( name=ActivityName.PGA, description="Polish Gmina Award", - sig_type=ActivityType.REGIONAL, + activity_type=ActivityType.REGIONAL, has_refs=True, refs_globally_unique=False, ref_type=ActivityRefType.REGION, @@ -491,7 +491,7 @@ ACTIVITIES: dict[ActivityName, Activity] = { ActivityName.TOILETS: Activity( name=ActivityName.TOILETS, description="Toilets on the Air", - sig_type=ActivityType.EVENT, + activity_type=ActivityType.EVENT, has_refs=True, refs_globally_unique=True, ref_type=ActivityRefType.TOILET, diff --git a/data/activity.py b/data/activity.py index c0e2fb0..41d76c7 100644 --- a/data/activity.py +++ b/data/activity.py @@ -5,22 +5,20 @@ from core.enums import ActivityName, ActivityRefType, ActivityType @dataclass class Activity: - """Data class that defines an Activity (formerly referred to as a "Special Interest Group" or "SIG", a term - which is still used for the `sig` field name in the API for backwards compatibility). Each contains a name and - a longer form description. They also contain comment_names which attempts to separate out the way people might - refer to it in cluster comments from how it is referred to in the UI & API. (For example, "TOTA" in cluster - spot comments almost always means Towers on the Air, but no single programme is referred to in the UI as "TOTA" - as it's ambiguous between Towers, Toilets and Tiles. And while Beaches got the name "BOTA" first, "BOTA" spots - are much more likely to be bunkers.) Finally, there is a ref_regex which provides a regular expression to - match what references (such as parks and summits) look like for that programme.""" + """Data class that defines an Activity. Each contains a name and a longer form description. They also contain + comment_names which attempts to separate out the way people might refer to it in cluster comments from how it is + referred to in the UI & API. (For example, "TOTA" in cluster spot comments almost always means Towers on the Air, + but no single programme is referred to in the UI as "TOTA" as it's ambiguous between Towers, Toilets and Tiles. + And while Beaches got the name "BOTA" first, "BOTA" spots are much more likely to be bunkers.) Finally, there is a + ref_regex which provides a regular expression to match what references (such as parks and summits) look like for + that programme.""" # Activity name as used in the UI and API, e.g. "Towers" name: ActivityName # Description, e.g. "Towers on the Air" description: str # Type, either Worldwide, Regional or Event. Used for sorting in the web UI. - # Note: this field is still named "sig_type" in the API for backwards compatibility. - sig_type: ActivityType + activity_type: ActivityType # Whether this activity has a fixed set of references (e.g. parks) with some sort of ID to "activate" has_refs: bool # Identifies that the activity's reference ID structure defined by its regex is unique across all programmes and diff --git a/data/activity_ref.py b/data/activity_ref.py index d766c8d..e0e7178 100644 --- a/data/activity_ref.py +++ b/data/activity_ref.py @@ -8,8 +8,8 @@ class ActivityRef: """Data class that defines an Activity "info" or reference. As well as the basic reference ID we include a name and a lookup URL.""" - # Activity that this reference is in, e.g. "POTA". Still named "sig" for backwards compatibility with the API. - sig: str + # Activity that this reference is in, e.g. "POTA". + activity: str # Reference ID, e.g. "GB-0001". id: str | None = None # Name of the reference, e.g. "Null Country Park", if known. diff --git a/data/alert.py b/data/alert.py index d78e5a9..1acf2c3 100644 --- a/data/alert.py +++ b/data/alert.py @@ -5,7 +5,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta import pytz -from pyhamtools.locator import locator_to_latlong, latlong_to_locator +from pyhamtools.locator import latlong_to_locator, locator_to_latlong from core.activity_lookup_helper import populate_missing_activity_ref_info from core.activity_utils import get_icon_for_activity @@ -66,11 +66,10 @@ class Alert: # Activity info - # Activity (e.g. outdoor activity programme such as POTA). Still named "sig" for API backwards compatibility. - 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) + # Activity (e.g. outdoor activity programme such as POTA). + activity: str | None = None + # Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. + activity_refs: list = field(default_factory=list) # Timing info @@ -131,8 +130,8 @@ class Alert: self.dx_flag = get_flag_for_dxcc(self.dx_dxcc_id) # Fetch activity data, and set a real position if we can get one. - if self.sig_refs: - for activity_ref in self.sig_refs: + if self.activity_refs: + for activity_ref in self.activity_refs: activity_ref = populate_missing_activity_ref_info(activity_ref) # If the alert itself doesn't have location yet, but the activity ref does, extract it if activity_ref.grid and not self.dx_grid: @@ -148,8 +147,8 @@ class Alert: # If the spot itself doesn't have an activity yet, but we have at least one activity reference, take that # reference's activity and apply it to the whole spot. - if self.sig_refs and self.sig_refs[0] and not self.sig: - self.sig = self.sig_refs[0].sig + if self.activity_refs and self.activity_refs[0] and not self.activity: + self.activity = self.activity_refs[0].activity # DX Grid to lat/lon and vice versa in case one is missing if self.dx_grid and (not self.dx_latitude or not self.dx_longitude): @@ -182,7 +181,7 @@ class Alert: # Icon for the alert should be the icon of its activity if known, otherwise a radio tower self.icon = "fa-tower-cell" - if self.sig and (activity_icon := get_icon_for_activity(self.sig)): + if self.activity and (activity_icon := get_icon_for_activity(self.activity)): self.icon = activity_icon except Exception: diff --git a/data/spot.py b/data/spot.py index 85ae3ad..64a9f47 100644 --- a/data/spot.py +++ b/data/spot.py @@ -76,8 +76,8 @@ class Spot: # DX Location source. Indicates how accurate the location might be. dx_location_source: LocationSourceForSpot | None = None # DX Location good. Indicates that the software thinks the location data is good enough to plot on a map. This is - # true if the location source is "SPOT", "SIG REF LOOKUP" or "GRID", or if the location source is "HOME QTH" and - # the DX callsign doesn't have a suffix like /P. (Location source retains "SIG" wording for API compatibility.) + # true if the location source is "SPOT", "ACTIVITY REF LOOKUP" or "GRID", or if the location source is "HOME QTH" + # and the DX callsign doesn't have a suffix like /P. dx_location_good: bool = False # DE (Spotter) info @@ -125,11 +125,10 @@ class Spot: # Activity info - # Activity (e.g. outdoor activity programme such as POTA). Still named "sig" for API backwards compatibility. - 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) + # Activity (e.g. outdoor activity programme such as POTA). + activity: str | None = None + # Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. + activity_refs: list = field(default_factory=list) # Timing info @@ -159,12 +158,12 @@ class Spot: def __post_init__(self): """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..""" + objects such as the activity_refs list..""" - if self.sig_refs: - self.sig_refs = [ + if self.activity_refs: + self.activity_refs = [ activity_ref if isinstance(activity_ref, ActivityRef) else ActivityRef(**activity_ref) - for activity_ref in self.sig_refs + for activity_ref in self.activity_refs ] def infer_missing(self, credentials=None): @@ -270,18 +269,20 @@ class Spot: self.dx_location_source = LocationSourceForSpot.SPOT # Set the top-level activity if it is missing but we have at least one activity ref. - if not self.sig and self.sig_refs: - self.sig = self.sig_refs[0].sig.upper() + if not self.activity and self.activity_refs: + self.activity = self.activity_refs[0].activity.upper() # See if we already have an activity reference, but the comment looks like it contains more for the same # activity. This should catch e.g. POTA comments like "2-fer: GB-0001 GB-0002". - if self.comment and self.sig_refs and self.sig_refs[0].sig: - activity = self.sig_refs[0].sig.upper() + if self.comment and self.activity_refs and self.activity_refs[0].activity: + activity = self.activity_refs[0].activity.upper() regex = get_ref_regex_for_activity(activity) if regex: all_comment_ref_matches = re.finditer(r"(^|\W)(" + regex + r")($|\W)", self.comment, re.IGNORECASE) for ref_match in all_comment_ref_matches: - self._append_activity_ref_if_missing(ActivityRef(id=ref_match.group(2).upper(), sig=activity)) + self._append_activity_ref_if_missing( + ActivityRef(id=ref_match.group(2).upper(), activity=activity) + ) # See if the comment looks like it contains any activities (and optionally activity references) that we # can add to the spot. This should catch cluster spot comments like "POTA GB-0001 WWFF GFF-0001" and e.g. @@ -292,11 +293,11 @@ class Spot: # First of all, if we haven't got an activity for this spot set yet, now we have. This covers # things like cluster spots where the comment is just "POTA". found_activity = get_activity_name_from_comment_name(activity_match.group(2)) - if not self.sig: - self.sig = found_activity + if not self.activity: + self.activity = found_activity # Now look to see if that activity name was followed by something that looks like a reference ID - # for that activity. If so, add that to the sig_refs list for this spot. + # for that activity. If so, add that to the activity_refs list for this spot. found_activity_info = get_activity_by_name(found_activity) if found_activity_info and found_activity_info.has_refs and found_activity_info.ref_regex: ref_matches = re.finditer( @@ -306,7 +307,7 @@ class Spot: ) for ref_match in ref_matches: self._append_activity_ref_if_missing( - ActivityRef(id=ref_match.group(3).upper(), sig=found_activity) + ActivityRef(id=ref_match.group(3).upper(), activity=found_activity) ) # See if the comment looks like it contains any activity references *without* the corresponding activity @@ -322,17 +323,17 @@ class Spot: # First of all, if we haven't got an activity for this spot set yet, now we have. This # covers things like cluster spots where the comment is just "OHFF-1234", now we know # it's WWFF. - if not self.sig: - self.sig = activity.name + if not self.activity: + self.activity = activity.name self._append_activity_ref_if_missing( - ActivityRef(id=ref_match.group(2).upper(), sig=activity.name) + ActivityRef(id=ref_match.group(2).upper(), activity=activity.name) ) # Fetch activity data. In case a particular API doesn't provide a full set of name, lat, lon & grid for a # reference in its initial call, we use this code to populate the rest of the data. This includes working # out grid refs from WAB and WAI, which count as an activity even though there's no real lookup, just maths - if self.sig_refs: - for activity_ref in self.sig_refs: + if self.activity_refs: + for activity_ref in self.activity_refs: activity_ref = populate_missing_activity_ref_info(activity_ref) # If the spot itself doesn't have location yet, but the activity ref does, extract it if activity_ref.grid and not self.dx_grid: @@ -345,15 +346,15 @@ class Spot: ): self.dx_latitude = activity_ref.latitude self.dx_longitude = activity_ref.longitude - if self.sig in (ActivityName.WAB, ActivityName.WAI, ActivityName.TILES): + if self.activity in (ActivityName.WAB, ActivityName.WAI, ActivityName.TILES): self.dx_location_source = LocationSourceForSpot.GRID else: - self.dx_location_source = LocationSourceForSpot.SIG_REF_LOOKUP + self.dx_location_source = LocationSourceForSpot.ACTIVITY_REF_LOOKUP # If the spot itself doesn't have an activity yet, but we have at least one activity reference, take that # reference's activity and apply it to the whole spot. - if self.sig_refs and not self.sig: - self.sig = self.sig_refs[0].sig + if self.activity_refs and not self.activity: + self.activity = self.activity_refs[0].activity # Parse "de_griddx_grid" structures from the comment, e.g. "JN61ES(ES)JM56XT" or "JO02GQ<>KN17LG". # These are common on cluster spots and can provide grid references in preference to e.g. QRZ lookup, as well as @@ -406,32 +407,32 @@ class Spot: self.dx_location_source = LocationSourceForSpot.GRID # Set activities based on propagation mode - if self.propagation_mode == "Satellite" and not self.sig: - self.sig = ActivityName.SATELLITE - if self.propagation_mode == "Earth-Moon-Earth" and not self.sig: - self.sig = ActivityName.EME + if self.propagation_mode == "Satellite" and not self.activity: + self.activity = ActivityName.SATELLITE + if self.propagation_mode == "Earth-Moon-Earth" and not self.activity: + self.activity = ActivityName.EME # Set activities based on the DX callsign suffix - if self.dx_call and not self.sig: + if self.dx_call and not self.activity: if self.dx_call.upper().endswith(ActivityName.AERONAUTICAL_MOBILE): - self.sig = ActivityName.AERONAUTICAL_MOBILE + self.activity = ActivityName.AERONAUTICAL_MOBILE elif self.dx_call.upper().endswith(ActivityName.MARITIME_MOBILE): - self.sig = ActivityName.MARITIME_MOBILE + self.activity = ActivityName.MARITIME_MOBILE # Alright, now let's get really fancy. Check if the DX callsign matches one taking part in a currently # running DXpedition which we know from the alerts list. - if self.dx_call and not self.sig: + if self.dx_call and not self.activity: now = datetime.now(pytz.UTC).timestamp() for alert in DATA_STORE.alerts.values(): if ( - alert.sig == ActivityName.DXPEDITION + alert.activity == ActivityName.DXPEDITION and alert.dx_calls and alert.start_time and alert.end_time and alert.start_time < now < alert.end_time and self.dx_call.upper() in [c.upper() for c in alert.dx_calls if c] ): - self.sig = ActivityName.DXPEDITION + self.activity = ActivityName.DXPEDITION break # DX Grid to lat/lon and vice versa in case one is missing @@ -476,10 +477,10 @@ class Spot: # Determine a "QTH" string. If we have an activity ref, pick the first one and turn it into a suitable # string, otherwise see what they have set on an online lookup service. - if self.sig_refs: - qth = self.sig_refs[0].id - if self.sig_refs[0].name: - qth += f" {self.sig_refs[0].name}" + if self.activity_refs: + qth = self.activity_refs[0].id + if self.activity_refs[0].name: + qth += f" {self.activity_refs[0].name}" self.dx_qth = qth else: self.dx_qth = dx_call_info.qth @@ -512,7 +513,7 @@ class Spot: and self.dx_longitude and ( self.dx_location_source == LocationSourceForSpot.SPOT - or self.dx_location_source == LocationSourceForSpot.SIG_REF_LOOKUP + or self.dx_location_source == LocationSourceForSpot.ACTIVITY_REF_LOOKUP or self.dx_location_source == LocationSourceForSpot.GRID or (self.dx_location_source == LocationSourceForSpot.HOME_QTH and "/" not in (self.dx_call or "")) ) @@ -532,7 +533,7 @@ class Spot: # Icon for the spot should be the icon of its activity if known, otherwise a radio tower self.icon = "fa-tower-cell" - if self.sig and (activity_icon := get_icon_for_activity(self.sig)): + if self.activity and (activity_icon := get_icon_for_activity(self.activity)): self.icon = activity_icon except Exception: @@ -547,13 +548,13 @@ class Spot: """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() - new_activity_ref.sig = new_activity_ref.sig.strip().upper() + new_activity_ref.activity = new_activity_ref.activity.strip().upper() if new_activity_ref.id == "": return - for activity_ref in self.sig_refs: - if activity_ref.id == new_activity_ref.id and activity_ref.sig == new_activity_ref.sig: + for activity_ref in self.activity_refs: + if activity_ref.id == new_activity_ref.id and activity_ref.activity == new_activity_ref.activity: return - self.sig_refs.append(new_activity_ref) + self.activity_refs.append(new_activity_ref) def expired(self): """Decide if this spot has expired (in which case it should not be added to the system in the first place, and not diff --git a/providers/activityrefdata/activity_ref_data_provider.py b/providers/activityrefdata/activity_ref_data_provider.py index 50fbfb7..c277271 100644 --- a/providers/activityrefdata/activity_ref_data_provider.py +++ b/providers/activityrefdata/activity_ref_data_provider.py @@ -13,11 +13,10 @@ class ActivityRefDataProvider: """Generic activity reference data provider class. Subclasses of this query the individual URLs or files for data.""" - def __init__(self, sig_name, provider_config): - """Constructor. Note the parameter and attribute are still named "sig_name" for consistency with the API's - "sig" field name.""" + def __init__(self, activity_name, provider_config): + """Constructor""" - self.sig_name = sig_name + self.activity_name = activity_name self.enabled = provider_config["enabled"] self.last_update_time = datetime.min.replace(tzinfo=pytz.UTC) self.status = "Not Started" if self.enabled else "Disabled" @@ -43,7 +42,7 @@ class ActivityRefDataProvider: # transact()s is to fail if they can't get the lock (?!). This behaviour is fixed by retry=True. with DATA_STORE.activity_refs.transact(retry=True): for d in new_data: - DATA_STORE.activity_refs.set(f"{self.sig_name}:{d.id}", d) + DATA_STORE.activity_refs.set(f"{self.activity_name}:{d.id}", d) # For the big data sources, loading will take a few minutes. If we want to shut down the software neatly # within the first few minutes of startup, we need a way to abort this expensive process of filling up the @@ -52,4 +51,4 @@ class ActivityRefDataProvider: break self.reference_count = len(new_data) - logger.info(f"Loaded {self.reference_count} references for {self.sig_name} into the data store.") + logger.info(f"Loaded {self.reference_count} references for {self.activity_name} into the data store.") diff --git a/providers/activityrefdata/arlhs.py b/providers/activityrefdata/arlhs.py index 0f2079f..c434985 100644 --- a/providers/activityrefdata/arlhs.py +++ b/providers/activityrefdata/arlhs.py @@ -25,7 +25,7 @@ class ARLHS(FileDownloadActivityRefDataProvider): ref_id = row["ARLHS"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Name", None), ref_type=ActivityRefType.LIGHTHOUSE, diff --git a/providers/activityrefdata/cota.py b/providers/activityrefdata/cota.py index d51403e..f6b0f65 100644 --- a/providers/activityrefdata/cota.py +++ b/providers/activityrefdata/cota.py @@ -30,7 +30,7 @@ class COTA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=name, ref_type=ActivityRefType.CASTLE, diff --git a/providers/activityrefdata/dce.py b/providers/activityrefdata/dce.py index 5975b70..9be5725 100644 --- a/providers/activityrefdata/dce.py +++ b/providers/activityrefdata/dce.py @@ -27,7 +27,12 @@ class DCE(FileDownloadActivityRefDataProvider): for index, row in df.iterrows(): if row.iloc[0] and row.iloc[2]: new_data.append( - ActivityRef(sig=self.ACTIVITY, id=row.iloc[0].strip(), name=row.iloc[2].strip(), ref_type=ActivityRefType.CASTLE) + ActivityRef( + activity=self.ACTIVITY, + id=row.iloc[0].strip(), + name=row.iloc[2].strip(), + ref_type=ActivityRefType.CASTLE, + ) ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of diff --git a/providers/activityrefdata/defe.py b/providers/activityrefdata/defe.py index c1c5569..52ddf25 100644 --- a/providers/activityrefdata/defe.py +++ b/providers/activityrefdata/defe.py @@ -31,7 +31,12 @@ class DEFE(FileDownloadActivityRefDataProvider): if row.iloc[0] and row.iloc[1]: new_data.append( - ActivityRef(sig=self.ACTIVITY, id=row.iloc[0].strip(), name=row.iloc[1].strip(), ref_type=ActivityRefType.BUILDING) + ActivityRef( + activity=self.ACTIVITY, + id=row.iloc[0].strip(), + name=row.iloc[1].strip(), + ref_type=ActivityRefType.BUILDING, + ) ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of diff --git a/providers/activityrefdata/dme.py b/providers/activityrefdata/dme.py index 0955b80..5d05b6c 100644 --- a/providers/activityrefdata/dme.py +++ b/providers/activityrefdata/dme.py @@ -40,7 +40,7 @@ class DME(LocalFileActivityRefDataProvider): ) ref = ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, ref_type=ActivityRefType.TOWN, name=f"{row['NOMBRE_ACTUAL']}, {row['PROVINCIA']}", diff --git a/providers/activityrefdata/dmue.py b/providers/activityrefdata/dmue.py index 6ff3403..55748c2 100644 --- a/providers/activityrefdata/dmue.py +++ b/providers/activityrefdata/dmue.py @@ -22,7 +22,12 @@ class DMUE(FileDownloadActivityRefDataProvider): for row in csv.reader(http_response.content.decode("utf-8-sig").splitlines(), delimiter=";"): if len(row) > 1 and row[0] and row[1]: new_data.append( - ActivityRef(sig=self.ACTIVITY, id=row[0].strip(), name=row[1].strip(), ref_type=ActivityRefType.BUILDING) + ActivityRef( + activity=self.ACTIVITY, + id=row[0].strip(), + name=row[1].strip(), + ref_type=ActivityRefType.BUILDING, + ) ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of diff --git a/providers/activityrefdata/dmve.py b/providers/activityrefdata/dmve.py index 841a7af..9bdb309 100644 --- a/providers/activityrefdata/dmve.py +++ b/providers/activityrefdata/dmve.py @@ -36,7 +36,11 @@ class DMVE(FileDownloadActivityRefDataProvider): continue if ref and name: - new_data.append(ActivityRef(sig=self.ACTIVITY, id=ref.strip(), name=name.strip(), ref_type=ActivityRefType.BUILDING)) + new_data.append( + ActivityRef( + activity=self.ACTIVITY, id=ref.strip(), name=name.strip(), ref_type=ActivityRefType.BUILDING + ) + ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of # the data in this case diff --git a/providers/activityrefdata/dtmba.py b/providers/activityrefdata/dtmba.py index 6791435..8cb5b12 100644 --- a/providers/activityrefdata/dtmba.py +++ b/providers/activityrefdata/dtmba.py @@ -21,7 +21,9 @@ class DTMBA(FileDownloadActivityRefDataProvider): split = row.split(";") ref_id = split[0] ref_name = split[1] - new_data.append(ActivityRef(sig=self.ACTIVITY, id=ref_id, name=ref_name, ref_type=ActivityRefType.BUILDING)) + new_data.append( + ActivityRef(activity=self.ACTIVITY, id=ref_id, name=ref_name, ref_type=ActivityRefType.BUILDING) + ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of # the data in this case diff --git a/providers/activityrefdata/fea.py b/providers/activityrefdata/fea.py index cfc12ff..9a3a0d0 100644 --- a/providers/activityrefdata/fea.py +++ b/providers/activityrefdata/fea.py @@ -41,8 +41,16 @@ class FEA(FileDownloadActivityRefDataProvider): # prefix and just use FEA-1234 or FEA 1234, so we add both copies to the database. ref_id_1 = row[0].strip() ref_id_2 = ref_id_1.replace("D-", "FEA-").replace("E-", "FEA-") - new_data.append(ActivityRef(sig=self.ACTIVITY, id=ref_id_1, name=row[1].strip(), ref_type=ActivityRefType.LIGHTHOUSE)) - new_data.append(ActivityRef(sig=self.ACTIVITY, id=ref_id_2, name=row[1].strip(), ref_type=ActivityRefType.LIGHTHOUSE)) + new_data.append( + ActivityRef( + activity=self.ACTIVITY, id=ref_id_1, name=row[1].strip(), ref_type=ActivityRefType.LIGHTHOUSE + ) + ) + new_data.append( + ActivityRef( + activity=self.ACTIVITY, id=ref_id_2, name=row[1].strip(), ref_type=ActivityRefType.LIGHTHOUSE + ) + ) # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest of # the data in this case diff --git a/providers/activityrefdata/file_download_activity_ref_data_provider.py b/providers/activityrefdata/file_download_activity_ref_data_provider.py index 3d8e865..5d4b03f 100644 --- a/providers/activityrefdata/file_download_activity_ref_data_provider.py +++ b/providers/activityrefdata/file_download_activity_ref_data_provider.py @@ -16,19 +16,21 @@ class FileDownloadActivityRefDataProvider(ActivityRefDataProvider): """Generic activity ref data provider class for providers that fetch their data from the web by downloading a file.""" - def __init__(self, sig_name, provider_config, url, poll_interval): + def __init__(self, activity_name, provider_config, url, poll_interval): """Set up the provider, note poll_interval is in *days*.""" - super().__init__(sig_name, provider_config) + super().__init__(activity_name, provider_config) self._url = url self._poll_interval = poll_interval self._thread = None - self._url_data_cache = URLDataCache(f"activity_ref_data_{sig_name}") + self._url_data_cache = URLDataCache(f"activity_ref_data_{activity_name}") def start(self): # Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between # subsequent polls, so start() returns immediately and the application can continue starting. - logger.info(f"Set up query of {self.sig_name} activity ref data every {self._poll_interval!s} days.") - self._thread = Thread(target=self._run, name=f"FileDownloadActivityRefDataProvider-{self.sig_name}", daemon=True) + logger.info(f"Set up query of {self.activity_name} activity ref data every {self._poll_interval!s} days.") + self._thread = Thread( + target=self._run, name=f"FileDownloadActivityRefDataProvider-{self.activity_name}", daemon=True + ) self._thread.start() def stop(self): @@ -36,7 +38,9 @@ class FileDownloadActivityRefDataProvider(ActivityRefDataProvider): if self._thread: self._thread.join(timeout=12) if self._thread.is_alive(): - logger.warning(f"{self.sig_name} activity ref data worker thread did not exit on time and will be killed.") + logger.warning( + f"{self.activity_name} activity ref data worker thread did not exit on time and will be killed." + ) def _run(self): while True: @@ -48,7 +52,7 @@ class FileDownloadActivityRefDataProvider(ActivityRefDataProvider): try: # Request data from API. Use the data cache (with a TTL of 1 day) here, not as the main mechanism for # caching, but just so continual restarts of the software during testing don't hammer the servers. - logger.debug(f"Downloading {self.sig_name} activity ref data...") + logger.debug(f"Downloading {self.activity_name} activity ref data...") http_response = self._url_data_cache.get(self._url, headers=HTTP_HEADERS) # Check response code was good if http_response.ok: @@ -60,20 +64,22 @@ class FileDownloadActivityRefDataProvider(ActivityRefDataProvider): self.status = "OK" self.last_update_time = datetime.now(pytz.UTC) - logger.debug(f"Received activity ref data for {self.sig_name}") + logger.debug(f"Received activity ref data for {self.activity_name}") else: self.status = "Error" - logger.warning(f"HTTP {http_response.status_code} when downloading activity ref data for {self.sig_name}.") + logger.warning( + f"HTTP {http_response.status_code} when downloading activity ref data for {self.activity_name}." + ) except ConnectionError: self.status = "Error" - logger.warning(f"Connection error when downloading activity ref data for {self.sig_name}.") + logger.warning(f"Connection error when downloading activity ref data for {self.activity_name}.") except (ConnectTimeout, ReadTimeout): self.status = "Error" - logger.warning(f"Timeout when downloading activity ref data for {self.sig_name}.") + logger.warning(f"Timeout when downloading activity ref data for {self.activity_name}.") except Exception: self.status = "Error" - logger.exception(f"Exception in HTTP Activity Ref Data Provider ({self.sig_name})") + logger.exception(f"Exception in HTTP Activity Ref Data Provider ({self.activity_name})") self._stop_event.wait(timeout=1) def _http_response_to_data(self, http_response): diff --git a/providers/activityrefdata/gma.py b/providers/activityrefdata/gma.py index da1e282..d71605e 100644 --- a/providers/activityrefdata/gma.py +++ b/providers/activityrefdata/gma.py @@ -24,7 +24,7 @@ class GMA(FileDownloadActivityRefDataProvider): ref_id = row["Reference"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Name", None), ref_type=ActivityRefType.SUMMIT, diff --git a/providers/activityrefdata/illw.py b/providers/activityrefdata/illw.py index 2575437..601bc31 100644 --- a/providers/activityrefdata/illw.py +++ b/providers/activityrefdata/illw.py @@ -25,7 +25,7 @@ class ILLW(FileDownloadActivityRefDataProvider): ref_id = row["ILLW"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Name", None), ref_type=ActivityRefType.LIGHTHOUSE, diff --git a/providers/activityrefdata/iota.py b/providers/activityrefdata/iota.py index d6ddcf5..5fce21d 100644 --- a/providers/activityrefdata/iota.py +++ b/providers/activityrefdata/iota.py @@ -42,7 +42,7 @@ class IOTA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=ref["name"], ref_type=ActivityRefType.ISLAND, diff --git a/providers/activityrefdata/llota.py b/providers/activityrefdata/llota.py index bdcf9c2..f36f856 100644 --- a/providers/activityrefdata/llota.py +++ b/providers/activityrefdata/llota.py @@ -30,7 +30,7 @@ class LLOTA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=str(ref["name"]), ref_type=ActivityRefType.LAKE, diff --git a/providers/activityrefdata/local_file_activity_ref_data_provider.py b/providers/activityrefdata/local_file_activity_ref_data_provider.py index e03c7c9..2f64a86 100644 --- a/providers/activityrefdata/local_file_activity_ref_data_provider.py +++ b/providers/activityrefdata/local_file_activity_ref_data_provider.py @@ -11,12 +11,12 @@ logger = logging.getLogger(__name__) class LocalFileActivityRefDataProvider(ActivityRefDataProvider): """Generic activity ref data provider class for providers that fetch their data from a local file on startup.""" - def __init__(self, sig_name, provider_config, path): - super().__init__(sig_name, provider_config) + def __init__(self, activity_name, provider_config, path): + super().__init__(activity_name, provider_config) self._path = path def start(self): - logger.debug(f"Loading {self.sig_name} activity ref data from file.") + logger.debug(f"Loading {self.activity_name} activity ref data from file.") try: new_data = self._file_to_data(self._path) if new_data: @@ -25,10 +25,10 @@ class LocalFileActivityRefDataProvider(ActivityRefDataProvider): self.last_update_time = datetime.now(pytz.UTC) else: self.status = "Error" - logger.info(f"Failed to load activity ref data for {self.sig_name}") + logger.info(f"Failed to load activity ref data for {self.activity_name}") except Exception: self.status = "Error" - logger.exception(f"Exception in local file Activity Ref Data Provider ({self.sig_name})") + logger.exception(f"Exception in local file Activity Ref Data Provider ({self.activity_name})") def _file_to_data(self, path): """Load a file on the given path and turn it into activity ref data.""" diff --git a/providers/activityrefdata/mota.py b/providers/activityrefdata/mota.py index 70ddae2..8486ab8 100644 --- a/providers/activityrefdata/mota.py +++ b/providers/activityrefdata/mota.py @@ -24,7 +24,7 @@ class MOTA(FileDownloadActivityRefDataProvider): ref_id = row["Reference"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Name", None), ref_type=ActivityRefType.MILL, diff --git a/providers/activityrefdata/pga.py b/providers/activityrefdata/pga.py index 476b348..9fdace6 100644 --- a/providers/activityrefdata/pga.py +++ b/providers/activityrefdata/pga.py @@ -39,7 +39,7 @@ class PGA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=name, ref_type=ActivityRefType.REGION, diff --git a/providers/activityrefdata/pnp_kml_activity_ref_data_provider.py b/providers/activityrefdata/pnp_kml_activity_ref_data_provider.py index 119ce37..fcbef5c 100644 --- a/providers/activityrefdata/pnp_kml_activity_ref_data_provider.py +++ b/providers/activityrefdata/pnp_kml_activity_ref_data_provider.py @@ -17,9 +17,9 @@ class ParksNPeaksKMLActivityRefDataProvider(FileDownloadActivityRefDataProvider) REF_PATTERN = re.compile(r"VKFF-\d+") - def __init__(self, sig_name, provider_config, url, poll_interval): + def __init__(self, activity_name, provider_config, url, poll_interval): """Set up the provider, note poll_interval is in *days*.""" - super().__init__(sig_name, provider_config, url, poll_interval) + super().__init__(activity_name, provider_config, url, poll_interval) def _http_response_to_data(self, http_response): new_data = [] @@ -41,7 +41,7 @@ class ParksNPeaksKMLActivityRefDataProvider(FileDownloadActivityRefDataProvider) longitude, latitude = placemark.geometry.x, placemark.geometry.y ref = ActivityRef( - sig=self.sig_name, + activity=self.activity_name, id=ref_id, name=placemark.name, ref_type=ActivityRefType.PARK, diff --git a/providers/activityrefdata/pota.py b/providers/activityrefdata/pota.py index 991eeda..17c2ce3 100644 --- a/providers/activityrefdata/pota.py +++ b/providers/activityrefdata/pota.py @@ -24,7 +24,7 @@ class POTA(FileDownloadActivityRefDataProvider): ref_id = row["reference"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("name", None), ref_type=ActivityRefType.PARK, diff --git a/providers/activityrefdata/siota.py b/providers/activityrefdata/siota.py index 3bce496..08df049 100644 --- a/providers/activityrefdata/siota.py +++ b/providers/activityrefdata/siota.py @@ -24,7 +24,7 @@ class SIOTA(FileDownloadActivityRefDataProvider): ref_id = row["SILO_CODE"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("NAME", None), ref_type=ActivityRefType.SILO, diff --git a/providers/activityrefdata/sota.py b/providers/activityrefdata/sota.py index e34dcab..f96895d 100644 --- a/providers/activityrefdata/sota.py +++ b/providers/activityrefdata/sota.py @@ -28,7 +28,7 @@ class SOTA(FileDownloadActivityRefDataProvider): longitude = float(row["Longitude"]) if "Longitude" in row and row["Longitude"] != "" else None altitude = float(row["AltM"]) if "AltM" in row and row["AltM"] != "" else None ref = ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("SummitName", None), ref_type=ActivityRefType.SUMMIT, diff --git a/providers/activityrefdata/toilets.py b/providers/activityrefdata/toilets.py index efbe15d..f94f4de 100644 --- a/providers/activityrefdata/toilets.py +++ b/providers/activityrefdata/toilets.py @@ -24,7 +24,7 @@ class Toilets(LocalFileActivityRefDataProvider): for row in dr: new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=row["ref"], name=row["ref"], ref_type=ActivityRefType.TOILET, diff --git a/providers/activityrefdata/towers.py b/providers/activityrefdata/towers.py index 4800764..f45f90b 100644 --- a/providers/activityrefdata/towers.py +++ b/providers/activityrefdata/towers.py @@ -24,7 +24,7 @@ class Towers(FileDownloadActivityRefDataProvider): ref_id = row["Ref"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Nazev", None), ref_type=ActivityRefType.TOWER, diff --git a/providers/activityrefdata/wca.py b/providers/activityrefdata/wca.py index b0de0f2..4842556 100644 --- a/providers/activityrefdata/wca.py +++ b/providers/activityrefdata/wca.py @@ -43,7 +43,7 @@ class WCA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("CLEAN NAME", None), ref_type=ActivityRefType.CASTLE, diff --git a/providers/activityrefdata/wota.py b/providers/activityrefdata/wota.py index 5329348..0be53eb 100644 --- a/providers/activityrefdata/wota.py +++ b/providers/activityrefdata/wota.py @@ -30,7 +30,7 @@ class WOTA(FileDownloadActivityRefDataProvider): new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=feature["properties"]["title"], url=url, diff --git a/providers/activityrefdata/wwbota.py b/providers/activityrefdata/wwbota.py index ca024a9..7a9d6dd 100644 --- a/providers/activityrefdata/wwbota.py +++ b/providers/activityrefdata/wwbota.py @@ -24,7 +24,7 @@ class WWBOTA(FileDownloadActivityRefDataProvider): ref_id = row["Reference"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("Name", None), ref_type=ActivityRefType.BUNKER, diff --git a/providers/activityrefdata/wwff.py b/providers/activityrefdata/wwff.py index 4e15f01..358ada1 100644 --- a/providers/activityrefdata/wwff.py +++ b/providers/activityrefdata/wwff.py @@ -24,7 +24,7 @@ class WWFF(FileDownloadActivityRefDataProvider): ref_id = row["reference"] new_data.append( ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=row.get("name", None), ref_type=ActivityRefType.PARK, diff --git a/providers/activityrefdata/zlota.py b/providers/activityrefdata/zlota.py index 0ebf445..362279e 100644 --- a/providers/activityrefdata/zlota.py +++ b/providers/activityrefdata/zlota.py @@ -33,7 +33,7 @@ class ZLOTA(FileDownloadActivityRefDataProvider): ref_type = None new_ref = ActivityRef( - sig=self.ACTIVITY, + activity=self.ACTIVITY, id=ref_id, name=ref["name"], ref_type=ref_type, diff --git a/providers/alert/bota.py b/providers/alert/bota.py index be3f39c..ec85499 100644 --- a/providers/alert/bota.py +++ b/providers/alert/bota.py @@ -56,8 +56,8 @@ class BOTA(HTTPAlertProvider): alert = Alert( source=self.name, dx_calls=[dx_call], - sig=ActivityName.BOTA, - sig_refs=[ActivityRef(id=ref_name, sig=ActivityName.BOTA)], + activity=ActivityName.BOTA, + activity_refs=[ActivityRef(id=ref_name, activity=ActivityName.BOTA)], start_time=date_time.timestamp(), ) diff --git a/providers/alert/hamsat.py b/providers/alert/hamsat.py index bb59c15..2686313 100644 --- a/providers/alert/hamsat.py +++ b/providers/alert/hamsat.py @@ -38,11 +38,11 @@ class Hamsat(HTTPAlertProvider): dx_grid=source_alert["grids"][0], freqs_modes=freqs_modes, comment=source_alert["comment"], - sig=ActivityName.SATELLITE, + activity=ActivityName.SATELLITE, # Fudge an activity ref to provide the remaining bits of data we need: the satellite and the operator's grid - sig_refs=[ + activity_refs=[ ActivityRef( - sig=ActivityName.SATELLITE, + activity=ActivityName.SATELLITE, id=source_alert["satellite"]["name"], ) ], diff --git a/providers/alert/ng3k.py b/providers/alert/ng3k.py index 3d7d928..a950b92 100644 --- a/providers/alert/ng3k.py +++ b/providers/alert/ng3k.py @@ -89,7 +89,7 @@ class NG3K(HTTPAlertProvider): comment=f"{by}; {comment}; {qsl_info}", start_time=start_timestamp, end_time=end_timestamp, - sig=ActivityName.DXPEDITION, + activity=ActivityName.DXPEDITION, ) # Add to our list. diff --git a/providers/alert/parksnpeaks.py b/providers/alert/parksnpeaks.py index 7e7b4aa..cce2279 100644 --- a/providers/alert/parksnpeaks.py +++ b/providers/alert/parksnpeaks.py @@ -37,7 +37,7 @@ class ParksNPeaks(HTTPAlertProvider): datetime.strptime(source_alert["alTime"], "%Y-%m-%d %H:%M:%S").replace(tzinfo=pytz.UTC).timestamp() ) - activity_refs = [ActivityRef(id=ref_id, sig=activity, name=ref_name)] + activity_refs = [ActivityRef(id=ref_id, activity=activity, name=ref_name)] # Convert to our alert format alert = Alert( @@ -46,8 +46,8 @@ class ParksNPeaks(HTTPAlertProvider): dx_calls=[source_alert["CallSign"].upper()], freqs_modes=f"{source_alert['Freq']} {source_alert['MODE']}", comment=source_alert["Comments"], - sig=activity, - sig_refs=activity_refs, + activity=activity, + activity_refs=activity_refs, start_time=start_time, ) diff --git a/providers/alert/pota.py b/providers/alert/pota.py index fe656fe..514c23f 100644 --- a/providers/alert/pota.py +++ b/providers/alert/pota.py @@ -28,11 +28,11 @@ class POTA(HTTPAlertProvider): dx_calls=[source_alert["activator"].upper()], freqs_modes=source_alert["frequencies"], comment=source_alert["comments"], - sig=ActivityName.POTA, - sig_refs=[ + activity=ActivityName.POTA, + activity_refs=[ ActivityRef( id=source_alert["reference"], - sig=ActivityName.POTA, + activity=ActivityName.POTA, name=source_alert["name"], url=f"https://pota.app/#/park/{source_alert['reference']}", ) diff --git a/providers/alert/rsgb_ical_alert_provider.py b/providers/alert/rsgb_ical_alert_provider.py index d059b99..1402700 100644 --- a/providers/alert/rsgb_ical_alert_provider.py +++ b/providers/alert/rsgb_ical_alert_provider.py @@ -69,7 +69,7 @@ class RSGBICALAlertProvider(ICALAlertProvider): comment=summary, start_time=start_timestamp, end_time=end_timestamp, - sig=ActivityName.CONTEST, + activity=ActivityName.CONTEST, ) return alert diff --git a/providers/alert/sota.py b/providers/alert/sota.py index 9678917..cc6169e 100644 --- a/providers/alert/sota.py +++ b/providers/alert/sota.py @@ -34,11 +34,11 @@ class SOTA(HTTPAlertProvider): dx_names=[source_alert["activatorName"].upper()], freqs_modes=source_alert["frequency"], comment=source_alert["comments"], - sig=ActivityName.SOTA, - sig_refs=[ + activity=ActivityName.SOTA, + activity_refs=[ ActivityRef( id=f"{source_alert['associationCode']}/{source_alert['summitCode']}", - sig=ActivityName.SOTA, + activity=ActivityName.SOTA, name=summit_name, activation_score=summit_points, ) diff --git a/providers/alert/wa7bnm.py b/providers/alert/wa7bnm.py index d311826..09cdde7 100644 --- a/providers/alert/wa7bnm.py +++ b/providers/alert/wa7bnm.py @@ -35,7 +35,7 @@ class WA7BNM(ICALAlertProvider): url=url, start_time=start_timestamp, end_time=end_timestamp, - sig=ActivityName.CONTEST, + activity=ActivityName.CONTEST, ) return alert diff --git a/providers/alert/wota.py b/providers/alert/wota.py index e9ce74b..b6ed9c3 100644 --- a/providers/alert/wota.py +++ b/providers/alert/wota.py @@ -75,7 +75,7 @@ class WOTA(HTTPAlertProvider): dx_calls=[dx_call], freqs_modes=freqs_modes, comment=comment, - sig_refs=[ActivityRef(id=ref, sig=ActivityName.WOTA, name=ref_name)] if ref else [], + activity_refs=[ActivityRef(id=ref, activity=ActivityName.WOTA, name=ref_name)] if ref else [], start_time=time.timestamp(), ) diff --git a/providers/alert/wwff.py b/providers/alert/wwff.py index 733d327..570f0f3 100644 --- a/providers/alert/wwff.py +++ b/providers/alert/wwff.py @@ -28,8 +28,8 @@ class WWFF(HTTPAlertProvider): dx_calls=[source_alert["activator_call"].upper()], freqs_modes=f"{source_alert['band']} {source_alert['mode']}", comment=source_alert["remarks"], - sig=ActivityName.WWFF, - sig_refs=[ActivityRef(id=source_alert["reference"], sig=ActivityName.WWFF)], + activity=ActivityName.WWFF, + activity_refs=[ActivityRef(id=source_alert["reference"], activity=ActivityName.WWFF)], start_time=datetime.strptime(source_alert["utc_start"], "%Y-%m-%d %H:%M:%S") .replace(tzinfo=pytz.UTC) .timestamp(), diff --git a/providers/spot/gma.py b/providers/spot/gma.py index 27dd166..1057e64 100644 --- a/providers/spot/gma.py +++ b/providers/spot/gma.py @@ -68,10 +68,10 @@ 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"], - sig_refs=[ + activity_refs=[ ActivityRef( id=source_spot["REF"], - sig="", + activity="", name=source_spot["NAME"], latitude=lat, longitude=lon, @@ -98,57 +98,57 @@ class GMA(HTTPSpotProvider): and ref_response.text != "\n" ): ref_info = ref_response.json() - if spot.sig_refs and ref_info and "reftype" in ref_info: + if spot.activity_refs and ref_info and "reftype" in ref_info: 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.sig_refs[0].sig = ActivityName.SOTA - spot.sig_refs[0].ref_type = ActivityRefType.SUMMIT - spot.sig = ActivityName.SOTA + spot.activity_refs[0].activity = ActivityName.SOTA + spot.activity_refs[0].ref_type = ActivityRefType.SUMMIT + spot.activity = ActivityName.SOTA else: - spot.sig_refs[0].sig = ActivityName.GMA - spot.sig_refs[0].ref_type = ActivityRefType.SUMMIT - spot.sig = ActivityName.GMA + spot.activity_refs[0].activity = ActivityName.GMA + spot.activity_refs[0].ref_type = ActivityRefType.SUMMIT + spot.activity = ActivityName.GMA case "POTA": - spot.sig_refs[0].sig = ActivityName.POTA - spot.sig_refs[0].ref_type = ActivityRefType.PARK - spot.sig = ActivityName.POTA + spot.activity_refs[0].activity = ActivityName.POTA + spot.activity_refs[0].ref_type = ActivityRefType.PARK + spot.activity = ActivityName.POTA case "WWFF": - spot.sig_refs[0].sig = ActivityName.WWFF - spot.sig_refs[0].ref_type = ActivityRefType.PARK - spot.sig = ActivityName.WWFF + spot.activity_refs[0].activity = ActivityName.WWFF + spot.activity_refs[0].ref_type = ActivityRefType.PARK + spot.activity = ActivityName.WWFF case "IOTA Island": - spot.sig_refs[0].sig = ActivityName.IOTA - spot.sig_refs[0].ref_type = ActivityRefType.ISLAND - spot.sig = ActivityName.IOTA + spot.activity_refs[0].activity = ActivityName.IOTA + spot.activity_refs[0].ref_type = ActivityRefType.ISLAND + spot.activity = ActivityName.IOTA case "GMA Island": - spot.sig_refs[0].sig = ActivityName.GMA_ISLANDS - spot.sig_refs[0].ref_type = ActivityRefType.ISLAND - spot.sig = ActivityName.GMA_ISLANDS + spot.activity_refs[0].activity = ActivityName.GMA_ISLANDS + spot.activity_refs[0].ref_type = ActivityRefType.ISLAND + spot.activity = ActivityName.GMA_ISLANDS case "Lighthouse (ILLW)": - spot.sig_refs[0].sig = ActivityName.ILLW - spot.sig_refs[0].ref_type = ActivityRefType.LIGHTHOUSE - spot.sig = ActivityName.ILLW + spot.activity_refs[0].activity = ActivityName.ILLW + spot.activity_refs[0].ref_type = ActivityRefType.LIGHTHOUSE + spot.activity = ActivityName.ILLW case "Lighthouse (ARLHS)": - spot.sig_refs[0].sig = ActivityName.ARLHS - spot.sig_refs[0].ref_type = ActivityRefType.LIGHTHOUSE - spot.sig = ActivityName.ARLHS + spot.activity_refs[0].activity = ActivityName.ARLHS + spot.activity_refs[0].ref_type = ActivityRefType.LIGHTHOUSE + spot.activity = ActivityName.ARLHS case "Castle": - spot.sig_refs[0].sig = ActivityName.WCA - spot.sig_refs[0].ref_type = ActivityRefType.CASTLE - spot.sig = ActivityName.WCA + spot.activity_refs[0].activity = ActivityName.WCA + spot.activity_refs[0].ref_type = ActivityRefType.CASTLE + spot.activity = ActivityName.WCA case "Mill": - spot.sig_refs[0].sig = ActivityName.MOTA - spot.sig_refs[0].ref_type = ActivityRefType.MILL - spot.sig = ActivityName.MOTA + spot.activity_refs[0].activity = ActivityName.MOTA + spot.activity_refs[0].ref_type = ActivityRefType.MILL + spot.activity = ActivityName.MOTA case _: logger.warning( f"GMA spot found with ref type {ref_info['reftype']}, developer needs to add support for this!" ) - spot.sig_refs[0].sig = ref_info["reftype"] - spot.sig = ref_info["reftype"] + spot.activity_refs[0].activity = ref_info["reftype"] + spot.activity = ref_info["reftype"] elif not ref_response.from_cache: if not ref_response.ok: diff --git a/providers/spot/hema.py b/providers/spot/hema.py index 8236f7c..4971405 100644 --- a/providers/spot/hema.py +++ b/providers/spot/hema.py @@ -62,11 +62,11 @@ class HEMA(HTTPSpotProvider): freq=float(freq_mode_match.group(1)) * 1000000, mode=Mode.from_name(freq_mode_match.group(2).upper()), comment=spotter_comment_match.group(2), - sig=ActivityName.HEMA, - sig_refs=[ + activity=ActivityName.HEMA, + activity_refs=[ ActivityRef( id=spot_items[3].upper(), - sig=ActivityName.HEMA, + activity=ActivityName.HEMA, name=spot_items[4], latitude=float(spot_items[7]), longitude=float(spot_items[8]), diff --git a/providers/spot/llota.py b/providers/spot/llota.py index d43dd87..798654c 100644 --- a/providers/spot/llota.py +++ b/providers/spot/llota.py @@ -34,11 +34,11 @@ class LLOTA(HTTPSpotProvider): freq=float(source_spot["frequency"]) * 1000000, mode=Mode.from_name(source_spot["mode"].upper()), comment=comment, - sig=ActivityName.LLOTA, - sig_refs=[ + activity=ActivityName.LLOTA, + activity_refs=[ ActivityRef( id=source_spot["reference"], - sig=ActivityName.LLOTA, + activity=ActivityName.LLOTA, name=source_spot["reference_name"], ref_type=ActivityRefType.LAKE, ) diff --git a/providers/spot/parksnpeaks.py b/providers/spot/parksnpeaks.py index 65e70da..e29bffb 100644 --- a/providers/spot/parksnpeaks.py +++ b/providers/spot/parksnpeaks.py @@ -70,20 +70,20 @@ class ParksNPeaks(HTTPSpotProvider): ref_id = source_spot["actSiteID"] if activity: - spot.sig = activity + spot.activity = activity if ref_id: activity_refs = [ ActivityRef( id=ref_id, - sig=activity, + activity=activity, # 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"] != "" else None, ) ] - spot.sig_refs = activity_refs + spot.activity_refs = activity_refs else: # If no actSiteID is set, e.g. because actClass is "QRP", sometimes we still have an actLocation @@ -128,9 +128,9 @@ class ParksNPeaks(HTTPSpotProvider): raise ValueError( "Parks N Peaks user ID and API key are required. Get yours from your Parks N Peaks account." ) - ref_id = spot.sig_refs[0].id if spot.sig_refs else "" + ref_id = spot.activity_refs[0].id if spot.activity_refs else "" body = { - "actClass": spot.sig or "", + "actClass": spot.activity or "", "actCallsign": spot.dx_call, "actSite": ref_id, "mode": spot.mode or "", diff --git a/providers/spot/pota.py b/providers/spot/pota.py index ef94d6f..8ed2be9 100644 --- a/providers/spot/pota.py +++ b/providers/spot/pota.py @@ -33,11 +33,11 @@ class POTA(HTTPSpotProvider): freq=float(source_spot["frequency"]) * 1000 if source_spot["frequency"] != "INVALID" else None, mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["comments"], - sig=ActivityName.POTA, - sig_refs=[ + activity=ActivityName.POTA, + activity_refs=[ ActivityRef( id=source_spot["reference"], - sig=ActivityName.POTA, + activity=ActivityName.POTA, name=source_spot["name"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -61,14 +61,14 @@ class POTA(HTTPSpotProvider): return activity == ActivityName.POTA def submit_spot(self, spot, credentials): - sig_ref = spot.sig_refs[0].id if spot.sig_refs else None - if sig_ref: + ref_id = spot.activity_refs[0].id if spot.activity_refs else None + if ref_id: body = { "activator": spot.dx_call, "spotter": spot.de_call, "frequency": str(spot.freq / 1000.0), "mode": spot.mode or "", - "reference": sig_ref, + "reference": ref_id, "comments": spot.comment or "", "source": "Spothole", } diff --git a/providers/spot/sota.py b/providers/spot/sota.py index 07d527e..d2f3e94 100644 --- a/providers/spot/sota.py +++ b/providers/spot/sota.py @@ -57,11 +57,11 @@ class SOTA(HTTPSpotProvider): # Seen SOTA spots with no frequency! mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["comments"], - sig=ActivityName.SOTA, - sig_refs=[ + activity=ActivityName.SOTA, + activity_refs=[ ActivityRef( id=source_spot["summitCode"], - sig=ActivityName.SOTA, + activity=ActivityName.SOTA, name=source_spot["summitName"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -92,10 +92,10 @@ class SOTA(HTTPSpotProvider): id_token = credentials.get("id_token", "") if not access_token or not id_token: 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: + ref_id = spot.activity_refs[0].id if spot.activity_refs else "" + if ref_id: # Split reference into association and summit codes - ref_split = sig_ref.split("/") + ref_split = ref_id.split("/") # Figure out a valid mode. Borrowed this from PoLo :) # https://github.com/ham2k/app-polo/blob/main/src/extensions/activities/sota/SOTAPostSelfSpot.js diff --git a/providers/spot/tiles.py b/providers/spot/tiles.py index 8e4e19c..69c71c1 100644 --- a/providers/spot/tiles.py +++ b/providers/spot/tiles.py @@ -59,13 +59,13 @@ class Tiles(HTTPSpotProvider): freq=freq, mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["notes"], - sig=ActivityName.TILES, + activity=ActivityName.TILES, # Tiles spots can include POTA & SOTA references, but ignore those on the basis that we will get them separately from the POTA/SOTA providers anyway. # Just take the grid reference itself as the single Tiles activity reference. - sig_refs=[ + activity_refs=[ ActivityRef( id=source_spot["maidenhead_grid"], - sig=ActivityName.TILES, + activity=ActivityName.TILES, name=source_spot["maidenhead_grid"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], diff --git a/providers/spot/towers.py b/providers/spot/towers.py index 945e366..d81c04d 100644 --- a/providers/spot/towers.py +++ b/providers/spot/towers.py @@ -34,8 +34,10 @@ class Towers(HTTPSpotProvider): dx_call=source_spot["call"].upper(), freq=likely_freq, comment=source_spot["comment"], - sig=ActivityName.TOWERS, - sig_refs=[ActivityRef(id=source_spot["ref"], sig=ActivityName.TOWERS, ref_type=ActivityRefType.TOWER)], + activity=ActivityName.TOWERS, + activity_refs=[ + ActivityRef(id=source_spot["ref"], activity=ActivityName.TOWERS, ref_type=ActivityRefType.TOWER) + ], time=datetime.strptime(response_json["updated"][:10] + source_spot["time"], "%Y-%m-%d%H:%M") .replace(tzinfo=pytz.utc) .timestamp(), diff --git a/providers/spot/wota.py b/providers/spot/wota.py index 52a6f46..8ac2bc6 100644 --- a/providers/spot/wota.py +++ b/providers/spot/wota.py @@ -92,9 +92,13 @@ class WOTA(HTTPSpotProvider): freq=freq_hz, mode=Mode.from_name(mode), comment=comment, - sig=ActivityName.WOTA, - sig_refs=( - [ActivityRef(id=ref, sig=ActivityName.WOTA, name=ref_name, ref_type=ActivityRefType.SUMMIT)] + activity=ActivityName.WOTA, + activity_refs=( + [ + ActivityRef( + id=ref, activity=ActivityName.WOTA, name=ref_name, ref_type=ActivityRefType.SUMMIT + ) + ] if ref else [] ), diff --git a/providers/spot/wwbota.py b/providers/spot/wwbota.py index eab901e..01b9c52 100644 --- a/providers/spot/wwbota.py +++ b/providers/spot/wwbota.py @@ -23,7 +23,7 @@ class WWBOTA(SSESpotProvider): for ref in source_spot["references"]: activity_ref = ActivityRef( id=ref["reference"], - sig=ActivityName.WWBOTA, + activity=ActivityName.WWBOTA, name=ref["name"], latitude=ref["lat"], longitude=ref["long"], @@ -38,8 +38,8 @@ class WWBOTA(SSESpotProvider): freq=float(source_spot["freq"]) * 1000000, mode=Mode.from_name(source_spot["mode"].upper()) if source_spot.get("mode") else None, comment=source_spot["comment"], - sig=ActivityName.WWBOTA, - sig_refs=refs, + activity=ActivityName.WWBOTA, + activity_refs=refs, time=datetime.fromisoformat(source_spot["time"].replace("Z", "+00:00")).timestamp(), # WWBOTA spots can contain multiple references for bunkers being activated simultaneously. For # now, we will just pick the first one to use as our grid, latitude and longitude. diff --git a/providers/spot/wwff.py b/providers/spot/wwff.py index eebc651..c7d1f78 100644 --- a/providers/spot/wwff.py +++ b/providers/spot/wwff.py @@ -30,11 +30,11 @@ class WWFF(HTTPSpotProvider): freq=float(source_spot["frequency_khz"]) * 1000, mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["remarks"], - sig=ActivityName.WWFF, - sig_refs=[ + activity=ActivityName.WWFF, + activity_refs=[ ActivityRef( id=source_spot["reference"], - sig=ActivityName.WWFF, + activity=ActivityName.WWFF, name=source_spot["reference_name"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], diff --git a/providers/spot/xota.py b/providers/spot/xota.py index 292b794..b394d89 100644 --- a/providers/spot/xota.py +++ b/providers/spot/xota.py @@ -14,16 +14,18 @@ class XOTA(WebsocketSpotProvider): The provider typically doesn't give us a lat/lon or activity explicitly, so our own config provides an activity which we can then use for lookups. This functionality is implemented for Toilets on the Air events, of which there are several - so a plain lookup of a "TOTA reference" doesn't make sense, it depends on which TOTA, which - is why we also provide a sig_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.""" + 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 def __init__(self, provider_config): name = provider_config.get("name", "xOTA") super().__init__(name, provider_config, provider_config["url"]) - self.ACTIVITY = str(provider_config["sig"]) if "sig" in provider_config else None - self._activity_ref_prefix = str(provider_config["sig_ref_prefix"]) if "sig_ref_prefix" in provider_config else "" + self.ACTIVITY = str(provider_config["activity"]) if "activity" in provider_config else None + self._activity_ref_prefix = ( + str(provider_config["activity_ref_prefix"]) if "activity_ref_prefix" in provider_config else "" + ) def _ws_message_to_spot(self, b): string = b.decode("utf-8") @@ -35,11 +37,11 @@ class XOTA(WebsocketSpotProvider): dx_call=source_spot["stationCallSign"].upper(), freq=float(source_spot["freq"]) * 1000, mode=Mode.from_name(source_spot["mode"].upper()), - sig=self.ACTIVITY, - sig_refs=[ + activity=self.ACTIVITY, + activity_refs=[ ActivityRef( id=ref_id, - sig=self.ACTIVITY or "", + activity=self.ACTIVITY or "", url=source_spot["reference"]["website"], ) ], diff --git a/providers/spot/zlota.py b/providers/spot/zlota.py index 671ab09..407a016 100644 --- a/providers/spot/zlota.py +++ b/providers/spot/zlota.py @@ -35,11 +35,11 @@ class ZLOTA(HTTPSpotProvider): freq=freq_hz, mode=Mode.from_name(source_spot["mode"].upper().strip()), comment=source_spot["comments"], - sig=ActivityName.ZLOTA, - sig_refs=[ + activity=ActivityName.ZLOTA, + activity_refs=[ ActivityRef( id=source_spot["reference"], - sig=ActivityName.ZLOTA, + activity=ActivityName.ZLOTA, name=source_spot["name"], ) ], diff --git a/pyproject.toml b/pyproject.toml index 51e7c97..4a8cd0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "spothole" -version = "2.2" +version = "3.0-pre" authors = [ { name = "Ian Renton", email = "ian@ianrenton.com" }, ] diff --git a/static/apidocs/openapi.yml b/static/apidocs/openapi.yml index 25922e3..b0be74e 100644 --- a/static/apidocs/openapi.yml +++ b/static/apidocs/openapi.yml @@ -15,6 +15,25 @@ info: ## Changelog + ### 3.0 + + The term "SIG" (Special Interest Group), which Spothole inherited from ADIF, has been replaced with "activity" throughout the API. + + * **Breaking change:** In spot and alert data, `sig` has been renamed to `activity` and `sig_refs` to `activity_refs`. + * **Breaking change:** In activity reference data (i.e. each entry in `activity_refs`, and the response of the activity reference lookup), `sig` has been renamed to `activity`. + * **Breaking change:** The `dx_location_source` value "SIG REF LOOKUP" has been renamed to "ACTIVITY REF LOOKUP". + * **Breaking change:** The `/spots`, `/spots/stream`, `/alerts` and `/alerts/stream` query parameter `sig` has been renamed to `activity`, and its special value `NO_SIG` to `NO_ACTIVITY`. The `/spots` and `/spots/stream` query parameters `needs_sig` and `needs_sig_ref` have been renamed to `needs_activity` and `needs_activity_ref`. When using the `fields` query parameter, use the new field names `activity` and `activity_refs`. + * **Breaking change:** `/lookup/sigref` has been renamed to `/lookup/activityref`, and its `sig` query parameter has been renamed to `activity`. + * **Breaking change:** POST `/spot` now expects `activity` and `activity_refs` in the `spot` object, rather than `sig` and `sig_refs`. + * **Breaking change:** In the `/options` response, `sigs` has been renamed to `activities`, and within each activity, `sig_type` has been renamed to `activity_type`. + * **Breaking change:** In the `/status` response, `sig_ref_data_providers` has been renamed to `activity_ref_data_providers`, and within each provider, `sig_name` has been renamed to `activity_name`. + + #### Upgrading a client from v2 to v3 API endpoints + + In v3.0 of Spothole, the `v2` (and `v1`) API endpoints will be maintained for backwards compatibility, so if you have written a client against the `v2` API, it will continue to receive `sig`, `sig_refs` etc. as before. However, you are encouraged to move to the `v3` API endpoints as soon as possible. + + To upgrade, replace `v2` with `v3` in the URLs your code calls, then rename any use of the fields, query parameters and values listed above. If you use the activity reference lookup, call `/lookup/activityref?activity=...&id=...` instead of `/lookup/sigref?sig=...&id=...`. + ### 2.2 * Renamed AMSAT SIG to "Satellite" as AMSAT is a specific organisation not just a general term for satellite QSOs @@ -102,10 +121,10 @@ info: license: name: The Unlicense url: https://unlicense.org/#the-unlicense - version: 2.0 + version: 3.0 servers: - - url: https://spothole.app/api/v2 + - url: https://spothole.app/api/v3 tags: - name: Spots @@ -396,7 +415,7 @@ paths: example: "Failed" - /lookup/sigref: + /lookup/activityref: get: tags: - Utilities @@ -405,7 +424,7 @@ paths: Perform a lookup of data about a certain reference, providing the activity and the ID of the reference. An ActivityRef structure will be returned containing the activity and ID, plus any other information Spothole could find about it. - operationId: sigref + operationId: activityref parameters: - $ref: '#/components/parameters/ActivityRefLookupActivity' - $ref: '#/components/parameters/ActivityRefLookupId' @@ -461,7 +480,7 @@ paths: Supply a JSON object containing a `spot` sub-object (the spot data) and an optional `handling` sub-object containing server-side instructions such as upstream submission). Check `spot_submit_providers` in the `/options` response to see which activities and providers support upstream submission. cURL example: - `curl --request POST --header \"Content-Type: application/json\" --data '{\"spot\":{\"dx_call\":\"M0TRT\",\"time\":1760019539,\"freq\":14200000,\"comment\":\"Test spot please ignore\",\"de_call\":\"M0TRT\"}}' https://spothole.app/api/v2/spot`" + `curl --request POST --header \"Content-Type: application/json\" --data '{\"spot\":{\"dx_call\":\"M0TRT\",\"time\":1760019539,\"freq\":14200000,\"comment\":\"Test spot please ignore\",\"de_call\":\"M0TRT\"}}' https://spothole.app/api/v3/spot`" operationId: spot requestBody: description: Object containing a "spot" sub-object with the spot data, and an optional "handling" sub-object with server-side instructions of what to do with it. @@ -559,32 +578,32 @@ components: schema: $ref: "#/components/schemas/Source" SpotActivity: - name: sig + name: activity in: query description: > Limit the spots to only ones from one or more activities provided as an argument. - To select more than one activity, supply a comma-separated list. The special `sig` name `NO_SIG` - matches spots with no activity set. You can use `sig=NO_SIG` to specifically only return generic + To select more than one activity, supply a comma-separated list. The special `activity` name `NO_ACTIVITY` + matches spots with no activity set. You can use `activity=NO_ACTIVITY` to specifically only return generic spots with no associated activity. You can also use combinations to request for example POTA + no activity, but reject other activities. If you want to request 'every activity and not No Activity', see the - `needs_sig` query parameter for a shortcut. + `needs_activity` query parameter for a shortcut. schema: - $ref: "#/components/schemas/ActivityNameIncludingNoSig" + $ref: "#/components/schemas/ActivityNameIncludingNoActivity" SpotNeedsActivity: - name: needs_sig + name: needs_activity in: query description: > Limit the spots to only ones with an activity such as POTA. Because supplying all - known activities as a `sigs` parameter is unwieldy, and leaving `sigs` blank will also return spots + known activities as an `activity` parameter is unwieldy, and leaving `activity` blank will also return spots with *no* activity, this parameter can be set true to return only spots with an activity, regardless of - what it is, so long as it's not blank. This is the equivalent of supplying the `sig` query - param with a list of every known activity apart from the special `NO_SIG` value. This is what Field + what it is, so long as it's not blank. This is the equivalent of supplying the `activity` query + param with a list of every known activity apart from the special `NO_ACTIVITY` value. This is what Field Spotter uses to exclude generic cluster spots and only retrieve xOTA things. schema: type: boolean default: false SpotNeedsActivityRef: - name: needs_sig_ref + name: needs_activity_ref in: query description: > Limit the spots to only ones which have at least one reference (e.g. a park reference) for @@ -723,14 +742,14 @@ components: schema: $ref: "#/components/schemas/Source" AlertActivity: - name: sig + name: activity in: query description: > Limit the alerts to only ones from one or more activities. To select more than one - activity, supply a comma-separated list. The special value 'NO_SIG' can be included to return alerts - specifically without an associated activity (i.e. general DXpeditions). + activity, supply a comma-separated list. The special value 'NO_ACTIVITY' can be included to return alerts + specifically without an associated activity. schema: - $ref: "#/components/schemas/ActivityNameIncludingNoSig" + $ref: "#/components/schemas/ActivityNameIncludingNoActivity" AlertDxContinent: name: dx_continent in: query @@ -839,9 +858,9 @@ components: type: string example: M0TRT ActivityRefLookupActivity: - name: sig + name: activity in: query - description: Activity, e.g. outdoor activity programme such as POTA (still named "sig" in the API for backwards compatibility) + description: Activity, e.g. outdoor activity programme such as POTA required: true schema: $ref: "#/components/schemas/ActivityName" @@ -938,11 +957,11 @@ components: - EVENT example: TRADITIONAL - ActivityNameIncludingNoSig: + ActivityNameIncludingNoActivity: oneOf: - $ref: "#/components/schemas/ActivityName" - type: string - enum: [ NO_SIG ] + enum: [ NO_ACTIVITY ] example: POTA ActivityRefType: @@ -1068,7 +1087,7 @@ components: type: string enum: - SPOT - - "SIG REF LOOKUP" + - "ACTIVITY REF LOOKUP" - "GRID" - "HOME QTH" - DXCC @@ -1090,8 +1109,8 @@ components: type: string description: Activity reference ID. example: GB-0001 - sig: - description: Activity that this reference is in. Still named "sig" in the API for backwards compatibility. + activity: + description: Activity that this reference is in. $ref: "#/components/schemas/ActivityName" name: type: string @@ -1205,7 +1224,7 @@ components: itself, or from a lookup of the activity ref (e.g. park) it's likely quite accurate, but if we had to fall back to QRZ lookup, or even a location based on the DXCC itself, it will be a lot less accurate. "SPOT" indicates the location source was the spot itself from the - spotting service. "SIG REF LOOKUP" indicates that the spot didn't provide a location, + spotting service. "ACTIVITY REF LOOKUP" indicates that the spot didn't provide a location, but we looked it up from reference data. "GRID" indicates that the spot provided some location data such as a Maidenhead, UK Ordnance Survey or Irish grid reference, but the location is likely less accurate than "SPOT". "HOME QTH" indicates we looked up the DX @@ -1218,7 +1237,7 @@ components: type: boolean description: > Does the software think the location is good enough to put a marker on a map? This is - true if the source is "SPOT", "SIG REF LOOKUP" or "GRID", or alternatively if + true if the source is "SPOT", "ACTIVITY REF LOOKUP" or "GRID", or alternatively if the source is "HOME QTH" and the callsign doesn't have a slash in it (i.e. operator likely at home). example: true @@ -1315,14 +1334,14 @@ components: type: string description: Comment left by the spotter, if any example: "59 in NY 73" - sig: - description: Activity, e.g. outdoor activity programme such as POTA (still named "sig" in the API for backwards compatibility) + activity: + description: Activity, e.g. outdoor activity programme such as POTA $ref: "#/components/schemas/ActivityName" - sig_refs: + activity_refs: type: array items: $ref: '#/components/schemas/ActivityRef' - description: Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. Still named "sig_refs" in the API for backwards compatibility. + description: Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. qrt: type: boolean description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments. @@ -1364,7 +1383,7 @@ components: type: boolean description: > If true, forward the spot to an external upstream provider (e.g. POTA, SOTA) rather - than only adding it to this Spothole server. Requires `sig`, at least one `sig_refs` + than only adding it to this Spothole server. Requires `activity`, at least one `activity_refs` entry, and `upstream_provider` to be set. Check `spot_submit_providers` in the /options response to see which activities and providers support this. default: false @@ -1503,14 +1522,14 @@ components: type: string description: Comment made by the activator, if any example: "2025 DXpedition to null island" - sig: - description: Activity, e.g. outdoor activity programme such as POTA (still named "sig" in the API for backwards compatibility) + activity: + description: Activity, e.g. outdoor activity programme such as POTA $ref: "#/components/schemas/ActivityName" - sig_refs: + activity_refs: type: array items: $ref: '#/components/schemas/ActivityRef' - description: Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. Still named "sig_refs" in the API for backwards compatibility. + description: Activity references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO. url: type: string description: A URL linking to more information about the alert, e.g. DXpedition or contest info. @@ -1604,8 +1623,8 @@ components: Activity: type: object description: > - Represents an activity (a term which replaces the older "Special Interest Group" or "SIG" terminology, - though `sig`-prefixed field names remain for API backwards compatibility). + Represents an activity, such as an outdoor activity programme (e.g. POTA), or another kind of operating + (e.g. Contest, DXpedition). properties: name: description: The abbreviated name of the activity @@ -1614,12 +1633,11 @@ components: type: string description: The full name of the activity example: Parks on the Air - sig_type: - type: boolean + activity_type: description: > Whether the activity is traditional (e.g. EME), adventure (e.g. POTA), regional (e.g. WAB), or for a specific event (e.g. MOTA). Generally for Spothole's own internal use, clients probably won't need this. - Used to group them in the web UI. Still named "sig_type" in the API for backwards compatibility. + Used to group them in the web UI. $ref: "#/components/schemas/ActivityType" has_refs: type: boolean @@ -1998,7 +2016,7 @@ components: StaticDataProviderStatus: type: object properties: - sig_name: + name: type: string description: The name of the provider. example: K0SWE @@ -2020,9 +2038,9 @@ components: ActivityRefDataProviderStatus: type: object properties: - sig_name: + activity_name: type: string - description: The name of the activity. Still named "sig_name" in the API for backwards compatibility. + description: The name of the activity. example: WWFF enabled: type: boolean @@ -2046,7 +2064,7 @@ components: CallsignDataProviderStatus: type: object properties: - sig_name: + name: type: string description: The name of the provider. example: Country Files @@ -2185,7 +2203,7 @@ components: description: An array of all the static reference data providers. items: $ref: '#/components/schemas/StaticDataProviderStatus' - sig_ref_data_providers: + activity_ref_data_providers: type: array description: An array of all the activity reference data providers. items: @@ -2216,7 +2234,7 @@ components: items: type: string example: "PHONE" - sigs: + activities: type: array description: An array of all the supported activities. items: @@ -2254,7 +2272,7 @@ components: type: integer description: > The maximum age, in seconds, of any spot before it will be deleted by the system. When - querying the /api/v2/spots endpoint and providing a "max_age" or "since" parameter, there + querying the /api/v3/spots endpoint and providing a "max_age" or "since" parameter, there is no point providing a number larger than this, because the system drops all spots older than this. example: 3600 diff --git a/static/js/add-spot.js b/static/js/add-spot.js index 1d0130f..ebec4b4 100644 --- a/static/js/add-spot.js +++ b/static/js/add-spot.js @@ -29,7 +29,7 @@ const PROVIDER_CREDENTIAL_SCHEMAS = { // Load server options. Once a successful callback is made from this, we can populate the choice boxes in the form and load // any saved values from local storage. function loadOptions() { - $.getJSON('/api/v2/options', function (jsonData) { + $.getJSON('/api/v3/options', function (jsonData) { // Store options options = jsonData; @@ -42,8 +42,8 @@ function loadOptions() { }); // Populate activity drop-down - $.each(options["sigs"], function (i, activity) { - $('#sig').append($('