diff --git a/core/activity_lookup_helper.py b/core/activity_lookup_helper.py index c90d934..10183f1 100644 --- a/core/activity_lookup_helper.py +++ b/core/activity_lookup_helper.py @@ -3,9 +3,9 @@ import re from pyhamtools.locator import latlong_to_locator, locator_to_latlong -from core.constants import ACTIVITIES +from core.activity_utils import get_activity_by_name from core.data_store import DATA_STORE -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from core.geo_utils import wab_wai_square_to_lat_lon from data.activity_ref import ActivityRef @@ -30,10 +30,10 @@ def get_activity_ref_info(activity_name, ref_id): activity_ref = ActivityRef(sig=activity_name, id=ref_id) # We can always get the reference type and the icon from the activity itself - for activity in ACTIVITIES: - if activity.name.upper() == activity_name.upper(): - activity_ref.ref_type = activity.ref_type - activity_ref.icon = activity.icon + activity = get_activity_by_name(activity_name) + if activity: + activity_ref.ref_type = activity.ref_type + activity_ref.icon = activity.icon try: ### FUDGES ### @@ -41,7 +41,7 @@ def get_activity_ref_info(activity_name, ref_id): # DME fudge. Our database has leading zeros padding to 5 digits which is the expected format, but not all # activators add leading zeros. We also need to normalise "DME 01234" to "DME-01234" to match what's in our # database. - if activity_name.upper() == "DME": + if activity_name.upper() == ActivityName.DME: match = re.match(r"DME[\- ](\d{3,5})", ref_id, re.IGNORECASE) if match: number = match.group(1) @@ -49,21 +49,21 @@ def get_activity_ref_info(activity_name, ref_id): # DTMBA spotters sometimes include spaces and dashes, our regex allows them but they must be removed here so we # can look up against the official list which doesn't have them - if activity_name.upper() == "DTMBA": + if activity_name.upper() == ActivityName.DTMBA: ref_id = ref_id.replace("-", "").replace(" ", "") ### NO DATA ACTIVITIES ### # # If the activity is HEMA or BIWOTA, we have no way to either generate useful data or look it up on a # reference list, so just skip the lookup here. - if activity_name.upper() == "HEMA" or activity_name.upper() == "BIWOTA": + if activity_name.upper() == ActivityName.HEMA or activity_name.upper() == ActivityName.BIWOTA: return activity_ref ### PROGRAMMATIC DATA GENERATION INSTEAD OF LOOKUPS ### # # If the activity is Tiles, WAB, WAI or BOTA (Beaches), we don't have anything to look up from the data # store, we can calculate all the information we are going to get directly. - if activity_name.upper() == "TILES": + if activity_name.upper() == ActivityName.TILES.upper(): # Tiles on the Air just uses Maidenhead 6-digit squares, so ID, Name and Grid are all the same if not activity_ref.name: activity_ref.name = activity_ref.id @@ -75,7 +75,7 @@ def get_activity_ref_info(activity_name, ref_id): activity_ref.longitude = ll[1] return activity_ref - elif activity_name.upper() == "WAB" or activity_name.upper() == "WAI": + elif activity_name.upper() == ActivityName.WAB or activity_name.upper() == ActivityName.WAI: ll = wab_wai_square_to_lat_lon(ref_id) if ll: activity_ref.name = ref_id @@ -87,7 +87,7 @@ def get_activity_ref_info(activity_name, ref_id): logger.warning("Invalid lat/lon received for WAB/WAI reference") return activity_ref - elif activity_name.upper() == "BOTA": + elif activity_name.upper() == ActivityName.BOTA: # For BOTA all we can ever generate is the URL, there is no data file or lookup for lat/longs if not activity_ref.name: activity_ref.name = activity_ref.id @@ -97,11 +97,11 @@ def get_activity_ref_info(activity_name, ref_id): ) return activity_ref - elif activity_name.upper() == "GMA Islands": + elif activity_name.upper() == ActivityName.GMA_ISLANDS.upper(): # GMA Islands is a bit of a mess of GMA and IOTA references. Try looking them both up and see what returns # the best result. - iota_lookup = get_activity_ref_info("IOTA", ref_id) - gma_lookup = get_activity_ref_info("GMA", ref_id) + iota_lookup = get_activity_ref_info(ActivityName.IOTA, ref_id) + gma_lookup = get_activity_ref_info(ActivityName.GMA, ref_id) for key, value in iota_lookup.__dict__.items(): if value is not None and activity_ref.__dict__.get(key) is None: activity_ref.__dict__[key] = value diff --git a/core/activity_utils.py b/core/activity_utils.py index d641360..4626005 100644 --- a/core/activity_utils.py +++ b/core/activity_utils.py @@ -1,23 +1,31 @@ -from core.constants import ACTIVITIES +from data.activities import ACTIVITIES + + +def get_activity_by_name(name): + """Utility function to resolve an arbitrary, case-insensitive activity name string (e.g. from a spot comment, a + provider, or an API request) to the matching known Activity. Returns None if no match is found.""" + + if not name: + return None + for activity_name, activity in ACTIVITIES.items(): + if activity_name.upper() == name.upper(): + return activity + return None def get_ref_regex_for_activity(activity): """Utility function to get the regex string for an activity reference for a named activity. If no match is found, None will be returned.""" - for a in ACTIVITIES: - if a.name.upper() == activity.upper(): - return a.ref_regex - return None + found = get_activity_by_name(activity) + return found.ref_regex if found else None def get_icon_for_activity(activity): """Utility function to get the icon for a named activity. If no match is found, None will be returned.""" - for a in ACTIVITIES: - if a.name.upper() == activity.upper(): - return a.icon - return None + found = get_activity_by_name(activity) + return found.icon if found else None def get_activity_name_from_comment_name(activity): @@ -25,11 +33,11 @@ def get_activity_name_from_comment_name(activity): but there are some cases (e.g. is "TOTA" Towers, Tiles or Toilets?) where we need to transform one to the other.""" - for a in ACTIVITIES: + for activity_name, a in ACTIVITIES.items(): if any(n.upper() == activity.upper() for n in a.comment_names): - return a.name + return activity_name return None # Regex matching any activity's "comment name", i.e. how it may be referred to in spot comments -ANY_ACTIVITY_REGEX = rf"({'|'.join(n for a in ACTIVITIES for n in a.comment_names)})" +ANY_ACTIVITY_REGEX = rf"({'|'.join(n for a in ACTIVITIES.values() for n in a.comment_names)})" diff --git a/core/constants.py b/core/constants.py index a66d916..eaaff3f 100644 --- a/core/constants.py +++ b/core/constants.py @@ -1,6 +1,4 @@ from core.config import SERVER_OWNER_CALLSIGN -from core.enums import ActivityRefType, ActivityType -from data.activity import Activity from data.band import Band # General software @@ -10,442 +8,6 @@ SOFTWARE_VERSION = "2.2-pre" HTTP_HEADERS = {"User-Agent": f"Spothole v{SOFTWARE_VERSION} (operated by {SERVER_OWNER_CALLSIGN})"} HAMQTH_PRG = f"Spothole v{SOFTWARE_VERSION} operated by {SERVER_OWNER_CALLSIGN}".replace(" ", "_") -# Activities -ACTIVITIES = [ - Activity( - name="Contest", - comment_names=["CONTEST"], - description="Contest", - sig_type=ActivityType.TRADITIONAL, - icon="fa-trophy", - refs_globally_unique=False, - ), - Activity( - name="DXpedition", - comment_names=[], - description="Radio expedition to a remote location", - sig_type=ActivityType.TRADITIONAL, - icon="fa-book-atlas", - refs_globally_unique=False, - ), - Activity( - name="Satellite", - comment_names=[], - description="Amateur Radio Satellite", - sig_type=ActivityType.TRADITIONAL, - icon="fa-satellite", - refs_globally_unique=False, - ), - Activity( - name="EME", - comment_names=[], - description="Earth-Moon-Earth (Moonbounce)", - sig_type=ActivityType.TRADITIONAL, - icon="fa-moon", - refs_globally_unique=False, - ), - Activity( - name="/AM", - comment_names=[], - description="Aeronautical Mobile", - sig_type=ActivityType.TRADITIONAL, - icon="fa-plane", - refs_globally_unique=False, - ), - Activity( - name="/MM", - comment_names=[], - description="Maritime Mobile", - sig_type=ActivityType.TRADITIONAL, - icon="fa-sailboat", - refs_globally_unique=False, - ), - Activity( - name="QRP", - comment_names=["QRP"], - description="Low power", - sig_type=ActivityType.TRADITIONAL, - icon="fa-volume-low", - refs_globally_unique=False, - ), - Activity( - name="POTA", - comment_names=["POTA"], - description="Parks on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.PARK, - ref_regex=r"[A-Z]{2}\-\d{4,5}|K\-TEST", - icon="fa-tree", - refs_globally_unique=False, - ), - Activity( - name="SOTA", - comment_names=["SOTA"], - description="Summits on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.SUMMIT, - ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", - icon="fa-mountain-sun", - refs_globally_unique=False, - ), - Activity( - name="WWFF", - comment_names=["WWFF"], - description="World Wide Flora & Fauna", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.PARK, - ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}", - icon="fa-seedling", - refs_globally_unique=True, - ), - Activity( - name="GMA", - comment_names=["GMA"], - description="Global Mountain Activity", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.SUMMIT, - ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", - icon="fa-person-hiking", - refs_globally_unique=False, - ), - Activity( - name="WWBOTA", - comment_names=["WWBOTA", "BOTA"], - description="Worldwide Bunkers on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.BUNKER, - ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}", - icon="fa-radiation", - refs_globally_unique=True, - ), - Activity( - name="HEMA", - comment_names=["HEMA"], - description="HuMPs Excluding Marilyns Award", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.SUMMIT, - ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}", - icon="fa-mound", - refs_globally_unique=False, - ), - Activity( - name="IOTA", - comment_names=["IOTA"], - description="Islands on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.ISLAND, - ref_regex=r"[A-Z]{2}\-\d{3}", - icon="fa-book-atlas", - refs_globally_unique=False, - ), - Activity( - name="GMA Islands", - comment_names=[], - description="Global Mountain Activity - Islands", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.ISLAND, - ref_regex=r"(([A-Z]{2}\-\d{3})|([A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}))", - icon="fa-person-hiking", - refs_globally_unique=False, - ), - Activity( - name="ARLHS", - comment_names=["ARLHS"], - description="Amateur Radio Lighthouse Society", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.LIGHTHOUSE, - ref_regex=r"[A-Z]{3}[\- ]\d{3,4}", - icon="fa-house-flood-water", - refs_globally_unique=False, - ), - Activity( - name="ILLW", - comment_names=["ILLW"], - description="International Lighthouse & Lightship Weekend", - sig_type=ActivityType.EVENT, - ref_type=ActivityRefType.LIGHTHOUSE, - ref_regex=r"[A-Z]{2}\d{4}", - icon="fa-house-flood-water", - refs_globally_unique=False, - ), - Activity( - name="MOTA", - comment_names=["MOTA"], - description="Mills on the Air", - sig_type=ActivityType.EVENT, - ref_type=ActivityRefType.MILL, - ref_regex=r"X\d{4,6}", - icon="fa-fan", - refs_globally_unique=True, - ), - Activity( - name="SIOTA", - comment_names=["SIOTA"], - description="Silos on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.SILO, - ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d", - icon="fa-wheat-awn", - refs_globally_unique=False, - ), - Activity( - name="WCA", - comment_names=["WCA"], - description="World Castles Award", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.CASTLE, - ref_regex=r"[A-Z0-9]{1,3}\-\d{5}", - icon="fa-chess-rook", - refs_globally_unique=False, - ), - Activity( - name="ZLOTA", - comment_names=["ZLOTA"], - description="New Zealand on the Air", - sig_type=ActivityType.REGIONAL, - ref_type=None, - ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}", - icon="fa-kiwi-bird", - region_flag="🇳🇿", - refs_globally_unique=True, - ), - Activity( - name="WOTA", - comment_names=["WOTA"], - description="Wainwrights on the Air", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.SUMMIT, - ref_regex=r"[A-Z]{3}-[0-9]{2}", - icon="fa-w", - region_flag="🇬🇧", - refs_globally_unique=False, - ), - Activity( - name="BOTA", - comment_names=[], - description="Beaches on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.BEACH, - icon="fa-umbrella-beach", - refs_globally_unique=False, - ), - Activity( - name="KRMNPA", - comment_names=["KRMNPA"], - description="Keith Roget Memorial National Parks Award", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.PARK, - ref_regex=r"VKFF\-\d{4}", - icon="fa-earth-oceania", - region_flag="🇦🇺", - refs_globally_unique=False, - ), - Activity( - name="SANPCPA", - comment_names=["SANPCPA"], - description="South Australian National Parks and Conservation Parks Award", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.PARK, - ref_regex=r"VKFF\-\d{4}", - icon="fa-earth-oceania", - region_flag="🇦🇺", - refs_globally_unique=False, - ), - Activity( - name="LLOTA", - comment_names=["LLOTA"], - description="Lagos y Lagunas on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.LAKE, - ref_regex=r"LL[A-Z]{2}\-\d{4}", - icon="fa-water", - refs_globally_unique=True, - ), - Activity( - name="Towers", - comment_names=["TOTA"], - description="Towers on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.TOWER, - ref_regex=r"[A-Z]{2,3}R\-\d{4}", - icon="fa-tower-observation", - refs_globally_unique=False, - ), - Activity( - name="Tiles", - comment_names=[], - description="Tiles on the Air", - sig_type=ActivityType.ADVENTURE, - ref_type=ActivityRefType.GRID, - ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}", - icon="fa-square", - refs_globally_unique=False, - ), - Activity( - name="RaDAR Rally", - comment_names=["RaDAR"], - description="RaDAR Rally", - sig_type=ActivityType.EVENT, - icon="fa-headset", - refs_globally_unique=False, - ), - Activity( - name="WAB", - comment_names=["WAB"], - description="Worked All Britain", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.GRID, - ref_regex=r"[A-Z]{1,2}[0-9]{2}", - icon="fa-table-cells-large", - region_flag="🇬🇧", - refs_globally_unique=False, - ), - Activity( - name="WAI", - comment_names=["WAI"], - description="Worked All Ireland", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.GRID, - ref_regex=r"[A-Z][0-9]{2}", - icon="fa-table-cells-large", - region_flag="🇮🇪", - refs_globally_unique=False, - ), - Activity( - name="DMF", - comment_names=["DMF"], - description="Diplôme des Moulins de France", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.MILL, - icon="fa-fan", - region_flag="🇫🇷", - refs_globally_unique=False, - ), - Activity( - name="DME", - comment_names=["DME"], - description="Diploma Municipios de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.TOWN, - ref_regex=r"DME[\- ]\d{3,5}", - icon="fa-building", - region_flag="🇪🇸", - refs_globally_unique=True, - ), - Activity( - name="FEA", - comment_names=["FEA"], - description="Diploma Faros de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.LIGHTHOUSE, - # FEA references are technically [DE]\-\d{4}(\.\d)? but spotters always seem to miss out the D- or E- - # prefix and just use FEA-1234 or FEA 1234, so allow for that. The FEA activity ref data provider adds both - # forms to the database. - ref_regex=r"([DE]|FEA)[\- ]\d{4}(\.\d)?", - icon="fa-house-flood-water", - region_flag="🇪🇸", - refs_globally_unique=True, - ), - Activity( - name="DMUE", - comment_names=["DMUE"], - description="Diploma Museos de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.BUILDING, - ref_regex=r"MUE[A-Z]{2}-\d{3}", - icon="fa-landmark", - region_flag="🇪🇸", - refs_globally_unique=True, - ), - Activity( - name="DMVE", - comment_names=["DMVE"], - description="Diploma Monumentos y Vestigios de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.BUILDING, - ref_regex=r"MV[A-Z]{1,2}-\d{4}", - icon="fa-monument", - region_flag="🇪🇸", - refs_globally_unique=True, - ), - Activity( - name="DCE", - comment_names=["DCE"], - description="Diploma Castillos de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.CASTLE, - ref_regex=r"C[A-Z]{1,2}-\d{3}", - icon="fa-chess-rook", - region_flag="🇪🇸", - refs_globally_unique=False, - ), - Activity( - name="DEFE", - comment_names=["DEFE"], - description="Diploma Estaciones de Ferrocarril de España", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.BUILDING, - ref_regex=r"EF[A-Z]{1,2}-\d{3}", - icon="fa-train", - region_flag="🇪🇸", - refs_globally_unique=True, - ), - Activity( - name="DTMBA", - comment_names=["DTMBA"], - description="Diploma Teatri Musei e Belle Arti", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.BUILDING, - ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}", - icon="fa-landmark", - region_flag="🇮🇹", - refs_globally_unique=True, - ), - Activity( - name="BIWOTA", - comment_names=["BIWOTA"], - description="British Inland Waterways on the Air", - sig_type=ActivityType.EVENT, - ref_type=ActivityRefType.WATERWAY, - icon="fa-ship", - region_flag="🇬🇧", - refs_globally_unique=False, - ), - Activity( - name="COTA", - comment_names=["COTA"], - description="Castles on the Air", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.CASTLE, - ref_regex=r"[A-Z]{3}\-[0-9]{3,5}", - icon="fa-chess-rook", - region_flag="🇩🇪", - refs_globally_unique=False, - ), - Activity( - name="PGA", - comment_names=["PGA"], - description="Polish Gmina Award", - sig_type=ActivityType.REGIONAL, - ref_type=ActivityRefType.REGION, - ref_regex=r"[A-Z]{2}[0-9]{2}", - icon="fa-g", - region_flag="🇵🇱", - refs_globally_unique=False, - ), - Activity( - name="Toilets", - comment_names=[], - description="Toilets on the Air", - sig_type=ActivityType.EVENT, - ref_type=ActivityRefType.TOILET, - ref_regex=r"T\-[0-9]{2}", - icon="fa-toilet", - region_flag="🏴‍☠️", - refs_globally_unique=True, - ), -] - # Band definitions BANDS = [ Band(name="2200m", start_freq=135700, end_freq=137800), diff --git a/core/enums.py b/core/enums.py index e985ac7..d93e104 100644 --- a/core/enums.py +++ b/core/enums.py @@ -90,6 +90,60 @@ class LocationSourceForCallsign(str, Enum): DXCC = "DXCC" +# Definitions of every activity Spothole knows about, keyed by the ActivityName enum. An enum is used for the +# names to avoid typos when using literal strings like "POTA" all over the place. +class ActivityName(str, Enum): + """Canonical name of every activity. Spothole uses these rather than literals like "POTA" around the code + to ensure I don't accidentally introduce typos.""" + + CONTEST = "Contest" + DXPEDITION = "DXpedition" + SATELLITE = "Satellite" + EME = "EME" + AERONAUTICAL_MOBILE = "/AM" + MARITIME_MOBILE = "/MM" + QRP = "QRP" + POTA = "POTA" + SOTA = "SOTA" + WWFF = "WWFF" + GMA = "GMA" + WWBOTA = "WWBOTA" + HEMA = "HEMA" + IOTA = "IOTA" + GMA_ISLANDS = "GMA Islands" + ARLHS = "ARLHS" + ILLW = "ILLW" + MOTA = "MOTA" + SIOTA = "SIOTA" + WCA = "WCA" + ZLOTA = "ZLOTA" + WOTA = "WOTA" + BOTA = "BOTA" + KRMNPA = "KRMNPA" + SANPCPA = "SANPCPA" + LLOTA = "LLOTA" + TOWERS = "Towers" + TILES = "Tiles" + RADAR_RALLY = "RaDAR Rally" + WAB = "WAB" + WAI = "WAI" + DMF = "DMF" + DME = "DME" + FEA = "FEA" + DMUE = "DMUE" + DMVE = "DMVE" + DCE = "DCE" + DEFE = "DEFE" + DTMBA = "DTMBA" + BIWOTA = "BIWOTA" + COTA = "COTA" + PGA = "PGA" + TOILETS = "Toilets" + + def __str__(self): + return str(self.value) + + class ActivityRefType(str, Enum): """Type of an activity reference.""" diff --git a/data/activities.py b/data/activities.py new file mode 100644 index 0000000..1fe4457 --- /dev/null +++ b/data/activities.py @@ -0,0 +1,444 @@ +from core.enums import ActivityName, ActivityType, ActivityRefType +from data.activity import Activity + + +ACTIVITIES: dict[ActivityName, Activity] = { + ActivityName.CONTEST: Activity( + name=ActivityName.CONTEST, + # No sensible way to determine *which* contest, but if we set comment_names=["CONTEST"] then at least + # any spots with "contest" in the comment will get allocated to this activity. + comment_names=["CONTEST"], + description="Contest", + sig_type=ActivityType.TRADITIONAL, + icon="fa-trophy", + refs_globally_unique=False, + ), + ActivityName.DXPEDITION: Activity( + name=ActivityName.DXPEDITION, + # DXpedition stations are never really spotted with "DXpedition" in the comments, but we can assign + # this activity to a spot other ways. + comment_names=[], + description="Radio expedition to a remote location", + sig_type=ActivityType.TRADITIONAL, + icon="fa-book-atlas", + refs_globally_unique=False, + ), + ActivityName.SATELLITE: Activity( + name=ActivityName.SATELLITE, + comment_names=[], + description="Amateur Radio Satellite", + sig_type=ActivityType.TRADITIONAL, + icon="fa-satellite", + refs_globally_unique=False, + ), + ActivityName.EME: Activity( + name=ActivityName.EME, + comment_names=[], + description="Earth-Moon-Earth (Moonbounce)", + sig_type=ActivityType.TRADITIONAL, + icon="fa-moon", + refs_globally_unique=False, + ), + ActivityName.AERONAUTICAL_MOBILE: Activity( + name=ActivityName.AERONAUTICAL_MOBILE, + # Don't pick /AM out of comments, spot.py will handle picking it out of the callsign + comment_names=[], + description="Aeronautical Mobile", + sig_type=ActivityType.TRADITIONAL, + icon="fa-plane", + refs_globally_unique=False, + ), + ActivityName.MARITIME_MOBILE: Activity( + name=ActivityName.MARITIME_MOBILE, + # Don't pick /MM out of comments, spot.py will handle picking it out of the callsign + comment_names=[], + description="Maritime Mobile", + sig_type=ActivityType.TRADITIONAL, + icon="fa-sailboat", + refs_globally_unique=False, + ), + ActivityName.QRP: Activity( + name=ActivityName.QRP, + comment_names=["QRP"], + description="Low power", + sig_type=ActivityType.TRADITIONAL, + icon="fa-volume-low", + refs_globally_unique=False, + ), + ActivityName.POTA: Activity( + name=ActivityName.POTA, + comment_names=["POTA"], + description="Parks on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.PARK, + ref_regex=r"[A-Z]{2}\-\d{4,5}|K\-TEST", + icon="fa-tree", + refs_globally_unique=False, + ), + ActivityName.SOTA: Activity( + name=ActivityName.SOTA, + comment_names=["SOTA"], + description="Summits on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.SUMMIT, + ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", + icon="fa-mountain-sun", + refs_globally_unique=False, + ), + ActivityName.WWFF: Activity( + name=ActivityName.WWFF, + comment_names=["WWFF"], + description="World Wide Flora & Fauna", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.PARK, + ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}", + icon="fa-seedling", + refs_globally_unique=True, + ), + ActivityName.GMA: Activity( + name=ActivityName.GMA, + comment_names=["GMA"], + description="Global Mountain Activity", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.SUMMIT, + ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", + icon="fa-person-hiking", + refs_globally_unique=False, + ), + ActivityName.WWBOTA: Activity( + name=ActivityName.WWBOTA, + comment_names=["WWBOTA", "BOTA"], + description="Worldwide Bunkers on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.BUNKER, + ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}", + icon="fa-radiation", + refs_globally_unique=True, + ), + ActivityName.HEMA: Activity( + name=ActivityName.HEMA, + comment_names=["HEMA"], + description="HuMPs Excluding Marilyns Award", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.SUMMIT, + ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}", + icon="fa-mound", + refs_globally_unique=False, + ), + ActivityName.IOTA: Activity( + name=ActivityName.IOTA, + comment_names=["IOTA"], + description="Islands on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.ISLAND, + ref_regex=r"[A-Z]{2}\-\d{3}", + icon="fa-book-atlas", + refs_globally_unique=False, + ), + ActivityName.GMA_ISLANDS: Activity( + name=ActivityName.GMA_ISLANDS, + comment_names=[], + description="Global Mountain Activity - Islands", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.ISLAND, + ref_regex=r"(([A-Z]{2}\-\d{3})|([A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}))", + icon="fa-person-hiking", + refs_globally_unique=False, + ), + ActivityName.ARLHS: Activity( + name=ActivityName.ARLHS, + comment_names=["ARLHS"], + description="Amateur Radio Lighthouse Society", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.LIGHTHOUSE, + ref_regex=r"[A-Z]{3}[\- ]\d{3,4}", + icon="fa-house-flood-water", + refs_globally_unique=False, + ), + ActivityName.ILLW: Activity( + name=ActivityName.ILLW, + comment_names=["ILLW"], + description="International Lighthouse & Lightship Weekend", + sig_type=ActivityType.EVENT, + ref_type=ActivityRefType.LIGHTHOUSE, + ref_regex=r"[A-Z]{2}\d{4}", + icon="fa-house-flood-water", + refs_globally_unique=False, + ), + ActivityName.MOTA: Activity( + name=ActivityName.MOTA, + comment_names=["MOTA"], + description="Mills on the Air", + sig_type=ActivityType.EVENT, + ref_type=ActivityRefType.MILL, + ref_regex=r"X\d{4,6}", + icon="fa-fan", + refs_globally_unique=True, + ), + ActivityName.SIOTA: Activity( + name=ActivityName.SIOTA, + comment_names=["SIOTA"], + description="Silos on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.SILO, + ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d", + icon="fa-wheat-awn", + refs_globally_unique=False, + ), + ActivityName.WCA: Activity( + name=ActivityName.WCA, + comment_names=["WCA"], + description="World Castles Award", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.CASTLE, + ref_regex=r"[A-Z0-9]{1,3}\-\d{5}", + icon="fa-chess-rook", + refs_globally_unique=False, + ), + ActivityName.ZLOTA: Activity( + name=ActivityName.ZLOTA, + comment_names=["ZLOTA"], + description="New Zealand on the Air", + sig_type=ActivityType.REGIONAL, + ref_type=None, + ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}", + icon="fa-kiwi-bird", + region_flag="🇳🇿", + refs_globally_unique=True, + ), + ActivityName.WOTA: Activity( + name=ActivityName.WOTA, + comment_names=["WOTA"], + description="Wainwrights on the Air", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.SUMMIT, + ref_regex=r"[A-Z]{3}-[0-9]{2}", + icon="fa-w", + region_flag="🇬🇧", + refs_globally_unique=False, + ), + ActivityName.BOTA: Activity( + name=ActivityName.BOTA, + comment_names=[], + description="Beaches on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.BEACH, + icon="fa-umbrella-beach", + refs_globally_unique=False, + ), + ActivityName.KRMNPA: Activity( + name=ActivityName.KRMNPA, + comment_names=["KRMNPA"], + description="Keith Roget Memorial National Parks Award", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.PARK, + ref_regex=r"VKFF\-\d{4}", + icon="fa-earth-oceania", + region_flag="🇦🇺", + refs_globally_unique=False, + ), + ActivityName.SANPCPA: Activity( + name=ActivityName.SANPCPA, + comment_names=["SANPCPA"], + description="South Australian National Parks and Conservation Parks Award", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.PARK, + ref_regex=r"VKFF\-\d{4}", + icon="fa-earth-oceania", + region_flag="🇦🇺", + refs_globally_unique=False, + ), + ActivityName.LLOTA: Activity( + name=ActivityName.LLOTA, + comment_names=["LLOTA"], + description="Lagos y Lagunas on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.LAKE, + ref_regex=r"LL[A-Z]{2}\-\d{4}", + icon="fa-water", + refs_globally_unique=True, + ), + ActivityName.TOWERS: Activity( + name=ActivityName.TOWERS, + comment_names=["TOTA"], + description="Towers on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.TOWER, + ref_regex=r"[A-Z]{2,3}R\-\d{4}", + icon="fa-tower-observation", + refs_globally_unique=False, + ), + ActivityName.TILES: Activity( + name=ActivityName.TILES, + comment_names=[], + description="Tiles on the Air", + sig_type=ActivityType.ADVENTURE, + ref_type=ActivityRefType.GRID, + ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}", + icon="fa-square", + refs_globally_unique=False, + ), + ActivityName.RADAR_RALLY: Activity( + name=ActivityName.RADAR_RALLY, + comment_names=["RaDAR"], + description="RaDAR Rally", + sig_type=ActivityType.EVENT, + icon="fa-headset", + refs_globally_unique=False, + ), + ActivityName.WAB: Activity( + name=ActivityName.WAB, + comment_names=["WAB"], + description="Worked All Britain", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.GRID, + ref_regex=r"[A-Z]{1,2}[0-9]{2}", + icon="fa-table-cells-large", + region_flag="🇬🇧", + refs_globally_unique=False, + ), + ActivityName.WAI: Activity( + name=ActivityName.WAI, + comment_names=["WAI"], + description="Worked All Ireland", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.GRID, + ref_regex=r"[A-Z][0-9]{2}", + icon="fa-table-cells-large", + region_flag="🇮🇪", + refs_globally_unique=False, + ), + ActivityName.DMF: Activity( + name=ActivityName.DMF, + comment_names=["DMF"], + description="Diplôme des Moulins de France", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.MILL, + icon="fa-fan", + region_flag="🇫🇷", + refs_globally_unique=False, + ), + ActivityName.DME: Activity( + name=ActivityName.DME, + comment_names=["DME"], + description="Diploma Municipios de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.TOWN, + ref_regex=r"DME[\- ]\d{3,5}", + icon="fa-building", + region_flag="🇪🇸", + refs_globally_unique=True, + ), + ActivityName.FEA: Activity( + name=ActivityName.FEA, + comment_names=["FEA"], + description="Diploma Faros de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.LIGHTHOUSE, + # FEA references are technically [DE]\-\d{4}(\.\d)? but spotters always seem to miss out the D- or E- + # prefix and just use FEA-1234 or FEA 1234, so allow for that. The FEA activity ref data provider adds both + # forms to the database. + ref_regex=r"([DE]|FEA)[\- ]\d{4}(\.\d)?", + icon="fa-house-flood-water", + region_flag="🇪🇸", + refs_globally_unique=True, + ), + ActivityName.DMUE: Activity( + name=ActivityName.DMUE, + comment_names=["DMUE"], + description="Diploma Museos de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.BUILDING, + ref_regex=r"MUE[A-Z]{2}-\d{3}", + icon="fa-landmark", + region_flag="🇪🇸", + refs_globally_unique=True, + ), + ActivityName.DMVE: Activity( + name=ActivityName.DMVE, + comment_names=["DMVE"], + description="Diploma Monumentos y Vestigios de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.BUILDING, + ref_regex=r"MV[A-Z]{1,2}-\d{4}", + icon="fa-monument", + region_flag="🇪🇸", + refs_globally_unique=True, + ), + ActivityName.DCE: Activity( + name=ActivityName.DCE, + comment_names=["DCE"], + description="Diploma Castillos de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.CASTLE, + ref_regex=r"C[A-Z]{1,2}-\d{3}", + icon="fa-chess-rook", + region_flag="🇪🇸", + refs_globally_unique=False, + ), + ActivityName.DEFE: Activity( + name=ActivityName.DEFE, + comment_names=["DEFE"], + description="Diploma Estaciones de Ferrocarril de España", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.BUILDING, + ref_regex=r"EF[A-Z]{1,2}-\d{3}", + icon="fa-train", + region_flag="🇪🇸", + refs_globally_unique=True, + ), + ActivityName.DTMBA: Activity( + name=ActivityName.DTMBA, + comment_names=["DTMBA"], + description="Diploma Teatri Musei e Belle Arti", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.BUILDING, + ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}", + icon="fa-landmark", + region_flag="🇮🇹", + refs_globally_unique=True, + ), + ActivityName.BIWOTA: Activity( + name=ActivityName.BIWOTA, + comment_names=["BIWOTA"], + description="British Inland Waterways on the Air", + sig_type=ActivityType.EVENT, + ref_type=ActivityRefType.WATERWAY, + icon="fa-ship", + region_flag="🇬🇧", + refs_globally_unique=False, + ), + ActivityName.COTA: Activity( + name=ActivityName.COTA, + comment_names=["COTA"], + description="Castles on the Air", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.CASTLE, + ref_regex=r"[A-Z]{3}\-[0-9]{3,5}", + icon="fa-chess-rook", + region_flag="🇩🇪", + refs_globally_unique=False, + ), + ActivityName.PGA: Activity( + name=ActivityName.PGA, + comment_names=["PGA"], + description="Polish Gmina Award", + sig_type=ActivityType.REGIONAL, + ref_type=ActivityRefType.REGION, + ref_regex=r"[A-Z]{2}[0-9]{2}", + icon="fa-g", + region_flag="🇵🇱", + refs_globally_unique=False, + ), + ActivityName.TOILETS: Activity( + name=ActivityName.TOILETS, + comment_names=[], + description="Toilets on the Air", + sig_type=ActivityType.EVENT, + ref_type=ActivityRefType.TOILET, + ref_regex=r"T\-[0-9]{2}", + icon="fa-toilet", + region_flag="🏴‍☠️", + refs_globally_unique=True, + ), +} diff --git a/data/activity.py b/data/activity.py index dd5d6f1..4c769cb 100644 --- a/data/activity.py +++ b/data/activity.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from core.enums import ActivityRefType, ActivityType +from core.enums import ActivityName, ActivityRefType, ActivityType @dataclass @@ -15,7 +15,7 @@ class Activity: 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: str + name: ActivityName # Description, e.g. "Towers on the Air" description: str # Type, either Worldwide, Regional or Event. Used for sorting in the web UI. diff --git a/data/spot.py b/data/spot.py index f482afb..3c2cebd 100644 --- a/data/spot.py +++ b/data/spot.py @@ -18,9 +18,9 @@ from core.activity_utils import ( ) from core.call_lookup_helper import get_call_info from core.config import MAX_SPOT_AGE -from core.constants import ACTIVITIES, PROPAGATION_MODES +from core.constants import PROPAGATION_MODES from core.data_store import DATA_STORE -from core.enums import Continent, LocationSourceForSpot, Mode, ModeSource, ModeType +from core.enums import ActivityName, Continent, LocationSourceForSpot, Mode, ModeSource, ModeType from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone from core.utils import ( get_flag_for_dxcc, @@ -29,6 +29,7 @@ from core.utils import ( infer_mode_from_frequency, infer_mode_type_from_mode, ) +from data.activities import ACTIVITIES from data.activity_ref import ActivityRef logger = logging.getLogger(__name__) @@ -311,7 +312,7 @@ class Spot: # name, but where the activity reference is unique-looking enough that we can't confuse it with any other # activity. if self.comment: - for activity in ACTIVITIES: + for activity in ACTIVITIES.values(): if activity.refs_globally_unique and activity.ref_regex: ref_matches = re.finditer( r"(^|\W)(" + activity.ref_regex + r")($|\W)", self.comment, re.IGNORECASE @@ -343,7 +344,7 @@ class Spot: ): self.dx_latitude = activity_ref.latitude self.dx_longitude = activity_ref.longitude - if self.sig == "WAB" or self.sig == "WAI" or self.sig == "Tiles": + if self.sig in (ActivityName.WAB, ActivityName.WAI, ActivityName.TILES): self.dx_location_source = LocationSourceForSpot.GRID else: self.dx_location_source = LocationSourceForSpot.SIG_REF_LOOKUP @@ -380,16 +381,16 @@ class Spot: # Set activities based on propagation mode if self.propagation_mode == "Satellite" and not self.sig: - self.sig = "Satellite" + self.sig = ActivityName.SATELLITE if self.propagation_mode == "Earth-Moon-Earth" and not self.sig: - self.sig = "EME" + self.sig = ActivityName.EME # Set activities based on the DX callsign suffix if self.dx_call and not self.sig: - if self.dx_call.upper().endswith("/AM"): - self.sig = "/AM" - elif self.dx_call.upper().endswith("/MM"): - self.sig = "/MM" + if self.dx_call.upper().endswith(ActivityName.AERONAUTICAL_MOBILE): + self.sig = ActivityName.AERONAUTICAL_MOBILE + elif self.dx_call.upper().endswith(ActivityName.MARITIME_MOBILE): + self.sig = ActivityName.MARITIME_MOBILE # Parse "de_grid -> dx_grid" structures from the comment if self.comment: diff --git a/providers/activityrefdata/arlhs.py b/providers/activityrefdata/arlhs.py index c7e52c0..0f2079f 100644 --- a/providers/activityrefdata/arlhs.py +++ b/providers/activityrefdata/arlhs.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class ARLHS(FileDownloadActivityRefDataProvider): """Activity ref data provider for Amateur Radio Light House Society""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "ARLHS" + ACTIVITY = ActivityName.ARLHS DATA_URL = "https://www.gma.rocks/download/lighthouse.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/cota.py b/providers/activityrefdata/cota.py index b41cb92..d51403e 100644 --- a/providers/activityrefdata/cota.py +++ b/providers/activityrefdata/cota.py @@ -2,7 +2,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -11,7 +11,7 @@ class COTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Castles on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "COTA" + ACTIVITY = ActivityName.COTA DATA_URL = "https://www.cotagroup.org/cotagroup/map/data/castles-all-7d90ee2a5e1175e5dece1bbf9dc87504.json" def __init__(self, provider_config): diff --git a/providers/activityrefdata/dce.py b/providers/activityrefdata/dce.py index 53c817b..5975b70 100644 --- a/providers/activityrefdata/dce.py +++ b/providers/activityrefdata/dce.py @@ -3,7 +3,7 @@ from time import sleep import pandas as pd -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -12,7 +12,7 @@ class DCE(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Castillos de España""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "DCE" + ACTIVITY = ActivityName.DCE DATA_URL = "https://www.acracb.org/dce/descargas/General/directorio_referencias_dce.xls" def __init__(self, provider_config): diff --git a/providers/activityrefdata/defe.py b/providers/activityrefdata/defe.py index 1ab9ad7..c1c5569 100644 --- a/providers/activityrefdata/defe.py +++ b/providers/activityrefdata/defe.py @@ -3,7 +3,7 @@ from time import sleep import pandas as pd -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -12,7 +12,7 @@ class DEFE(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Estationes de Ferrocarril de España""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "DEFE" + ACTIVITY = ActivityName.DEFE DATA_URL = "https://www.acracb.org/defe/descargas/General/directorio_referencias_defe.xls" def __init__(self, provider_config): diff --git a/providers/activityrefdata/dme.py b/providers/activityrefdata/dme.py index d551eca..0955b80 100644 --- a/providers/activityrefdata/dme.py +++ b/providers/activityrefdata/dme.py @@ -3,7 +3,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.local_file_activity_ref_data_provider import ( LocalFileActivityRefDataProvider, @@ -13,7 +13,7 @@ from providers.activityrefdata.local_file_activity_ref_data_provider import ( class DME(LocalFileActivityRefDataProvider): """Activity ref data provider for Diploma Municipios de Espana""" - ACTIVITY = "DME" + ACTIVITY = ActivityName.DME PATH = "datafiles/MUNICIPIOS.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/dmue.py b/providers/activityrefdata/dmue.py index 65e09c8..6ff3403 100644 --- a/providers/activityrefdata/dmue.py +++ b/providers/activityrefdata/dmue.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -10,7 +10,7 @@ class DMUE(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Museos de España""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "DMUE" + ACTIVITY = ActivityName.DMUE DATA_URL = "https://dmue.radiogalena.es/nom_dmue.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/dmve.py b/providers/activityrefdata/dmve.py index e4e85a7..841a7af 100644 --- a/providers/activityrefdata/dmve.py +++ b/providers/activityrefdata/dmve.py @@ -3,7 +3,7 @@ from time import sleep import pandas as pd -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -12,7 +12,7 @@ class DMVE(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Monumentos y Vestigios de España""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "DMVE" + ACTIVITY = ActivityName.DMVE DATA_URL = "https://www.acracb.org/dmve/descargas/General/directorio_referencias_dmve.xls" def __init__(self, provider_config): diff --git a/providers/activityrefdata/dtmba.py b/providers/activityrefdata/dtmba.py index 5ea2d1d..6791435 100644 --- a/providers/activityrefdata/dtmba.py +++ b/providers/activityrefdata/dtmba.py @@ -1,6 +1,6 @@ from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -9,7 +9,7 @@ class DTMBA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Teatri Musei Belle Arti""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "DTMBA" + ACTIVITY = ActivityName.DTMBA DATA_URL = "https://www.iu1fig.com/share/iz0eik/dtmba/export.php" def __init__(self, provider_config): diff --git a/providers/activityrefdata/fea.py b/providers/activityrefdata/fea.py index 5bbd407..cfc12ff 100644 --- a/providers/activityrefdata/fea.py +++ b/providers/activityrefdata/fea.py @@ -3,7 +3,7 @@ from time import sleep import pdfplumber -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -12,7 +12,7 @@ class FEA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Diploma Faros de España""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "FEA" + ACTIVITY = ActivityName.FEA DATA_URL = "http://ea5ol.net/Lista%20Faros.pdf" def __init__(self, provider_config): diff --git a/providers/activityrefdata/file_download_activity_ref_data_provider.py b/providers/activityrefdata/file_download_activity_ref_data_provider.py index 9e21027..cfbc3de 100644 --- a/providers/activityrefdata/file_download_activity_ref_data_provider.py +++ b/providers/activityrefdata/file_download_activity_ref_data_provider.py @@ -22,7 +22,7 @@ class FileDownloadActivityRefDataProvider(ActivityRefDataProvider): self._url = url self._poll_interval = poll_interval self._thread = None - self._url_data_cache = URLDataCache(f"sigrefdata_{sig_name}") # cache dir name kept for continuity + self._url_data_cache = URLDataCache(f"activity_ref_data_{sig_name}") def start(self): # Fire off the polling thread. It will poll immediately on startup, then sleep for poll_interval between diff --git a/providers/activityrefdata/gma.py b/providers/activityrefdata/gma.py index 23a350b..da1e282 100644 --- a/providers/activityrefdata/gma.py +++ b/providers/activityrefdata/gma.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class GMA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Global Mountain Activity""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "GMA" + ACTIVITY = ActivityName.GMA DATA_URL = "https://www.gma.rocks/download/summits.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/illw.py b/providers/activityrefdata/illw.py index 1a87167..2575437 100644 --- a/providers/activityrefdata/illw.py +++ b/providers/activityrefdata/illw.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class ILLW(FileDownloadActivityRefDataProvider): """Activity ref data provider for International Lighthouse & Lightship Weekend""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "ILLW" + ACTIVITY = ActivityName.ILLW DATA_URL = "https://www.gma.rocks/download/lighthouse.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/iota.py b/providers/activityrefdata/iota.py index 47d9f50..d6ddcf5 100644 --- a/providers/activityrefdata/iota.py +++ b/providers/activityrefdata/iota.py @@ -3,7 +3,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -16,7 +16,7 @@ class IOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Islands on the Air""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "IOTA" + ACTIVITY = ActivityName.IOTA DATA_URL = "https://www.iota-world.org/islands-on-the-air/downloads/download-file.html?path=groups.json" def __init__(self, provider_config): diff --git a/providers/activityrefdata/krmnpa.py b/providers/activityrefdata/krmnpa.py index 5ff6f6b..340939b 100644 --- a/providers/activityrefdata/krmnpa.py +++ b/providers/activityrefdata/krmnpa.py @@ -1,3 +1,4 @@ +from core.enums import ActivityName from providers.activityrefdata.pnp_kml_activity_ref_data_provider import ( ParksNPeaksKMLActivityRefDataProvider, ) @@ -7,7 +8,7 @@ class KRMNPA(ParksNPeaksKMLActivityRefDataProvider): """Activity ref data provider for the Keith Roget Memorrial National Parks Award (KRMNPA).""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "KRMNPA" + ACTIVITY = ActivityName.KRMNPA DATA_URL = "https://parksnpeaks.org/getPOI.php?poiClass=KRMNPA&poiFormat=4" def __init__(self, provider_config): diff --git a/providers/activityrefdata/llota.py b/providers/activityrefdata/llota.py index 556b74e..bdcf9c2 100644 --- a/providers/activityrefdata/llota.py +++ b/providers/activityrefdata/llota.py @@ -2,7 +2,7 @@ from time import sleep from pyhamtools.locator import locator_to_latlong -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -13,7 +13,7 @@ class LLOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Lagos y Lagunas on the Air""" POLL_INTERVAL_DAYS = 7 - ACTIVITY = "LLOTA" + ACTIVITY = ActivityName.LLOTA DATA_URL = "https://llota.app/api/public/references" def __init__(self, provider_config): diff --git a/providers/activityrefdata/mota.py b/providers/activityrefdata/mota.py index 30994ce..70ddae2 100644 --- a/providers/activityrefdata/mota.py +++ b/providers/activityrefdata/mota.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class MOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Mills on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "MOTA" + ACTIVITY = ActivityName.MOTA DATA_URL = "https://www.gma.rocks/download/mills.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/pga.py b/providers/activityrefdata/pga.py index 8029b6e..476b348 100644 --- a/providers/activityrefdata/pga.py +++ b/providers/activityrefdata/pga.py @@ -2,7 +2,7 @@ from time import sleep from bs4 import BeautifulSoup -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import FileDownloadActivityRefDataProvider @@ -11,7 +11,7 @@ class PGA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Polish Gmina Award""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "PGA" + ACTIVITY = ActivityName.PGA DATA_URL = "http://www.spga.pl/lista_pga2.php" def __init__(self, provider_config): diff --git a/providers/activityrefdata/pota.py b/providers/activityrefdata/pota.py index f5b700a..991eeda 100644 --- a/providers/activityrefdata/pota.py +++ b/providers/activityrefdata/pota.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class POTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Parks on the Air""" POLL_INTERVAL_DAYS = 7 - ACTIVITY = "POTA" + ACTIVITY = ActivityName.POTA DATA_URL = "https://pota.app/all_parks_ext.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/sanpcpa.py b/providers/activityrefdata/sanpcpa.py index 2186777..aec8aba 100644 --- a/providers/activityrefdata/sanpcpa.py +++ b/providers/activityrefdata/sanpcpa.py @@ -1,3 +1,4 @@ +from core.enums import ActivityName from providers.activityrefdata.pnp_kml_activity_ref_data_provider import ( ParksNPeaksKMLActivityRefDataProvider, ) @@ -7,7 +8,7 @@ class SANPCPA(ParksNPeaksKMLActivityRefDataProvider): """Activity ref data provider for the South Australia National Parks and Conservation Parks Award (SANPCPA).""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "SANPCPA" + ACTIVITY = ActivityName.SANPCPA DATA_URL = "https://parksnpeaks.org/getPOI.php?poiClass=SANPCPA&poiFormat=4" def __init__(self, provider_config): diff --git a/providers/activityrefdata/siota.py b/providers/activityrefdata/siota.py index 5ccb0e9..3bce496 100644 --- a/providers/activityrefdata/siota.py +++ b/providers/activityrefdata/siota.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class SIOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Silos on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "SIOTA" + ACTIVITY = ActivityName.SIOTA DATA_URL = "https://www.silosontheair.com/data/silos.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/sota.py b/providers/activityrefdata/sota.py index 013350a..e34dcab 100644 --- a/providers/activityrefdata/sota.py +++ b/providers/activityrefdata/sota.py @@ -3,7 +3,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -14,7 +14,7 @@ class SOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Summits on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "SOTA" + ACTIVITY = ActivityName.SOTA DATA_URL = "https://storage.sota.org.uk/summitslist.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/toilets.py b/providers/activityrefdata/toilets.py index e9829df..efbe15d 100644 --- a/providers/activityrefdata/toilets.py +++ b/providers/activityrefdata/toilets.py @@ -1,6 +1,6 @@ import csv -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.local_file_activity_ref_data_provider import ( LocalFileActivityRefDataProvider, @@ -10,7 +10,7 @@ from providers.activityrefdata.local_file_activity_ref_data_provider import ( class Toilets(LocalFileActivityRefDataProvider): """Activity ref data provider for Toilets on the Air""" - ACTIVITY = "Toilets" + ACTIVITY = ActivityName.TOILETS PATH = "datafiles/toilets.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/towers.py b/providers/activityrefdata/towers.py index 56d5d13..4800764 100644 --- a/providers/activityrefdata/towers.py +++ b/providers/activityrefdata/towers.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class Towers(FileDownloadActivityRefDataProvider): """Activity ref data provider for Towers on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "Towers" + ACTIVITY = ActivityName.TOWERS DATA_URL = "https://wwtota.com/servis/generate_csv.php?ref=&filter=all" def __init__(self, provider_config): diff --git a/providers/activityrefdata/wca.py b/providers/activityrefdata/wca.py index 9145c25..b0de0f2 100644 --- a/providers/activityrefdata/wca.py +++ b/providers/activityrefdata/wca.py @@ -4,7 +4,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -17,7 +17,7 @@ class WCA(FileDownloadActivityRefDataProvider): """Activity ref data provider for World Castles Award""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "WCA" + ACTIVITY = ActivityName.WCA DATA_URL = "https://polo.ham2k.com/data/activities/wca/all-castles.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/wota.py b/providers/activityrefdata/wota.py index 544c8ad..5329348 100644 --- a/providers/activityrefdata/wota.py +++ b/providers/activityrefdata/wota.py @@ -1,6 +1,6 @@ from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -11,7 +11,7 @@ class WOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Wainwrights on the Air""" POLL_INTERVAL_DAYS = 365 - ACTIVITY = "WOTA" + ACTIVITY = ActivityName.WOTA DATA_URL = "https://www.wota.org.uk/mapping/data/summits.json" def __init__(self, provider_config): diff --git a/providers/activityrefdata/wwbota.py b/providers/activityrefdata/wwbota.py index e4edf1f..ca024a9 100644 --- a/providers/activityrefdata/wwbota.py +++ b/providers/activityrefdata/wwbota.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class WWBOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for Worldwide Bunkers on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "WWBOTA" + ACTIVITY = ActivityName.WWBOTA DATA_URL = "https://api.wwbota.org/bunkers/?format=CSV" def __init__(self, provider_config): diff --git a/providers/activityrefdata/wwff.py b/providers/activityrefdata/wwff.py index 36f24ec..4e15f01 100644 --- a/providers/activityrefdata/wwff.py +++ b/providers/activityrefdata/wwff.py @@ -1,7 +1,7 @@ import csv from time import sleep -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -12,7 +12,7 @@ class WWFF(FileDownloadActivityRefDataProvider): """Activity ref data provider for Worldwide Flora & Fauna""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "WWFF" + ACTIVITY = ActivityName.WWFF DATA_URL = "https://wwff.co/wwff-data/wwff_directory.csv" def __init__(self, provider_config): diff --git a/providers/activityrefdata/zlota.py b/providers/activityrefdata/zlota.py index 50c5f66..0ebf445 100644 --- a/providers/activityrefdata/zlota.py +++ b/providers/activityrefdata/zlota.py @@ -2,7 +2,7 @@ from time import sleep from pyhamtools.locator import latlong_to_locator -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from providers.activityrefdata.file_download_activity_ref_data_provider import ( FileDownloadActivityRefDataProvider, @@ -13,7 +13,7 @@ class ZLOTA(FileDownloadActivityRefDataProvider): """Activity ref data provider for New Zealand on the Air""" POLL_INTERVAL_DAYS = 30 - ACTIVITY = "ZLOTA" + ACTIVITY = ActivityName.ZLOTA DATA_URL = "https://ontheair.nz/assets/assets.json" def __init__(self, provider_config): diff --git a/providers/alert/bota.py b/providers/alert/bota.py index 3a4bcf7..be3f39c 100644 --- a/providers/alert/bota.py +++ b/providers/alert/bota.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta import pytz from bs4 import BeautifulSoup +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -55,8 +56,8 @@ class BOTA(HTTPAlertProvider): alert = Alert( source=self.name, dx_calls=[dx_call], - sig="BOTA", - sig_refs=[ActivityRef(id=ref_name, sig="BOTA")], + sig=ActivityName.BOTA, + sig_refs=[ActivityRef(id=ref_name, sig=ActivityName.BOTA)], start_time=date_time.timestamp(), ) diff --git a/providers/alert/hamsat.py b/providers/alert/hamsat.py index 3b3a688..b0eb507 100644 --- a/providers/alert/hamsat.py +++ b/providers/alert/hamsat.py @@ -2,6 +2,7 @@ from datetime import datetime import pytz +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -34,11 +35,11 @@ class Hamsat(HTTPAlertProvider): dx_calls=[source_alert["callsign"].upper()], freqs_modes=freqs_modes, comment=source_alert["comment"], - sig="Satellite", + sig=ActivityName.SATELLITE, # Fudge an activity ref to provide the remaining bits of data we need: the satellite and the operator's grid sig_refs=[ ActivityRef( - sig="Satellite", + sig=ActivityName.SATELLITE, id=f"{source_alert['satellite']['name']} from {source_alert['grids'][0]}", ) ], diff --git a/providers/alert/ng3k.py b/providers/alert/ng3k.py index a18ae36..3d7d928 100644 --- a/providers/alert/ng3k.py +++ b/providers/alert/ng3k.py @@ -6,6 +6,7 @@ import pytz from rss_parser import Parser from rss_parser.models.rss import RSS +from core.enums import ActivityName from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -88,7 +89,7 @@ class NG3K(HTTPAlertProvider): comment=f"{by}; {comment}; {qsl_info}", start_time=start_timestamp, end_time=end_timestamp, - sig="DXpedition", + sig=ActivityName.DXPEDITION, ) # Add to our list. diff --git a/providers/alert/parksnpeaks.py b/providers/alert/parksnpeaks.py index 4fad3a2..3908d80 100644 --- a/providers/alert/parksnpeaks.py +++ b/providers/alert/parksnpeaks.py @@ -3,6 +3,7 @@ from datetime import datetime import pytz +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -39,7 +40,7 @@ class ParksNPeaks(HTTPAlertProvider): activity_refs = [] # PnP can give us an alert of class "QRP" which is the only one that's not a real activity in Spothole's # list, so mask this out if we got it. - if activity != "QRP": + if activity != ActivityName.QRP: activity_refs = [ActivityRef(id=ref_id, sig=activity, name=ref_name)] # Convert to our alert format @@ -56,22 +57,22 @@ class ParksNPeaks(HTTPAlertProvider): # Log a warning for the developer if PnP gives us an unknown programme we've never seen before if activity and activity not in [ - "POTA", - "SOTA", - "WWFF", - "HEMA", - "SIOTA", - "ZLOTA", - "KRMNPA", - "SANPCPA", - "LLOTA", - "QRP", + ActivityName.POTA, + ActivityName.SOTA, + ActivityName.WWFF, + ActivityName.HEMA, + ActivityName.SIOTA, + ActivityName.ZLOTA, + ActivityName.KRMNPA, + ActivityName.SANPCPA, + ActivityName.LLOTA, + ActivityName.QRP, ]: logger.warning(f"PNP alert found with activity {activity}, developer needs to add support for this!") # If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. Otherwise, add to # the alert list. Note that while ZLOTA has its own spots API, it doesn't have its own alerts API. So that # means the PnP *spot* provider rejects ZLOTA spots here, but the PnP *alerts* provider here allows ZLOTA. - if activity not in ["POTA", "SOTA", "WWFF"]: + if activity not in [ActivityName.POTA, ActivityName.SOTA, ActivityName.WWFF]: new_alerts.append(alert) return new_alerts diff --git a/providers/alert/pota.py b/providers/alert/pota.py index 4e79984..fe656fe 100644 --- a/providers/alert/pota.py +++ b/providers/alert/pota.py @@ -2,6 +2,7 @@ from datetime import datetime import pytz +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -27,11 +28,11 @@ class POTA(HTTPAlertProvider): dx_calls=[source_alert["activator"].upper()], freqs_modes=source_alert["frequencies"], comment=source_alert["comments"], - sig="POTA", + sig=ActivityName.POTA, sig_refs=[ ActivityRef( id=source_alert["reference"], - sig="POTA", + sig=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 d4aa528..c2ec2a7 100644 --- a/providers/alert/rsgb_ical_alert_provider.py +++ b/providers/alert/rsgb_ical_alert_provider.py @@ -3,6 +3,7 @@ import re from icalendar import Event from core.enums import Continent +from core.enums import ActivityName from data.alert import Alert from providers.alert.ical_alert_provider import ICALAlertProvider @@ -69,7 +70,7 @@ class RSGBICALAlertProvider(ICALAlertProvider): comment=summary, start_time=start_timestamp, end_time=end_timestamp, - sig="Contest", + sig=ActivityName.CONTEST, ) return alert diff --git a/providers/alert/sota.py b/providers/alert/sota.py index a234261..9678917 100644 --- a/providers/alert/sota.py +++ b/providers/alert/sota.py @@ -2,6 +2,7 @@ from datetime import datetime import pytz +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -33,11 +34,11 @@ class SOTA(HTTPAlertProvider): dx_names=[source_alert["activatorName"].upper()], freqs_modes=source_alert["frequency"], comment=source_alert["comments"], - sig="SOTA", + sig=ActivityName.SOTA, sig_refs=[ ActivityRef( id=f"{source_alert['associationCode']}/{source_alert['summitCode']}", - sig="SOTA", + sig=ActivityName.SOTA, name=summit_name, activation_score=summit_points, ) diff --git a/providers/alert/wa7bnm.py b/providers/alert/wa7bnm.py index a2c3b75..d311826 100644 --- a/providers/alert/wa7bnm.py +++ b/providers/alert/wa7bnm.py @@ -1,5 +1,6 @@ from icalendar import Event +from core.enums import ActivityName from data.alert import Alert from providers.alert.ical_alert_provider import ICALAlertProvider @@ -34,7 +35,7 @@ class WA7BNM(ICALAlertProvider): url=url, start_time=start_timestamp, end_time=end_timestamp, - sig="Contest", + sig=ActivityName.CONTEST, ) return alert diff --git a/providers/alert/wota.py b/providers/alert/wota.py index 8ecd56c..698e429 100644 --- a/providers/alert/wota.py +++ b/providers/alert/wota.py @@ -7,6 +7,7 @@ import pytz from rss_parser import Parser as RSSParser from rss_parser.models.rss import RSS +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -74,7 +75,7 @@ class WOTA(HTTPAlertProvider): dx_calls=[dx_call], freqs_modes=freqs_modes, comment=comment, - sig_refs=[ActivityRef(id=ref, sig="WOTA", name=ref_name)] if ref else [], + sig_refs=[ActivityRef(id=ref, sig=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 1bea3a4..733d327 100644 --- a/providers/alert/wwff.py +++ b/providers/alert/wwff.py @@ -2,6 +2,7 @@ from datetime import datetime import pytz +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.alert import Alert from providers.alert.http_alert_provider import HTTPAlertProvider @@ -27,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="WWFF", - sig_refs=[ActivityRef(id=source_alert["reference"], sig="WWFF")], + sig=ActivityName.WWFF, + sig_refs=[ActivityRef(id=source_alert["reference"], sig=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 b1b8356..0bf3f1e 100644 --- a/providers/spot/gma.py +++ b/providers/spot/gma.py @@ -4,7 +4,8 @@ from datetime import datetime import pytz from core.constants import HTTP_HEADERS -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from core.url_data_cache import URLDataCache from data.activity_ref import ActivityRef from data.spot import Spot @@ -106,40 +107,40 @@ class GMA(HTTPSpotProvider): spot.sig_refs and ref_info and "reftype" in ref_info - and ref_info["reftype"] not in ["POTA", "WWFF"] + and ref_info["reftype"] not in [ActivityName.POTA, ActivityName.WWFF] and ( ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info["sota"] == "" ) ): match ref_info["reftype"]: case "Summit": - spot.sig_refs[0].sig = "GMA" + spot.sig_refs[0].sig = ActivityName.GMA spot.sig_refs[0].ref_type = ActivityRefType.SUMMIT - spot.sig = "GMA" + spot.sig = ActivityName.GMA case "IOTA Island": - spot.sig_refs[0].sig = "IOTA" + spot.sig_refs[0].sig = ActivityName.IOTA spot.sig_refs[0].ref_type = ActivityRefType.ISLAND - spot.sig = "IOTA" + spot.sig = ActivityName.IOTA case "GMA Island": - spot.sig_refs[0].sig = "GMA Islands" + spot.sig_refs[0].sig = ActivityName.GMA_ISLANDS spot.sig_refs[0].ref_type = ActivityRefType.ISLAND - spot.sig = "GMA Islands" + spot.sig = ActivityName.GMA_ISLANDS case "Lighthouse (ILLW)": - spot.sig_refs[0].sig = "ILLW" + spot.sig_refs[0].sig = ActivityName.ILLW spot.sig_refs[0].ref_type = ActivityRefType.LIGHTHOUSE - spot.sig = "ILLW" + spot.sig = ActivityName.ILLW case "Lighthouse (ARLHS)": - spot.sig_refs[0].sig = "ARLHS" + spot.sig_refs[0].sig = ActivityName.ARLHS spot.sig_refs[0].ref_type = ActivityRefType.LIGHTHOUSE - spot.sig = "ARLHS" + spot.sig = ActivityName.ARLHS case "Castle": - spot.sig_refs[0].sig = "WCA" + spot.sig_refs[0].sig = ActivityName.WCA spot.sig_refs[0].ref_type = ActivityRefType.CASTLE - spot.sig = "WCA" + spot.sig = ActivityName.WCA case "Mill": - spot.sig_refs[0].sig = "MOTA" + spot.sig_refs[0].sig = ActivityName.MOTA spot.sig_refs[0].ref_type = ActivityRefType.MILL - spot.sig = "MOTA" + spot.sig = ActivityName.MOTA case _: logger.warning( f"GMA spot found with ref type {ref_info['reftype']}, developer needs to add support for this!" @@ -170,7 +171,7 @@ class GMA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "GMA" + return activity == ActivityName.GMA def submit_spot(self, spot, credentials): # TODO: Implement. diff --git a/providers/spot/hema.py b/providers/spot/hema.py index d9e2d62..5ef9042 100644 --- a/providers/spot/hema.py +++ b/providers/spot/hema.py @@ -7,7 +7,8 @@ import requests from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout from core.constants import HTTP_HEADERS -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -62,11 +63,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="HEMA", + sig=ActivityName.HEMA, sig_refs=[ ActivityRef( id=spot_items[3].upper(), - sig="HEMA", + sig=ActivityName.HEMA, name=spot_items[4], latitude=float(spot_items[7]), longitude=float(spot_items[8]), @@ -90,7 +91,7 @@ class HEMA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "HEMA" + return activity == ActivityName.HEMA def submit_spot(self, spot, credentials): # TODO: Implement. Currently blocked awaiting their API team to make a change to allow us to spot with a diff --git a/providers/spot/llota.py b/providers/spot/llota.py index bfbc7a2..671f514 100644 --- a/providers/spot/llota.py +++ b/providers/spot/llota.py @@ -1,6 +1,7 @@ from datetime import datetime -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -34,11 +35,11 @@ class LLOTA(HTTPSpotProvider): freq=float(source_spot["frequency"]) * 1000000, mode=Mode.from_name(source_spot["mode"].upper()), comment=comment, - sig="LLOTA", + sig=ActivityName.LLOTA, sig_refs=[ ActivityRef( id=source_spot["reference"], - sig="LLOTA", + sig=ActivityName.LLOTA, name=source_spot["reference_name"], ref_type=ActivityRefType.LAKE, ) diff --git a/providers/spot/parksnpeaks.py b/providers/spot/parksnpeaks.py index dcf75bb..aacf7fd 100644 --- a/providers/spot/parksnpeaks.py +++ b/providers/spot/parksnpeaks.py @@ -7,6 +7,7 @@ import requests from core.constants import HTTP_HEADERS from core.enums import Mode +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -21,15 +22,15 @@ class ParksNPeaks(HTTPSpotProvider): SPOTS_URL = "https://www.parksnpeaks.org/api/ALL" SUBMIT_URL = "https://www.parksnpeaks.org/api/SPOT/" SUBMITTABLE_ACTIVITIES = [ - "POTA", - "SOTA", - "WWFF", - "HEMA", - "WOTA", - "ZLOTA", - "SIOTA", - "KRMNPA", - "SANPCPA", + ActivityName.POTA, + ActivityName.SOTA, + ActivityName.WWFF, + ActivityName.HEMA, + ActivityName.WOTA, + ActivityName.ZLOTA, + ActivityName.SIOTA, + ActivityName.KRMNPA, + ActivityName.SANPCPA, ] def __init__(self, provider_config): @@ -68,7 +69,7 @@ class ParksNPeaks(HTTPSpotProvider): # programme with a defined set of references activity = source_spot["actClass"].upper() ref_id = source_spot["actSiteID"] - if activity and activity != "" and activity != "QRP" and ref_id and ref_id != "": + if activity and activity != "" and activity != ActivityName.QRP and ref_id and ref_id != "": spot.sig = activity activity_refs = [ ActivityRef( @@ -84,15 +85,15 @@ class ParksNPeaks(HTTPSpotProvider): # Log a warning for the developer if PnP gives us an unknown programme we've never seen before if activity not in [ - "POTA", - "SOTA", - "WWFF", - "HEMA", - "SIOTA", - "ZLOTA", - "KRMNPA", - "SANPCPA", - "LLOTA", + ActivityName.POTA, + ActivityName.SOTA, + ActivityName.WWFF, + ActivityName.HEMA, + ActivityName.SIOTA, + ActivityName.ZLOTA, + ActivityName.KRMNPA, + ActivityName.SANPCPA, + ActivityName.LLOTA, ]: logger.warning(f"PNP spot found with activity {activity}, developer needs to add support for this!") diff --git a/providers/spot/pota.py b/providers/spot/pota.py index 662b182..a5e2981 100644 --- a/providers/spot/pota.py +++ b/providers/spot/pota.py @@ -4,7 +4,8 @@ import pytz import requests from core.constants import HTTP_HEADERS -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -33,11 +34,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="POTA", + sig=ActivityName.POTA, sig_refs=[ ActivityRef( id=source_spot["reference"], - sig="POTA", + sig=ActivityName.POTA, name=source_spot["name"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -58,7 +59,7 @@ class POTA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "POTA" + return activity == ActivityName.POTA def submit_spot(self, spot, credentials): sig_ref = spot.sig_refs[0].id if spot.sig_refs else None diff --git a/providers/spot/sota.py b/providers/spot/sota.py index 897184f..f96cf6f 100644 --- a/providers/spot/sota.py +++ b/providers/spot/sota.py @@ -5,7 +5,8 @@ import requests from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout from core.constants import HTTP_HEADERS -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -56,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="SOTA", + sig=ActivityName.SOTA, sig_refs=[ ActivityRef( id=source_spot["summitCode"], - sig="SOTA", + sig=ActivityName.SOTA, name=source_spot["summitName"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -83,7 +84,7 @@ class SOTA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "SOTA" + return activity == ActivityName.SOTA def submit_spot(self, spot, credentials): # TODO test this method works diff --git a/providers/spot/tiles.py b/providers/spot/tiles.py index 0dfd7d4..02887b0 100644 --- a/providers/spot/tiles.py +++ b/providers/spot/tiles.py @@ -4,7 +4,8 @@ from datetime import datetime import requests from core.constants import HTTP_HEADERS -from core.enums import ActivityRefType, LocationSourceForSpot, Mode +from core.enums import LocationSourceForSpot, Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -58,13 +59,13 @@ class Tiles(HTTPSpotProvider): freq=freq, mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["notes"], - sig="Tiles", + sig=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=[ ActivityRef( id=source_spot["maidenhead_grid"], - sig="Tiles", + sig=ActivityName.TILES, name=source_spot["maidenhead_grid"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -84,7 +85,7 @@ class Tiles(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "Tiles" + return activity == ActivityName.TILES def submit_spot(self, spot, credentials): # Tiles on the air currently only supports *self* spots diff --git a/providers/spot/towers.py b/providers/spot/towers.py index 1b6128c..945e366 100644 --- a/providers/spot/towers.py +++ b/providers/spot/towers.py @@ -3,7 +3,7 @@ from datetime import datetime import pytz -from core.enums import ActivityRefType +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -34,8 +34,8 @@ class Towers(HTTPSpotProvider): dx_call=source_spot["call"].upper(), freq=likely_freq, comment=source_spot["comment"], - sig="Towers", - sig_refs=[ActivityRef(id=source_spot["ref"], sig="Towers", ref_type=ActivityRefType.TOWER)], + sig=ActivityName.TOWERS, + sig_refs=[ActivityRef(id=source_spot["ref"], sig=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 138f94e..11b851a 100644 --- a/providers/spot/wota.py +++ b/providers/spot/wota.py @@ -8,7 +8,8 @@ import pytz from rss_parser import Parser from rss_parser.models.rss import RSS -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -90,8 +91,12 @@ class WOTA(HTTPSpotProvider): freq=freq_hz, mode=Mode.from_name(mode), comment=comment, - sig="WOTA", - sig_refs=[ActivityRef(id=ref, sig="WOTA", name=ref_name, ref_type=ActivityRefType.SUMMIT)] if ref else [], + sig=ActivityName.WOTA, + sig_refs=( + [ActivityRef(id=ref, sig=ActivityName.WOTA, name=ref_name, ref_type=ActivityRefType.SUMMIT)] + if ref + else [] + ), time=time.timestamp(), ) @@ -105,7 +110,7 @@ class WOTA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "WOTA" + return activity == ActivityName.WOTA def submit_spot(self, spot, credentials): # TODO Ask M5TEA if he's happy to share how this is done from his app diff --git a/providers/spot/wwbota.py b/providers/spot/wwbota.py index f168ff5..2964a3a 100644 --- a/providers/spot/wwbota.py +++ b/providers/spot/wwbota.py @@ -1,7 +1,8 @@ import json from datetime import datetime -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.sse_spot_provider import SSESpotProvider @@ -23,7 +24,7 @@ class WWBOTA(SSESpotProvider): for ref in source_spot["references"]: activity_ref = ActivityRef( id=ref["reference"], - sig="WWBOTA", + sig=ActivityName.WWBOTA, name=ref["name"], latitude=ref["lat"], longitude=ref["long"], @@ -38,7 +39,7 @@ class WWBOTA(SSESpotProvider): freq=float(source_spot["freq"]) * 1000000, mode=Mode.from_name(source_spot["mode"].upper()) if "mode" in source_spot else None, comment=source_spot["comment"], - sig="WWBOTA", + sig=ActivityName.WWBOTA, sig_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 @@ -53,7 +54,7 @@ class WWBOTA(SSESpotProvider): return spot if source_spot["type"] != "Test" else None def can_submit_spot(self, activity): - return activity == "WWBOTA" + return activity == ActivityName.WWBOTA def submit_spot(self, spot, credentials): # TODO: Implement. WWBOTA API docs cover this: https://api.wwbota.org/#tag/Spots/operation/create_spot_spots__post diff --git a/providers/spot/wwff.py b/providers/spot/wwff.py index ab6667e..d6bad7c 100644 --- a/providers/spot/wwff.py +++ b/providers/spot/wwff.py @@ -2,7 +2,8 @@ from datetime import datetime import pytz -from core.enums import ActivityRefType, Mode +from core.enums import Mode +from core.enums import ActivityName, ActivityRefType from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -30,11 +31,11 @@ class WWFF(HTTPSpotProvider): freq=float(source_spot["frequency_khz"]) * 1000, mode=Mode.from_name(source_spot["mode"].upper()), comment=source_spot["remarks"], - sig="WWFF", + sig=ActivityName.WWFF, sig_refs=[ ActivityRef( id=source_spot["reference"], - sig="WWFF", + sig=ActivityName.WWFF, name=source_spot["reference_name"], latitude=source_spot["latitude"], longitude=source_spot["longitude"], @@ -52,7 +53,7 @@ class WWFF(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "WWFF" + return activity == ActivityName.WWFF def submit_spot(self, spot, credentials): # TODO: Implement. Spotting to WWFF should be possible, need to look up the Spotline docs or copy approach from diff --git a/providers/spot/zlota.py b/providers/spot/zlota.py index 0ca93c0..8fac485 100644 --- a/providers/spot/zlota.py +++ b/providers/spot/zlota.py @@ -3,6 +3,7 @@ from datetime import datetime import pytz from core.enums import Mode +from core.enums import ActivityName from data.activity_ref import ActivityRef from data.spot import Spot from providers.spot.http_spot_provider import HTTPSpotProvider @@ -35,11 +36,11 @@ class ZLOTA(HTTPSpotProvider): freq=freq_hz, mode=Mode.from_name(source_spot["mode"].upper().strip()), comment=source_spot["comments"], - sig="ZLOTA", + sig=ActivityName.ZLOTA, sig_refs=[ ActivityRef( id=source_spot["reference"], - sig="ZLOTA", + sig=ActivityName.ZLOTA, name=source_spot["name"], ) ], @@ -52,7 +53,7 @@ class ZLOTA(HTTPSpotProvider): return new_spots def can_submit_spot(self, activity): - return activity == "ZLOTA" + return activity == ActivityName.ZLOTA def submit_spot(self, spot, credentials): # TODO: Implement. Spotting to ZLOTA is supported via POST, see https://ontheair.nz/api diff --git a/templates/about.html b/templates/about.html index 8deb275..66042dc 100644 --- a/templates/about.html +++ b/templates/about.html @@ -115,7 +115,7 @@ Vestigios de España (DMVE), Diploma Estaciones de Ferrocarril de España (DEFE), Diploma Teatri Musei e Belle Arti (DTMBA), British Inland Waterways on the Air (BIWOTA), Castles on the Air (COTA), Polish Gmina Award (PGA), Diplôme des Moulins de France (DMF), RaDAR Rally, and Toilets on the Air.

-

As of the time of writing in August 2026, I think Spothole captures most outdoor radio programmes that have a +

As of the time of writing in August 2026, I think Spothole captures most radio programmes that have a defined, downloadable reference list, and almost certainly those that have a spotting/alerting API. If you know of one I've missed, please let me know!

Why can I filter spots by both Activity and Source? Isn't that basically the same thing?

diff --git a/webserver/handlers/api/alerts.py b/webserver/handlers/api/alerts.py index 4861333..56bf0eb 100644 --- a/webserver/handlers/api/alerts.py +++ b/webserver/handlers/api/alerts.py @@ -9,6 +9,7 @@ import tornado_eventsource.handler from tornado import httputil from tornado.web import Application +from core.enums import ActivityName from core.utils import safe_json_dumps from data.lookup_credentials import extract_credentials @@ -168,13 +169,13 @@ def alert_allowed_by_query(alert, query): # the alert is a dxpedition, or contests_skip_max_duration_check and the alert is a contest, it also # always passes the check. if ( - alert.sig == "DXpedition" + alert.sig == ActivityName.DXPEDITION and "dxpeditions_skip_max_duration_check" in query and query.get("dxpeditions_skip_max_duration_check").upper() == "TRUE" ): continue if ( - alert.sig == "Contest" + alert.sig == ActivityName.CONTEST and "contests_skip_max_duration_check" in query and query.get("contests_skip_max_duration_check").upper() == "TRUE" ): diff --git a/webserver/handlers/api/lookups.py b/webserver/handlers/api/lookups.py index 2ee92a8..fceac5a 100644 --- a/webserver/handlers/api/lookups.py +++ b/webserver/handlers/api/lookups.py @@ -7,9 +7,8 @@ from tornado import httputil from tornado.web import Application from core.activity_lookup_helper import populate_missing_activity_ref_info -from core.activity_utils import get_ref_regex_for_activity +from core.activity_utils import get_activity_by_name, get_ref_regex_for_activity from core.call_lookup_helper import get_call_info -from core.constants import ACTIVITIES from core.geo_utils import ( lat_lon_for_grid_sw_corner_plus_size, lat_lon_to_cq_zone, @@ -85,7 +84,7 @@ class APILookupActivityRefHandler(tornado.web.RequestHandler): if "sig" in query_params and "id" in query_params: activity = str(query_params.get("sig")).upper() ref_id = str(query_params.get("id")).upper() - if activity in [a.name.upper() for a in ACTIVITIES]: + if get_activity_by_name(activity): if not get_ref_regex_for_activity(activity) or re.match( get_ref_regex_for_activity(activity), ref_id ): diff --git a/webserver/handlers/api/options.py b/webserver/handlers/api/options.py index feb5cb4..fd7357f 100644 --- a/webserver/handlers/api/options.py +++ b/webserver/handlers/api/options.py @@ -6,9 +6,10 @@ from tornado import httputil from tornado.web import Application from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE -from core.constants import ACTIVITIES, BANDS, PROPAGATION_MODES +from core.constants import BANDS, PROPAGATION_MODES from core.enums import Continent, Mode, ModeType from core.utils import safe_json_dumps +from data.activities import ACTIVITIES logger = logging.getLogger(__name__) @@ -39,7 +40,7 @@ class APIOptionsHandler(tornado.web.RequestHandler): # for provider in self._spot_providers: # if not provider.enabled: # continue - # for activity in ACTIVITIES: + # for activity in ACTIVITIES.values(): # if provider.can_submit_spot(activity.name): # spot_submit_providers.setdefault(activity.name, []).append(provider.name) @@ -74,7 +75,7 @@ class APIOptionsHandler(tornado.web.RequestHandler): "bands": BANDS, "modes": [m.value for m in Mode], "mode_types": [t.value for t in ModeType], - "sigs": ACTIVITIES, + "sigs": list(ACTIVITIES.values()), "spot_providers": spot_providers, "spot_providers_enabled_by_default": spot_providers_enabled_by_default, "alert_providers": alert_providers,