Start converting some enum-like strings to proper enums

This commit is contained in:
Ian Renton
2026-09-02 07:26:02 +01:00
parent 55eaa871e9
commit fa2e4b929a
21 changed files with 132 additions and 111 deletions
+31 -44
View File
@@ -17,7 +17,7 @@ SIGS = [
description="Parks on the Air", description="Parks on the Air",
ref_regex=r"[A-Z]{2}\-\d{4,5}|K\-TEST", ref_regex=r"[A-Z]{2}\-\d{4,5}|K\-TEST",
icon="fa-tree", icon="fa-tree",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="SOTA", name="SOTA",
@@ -25,7 +25,7 @@ SIGS = [
description="Summits on the Air", description="Summits on the Air",
ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}",
icon="fa-mountain-sun", icon="fa-mountain-sun",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="WWFF", name="WWFF",
@@ -33,7 +33,7 @@ SIGS = [
description="World Wide Flora & Fauna", description="World Wide Flora & Fauna",
ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}", ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}",
icon="fa-seedling", icon="fa-seedling",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="GMA", name="GMA",
@@ -41,7 +41,7 @@ SIGS = [
description="Global Mountain Activity", description="Global Mountain Activity",
ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}",
icon="fa-person-hiking", icon="fa-person-hiking",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="WWBOTA", name="WWBOTA",
@@ -49,7 +49,7 @@ SIGS = [
description="Worldwide Bunkers on the Air", description="Worldwide Bunkers on the Air",
ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}", ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}",
icon="fa-radiation", icon="fa-radiation",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="HEMA", name="HEMA",
@@ -57,7 +57,7 @@ SIGS = [
description="HuMPs Excluding Marilyns Award", description="HuMPs Excluding Marilyns Award",
ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}", ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}",
icon="fa-mound", icon="fa-mound",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="IOTA", name="IOTA",
@@ -65,7 +65,7 @@ SIGS = [
description="Islands on the Air", description="Islands on the Air",
ref_regex=r"[A-Z]{2}\-\d{3}", ref_regex=r"[A-Z]{2}\-\d{3}",
icon="fa-book-atlas", icon="fa-book-atlas",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="GMA Islands", name="GMA Islands",
@@ -73,7 +73,7 @@ SIGS = [
description="Global Mountain Activity - Islands", description="Global Mountain Activity - Islands",
ref_regex=r"(([A-Z]{2}\-\d{3})|([A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}))", ref_regex=r"(([A-Z]{2}\-\d{3})|([A-Z0-9]{1,3}\/[A-Z]{2}\-\d{3}))",
icon="fa-person-hiking", icon="fa-person-hiking",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="ARLHS", name="ARLHS",
@@ -81,7 +81,7 @@ SIGS = [
description="Amateur Radio Lighthouse Society", description="Amateur Radio Lighthouse Society",
ref_regex=r"[A-Z]{3}[\- ]\d{3,4}", ref_regex=r"[A-Z]{3}[\- ]\d{3,4}",
icon="fa-house-flood-water", icon="fa-house-flood-water",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="ILLW", name="ILLW",
@@ -89,7 +89,7 @@ SIGS = [
description="International Lighthouse & Lightship Weekend", description="International Lighthouse & Lightship Weekend",
ref_regex=r"[A-Z]{2}\d{4}", ref_regex=r"[A-Z]{2}\d{4}",
icon="fa-house-flood-water", icon="fa-house-flood-water",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="MOTA", name="MOTA",
@@ -97,7 +97,7 @@ SIGS = [
description="Mills on the Air", description="Mills on the Air",
ref_regex=r"X\d{4,6}", ref_regex=r"X\d{4,6}",
icon="fa-fan", icon="fa-fan",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="SIOTA", name="SIOTA",
@@ -105,7 +105,7 @@ SIGS = [
description="Silos on the Air", description="Silos on the Air",
ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d", ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d",
icon="fa-wheat-awn", icon="fa-wheat-awn",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="WCA", name="WCA",
@@ -113,7 +113,7 @@ SIGS = [
description="World Castles Award", description="World Castles Award",
ref_regex=r"[A-Z0-9]{1,3}\-\d{5}", ref_regex=r"[A-Z0-9]{1,3}\-\d{5}",
icon="fa-chess-rook", icon="fa-chess-rook",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="ZLOTA", name="ZLOTA",
@@ -122,7 +122,7 @@ SIGS = [
ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}", ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}",
icon="fa-kiwi-bird", icon="fa-kiwi-bird",
region_flag="🇳🇿", region_flag="🇳🇿",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="WOTA", name="WOTA",
@@ -131,20 +131,16 @@ SIGS = [
ref_regex=r"[A-Z]{3}-[0-9]{2}", ref_regex=r"[A-Z]{3}-[0-9]{2}",
icon="fa-w", icon="fa-w",
region_flag="🇬🇧", region_flag="🇬🇧",
refs_globally_unique=False refs_globally_unique=False,
),
SIG(name="BOTA",
comment_names=[],
description="Beaches on the Air",
icon="fa-umbrella-beach"
), ),
SIG(name="BOTA", comment_names=[], description="Beaches on the Air", icon="fa-umbrella-beach"),
SIG( SIG(
name="KRMNPA", name="KRMNPA",
comment_names=["KRMNPA"], comment_names=["KRMNPA"],
description="Keith Roget Memorial National Parks Award", description="Keith Roget Memorial National Parks Award",
ref_regex=r"VKFF\-\d{4}", ref_regex=r"VKFF\-\d{4}",
icon="fa-earth-oceania", icon="fa-earth-oceania",
region_flag="🇦🇺" region_flag="🇦🇺",
), ),
SIG( SIG(
name="SANPCPA", name="SANPCPA",
@@ -152,7 +148,7 @@ SIGS = [
description="South Australian National Parks and Conservation Parks Award", description="South Australian National Parks and Conservation Parks Award",
ref_regex=r"VKFF\-\d{4}", ref_regex=r"VKFF\-\d{4}",
icon="fa-earth-oceania", icon="fa-earth-oceania",
region_flag="🇦🇺" region_flag="🇦🇺",
), ),
SIG( SIG(
name="LLOTA", name="LLOTA",
@@ -160,7 +156,7 @@ SIGS = [
description="Lagos y Lagunas on the Air", description="Lagos y Lagunas on the Air",
ref_regex=r"LL[A-Z]{2}\-\d{4}", ref_regex=r"LL[A-Z]{2}\-\d{4}",
icon="fa-water", icon="fa-water",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="Towers", name="Towers",
@@ -168,7 +164,7 @@ SIGS = [
description="Towers on the Air", description="Towers on the Air",
ref_regex=r"[A-Z]{2,3}R\-\d{4}", ref_regex=r"[A-Z]{2,3}R\-\d{4}",
icon="fa-tower-observation", icon="fa-tower-observation",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="Tiles", name="Tiles",
@@ -176,7 +172,7 @@ SIGS = [
description="Tiles on the Air", description="Tiles on the Air",
ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}", ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}",
icon="fa-square", icon="fa-square",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="WAB", name="WAB",
@@ -185,7 +181,7 @@ SIGS = [
ref_regex=r"[A-Z]{1,2}[0-9]{2}", ref_regex=r"[A-Z]{1,2}[0-9]{2}",
icon="fa-table-cells-large", icon="fa-table-cells-large",
region_flag="🇬🇧", region_flag="🇬🇧",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="WAI", name="WAI",
@@ -194,7 +190,7 @@ SIGS = [
ref_regex=r"[A-Z][0-9]{2}", ref_regex=r"[A-Z][0-9]{2}",
icon="fa-table-cells-large", icon="fa-table-cells-large",
region_flag="🇮🇪", region_flag="🇮🇪",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="DME", name="DME",
@@ -203,7 +199,7 @@ SIGS = [
ref_regex=r"\d{4,5}", ref_regex=r"\d{4,5}",
icon="fa-building", icon="fa-building",
region_flag="🇪🇸", region_flag="🇪🇸",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="FEA", name="FEA",
@@ -215,7 +211,7 @@ SIGS = [
ref_regex=r"([DE]|FEA)[\- ]\d{4}(\.\d)?", ref_regex=r"([DE]|FEA)[\- ]\d{4}(\.\d)?",
icon="fa-house-flood-water", icon="fa-house-flood-water",
region_flag="🇪🇸", region_flag="🇪🇸",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="DTMBA", name="DTMBA",
@@ -224,14 +220,14 @@ SIGS = [
ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}", ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}",
icon="fa-masks-theater", icon="fa-masks-theater",
region_flag="🇮🇹", region_flag="🇮🇹",
refs_globally_unique=True refs_globally_unique=True,
), ),
SIG( SIG(
name="BIWOTA", name="BIWOTA",
comment_names=["BIWOTA"], comment_names=["BIWOTA"],
description="British Inland Waterways on the Air", description="British Inland Waterways on the Air",
icon="fa-ship", icon="fa-ship",
region_flag="🇬🇧" region_flag="🇬🇧",
), ),
SIG( SIG(
name="COTA", name="COTA",
@@ -240,7 +236,7 @@ SIGS = [
ref_regex=r"[A-Z]{3}\-[0-9]{3,5}", ref_regex=r"[A-Z]{3}\-[0-9]{3,5}",
icon="fa-chess-rook", icon="fa-chess-rook",
region_flag="🇩🇪", region_flag="🇩🇪",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="PGA", name="PGA",
@@ -249,7 +245,7 @@ SIGS = [
ref_regex=r"[A-Z]{2}[0-9]{2}", ref_regex=r"[A-Z]{2}[0-9]{2}",
icon="fa-g", icon="fa-g",
region_flag="🇵🇱", region_flag="🇵🇱",
refs_globally_unique=False refs_globally_unique=False,
), ),
SIG( SIG(
name="Toilets", name="Toilets",
@@ -258,19 +254,13 @@ SIGS = [
ref_regex=r"T\-[0-9]{2}", ref_regex=r"T\-[0-9]{2}",
icon="fa-toilet", icon="fa-toilet",
region_flag="🏴‍☠️", region_flag="🏴‍☠️",
refs_globally_unique=True refs_globally_unique=True,
), ),
] ]
# Modes. Note "DIGI" and "DIGITAL" are also supported but are normalised into "DATA". # Modes. Note "DIGI" and "DIGITAL" are also supported but are normalised into "DATA".
CW_MODES = ["CW"] CW_MODES = ["CW"]
PHONE_MODES = [ PHONE_MODES = ["PHONE", "SSB", "AM", "FM", "DV"]
"PHONE",
"SSB",
"AM",
"FM",
"DV"
]
DATA_MODES = [ DATA_MODES = [
"DATA", "DATA",
"FT8", "FT8",
@@ -341,9 +331,6 @@ BANDS = [
] ]
UNKNOWN_BAND = Band(name="Unknown", start_freq=0, end_freq=0) UNKNOWN_BAND = Band(name="Unknown", start_freq=0, end_freq=0)
# Continents
CONTINENTS = ["EU", "NA", "SA", "AS", "AF", "OC", "AN"]
# Propagation modes used in VHF/UHF DX cluster comments, e.g. "JN61ES<ES>JM56XT". I don't think there's an official list # Propagation modes used in VHF/UHF DX cluster comments, e.g. "JN61ES<ES>JM56XT". I don't think there's an official list
# of these anywhere, but here are some I've seen or seen reference to # of these anywhere, but here are some I've seen or seen reference to
PROPAGATION_MODES = { PROPAGATION_MODES = {
+39
View File
@@ -0,0 +1,39 @@
from enum import Enum
class Continent(str, Enum):
EU = "EU"
NA = "NA"
SA = "SA"
AS = "AS"
AF = "AF"
OC = "OC"
AN = "AN"
class ModeSource(str, Enum):
"""Where the mode data came from in a spot."""
SPOT = "SPOT"
COMMENT = "COMMENT"
BANDPLAN = "BANDPLAN"
NONE = "NONE"
class LocationSourceForSpot(str, Enum):
"""Where the location data came from in a spot."""
SPOT = "SPOT"
SIG_REF_LOOKUP = "SIG REF LOOKUP"
GRID = "GRID"
HOME_QTH = "HOME QTH"
DXCC = "DXCC"
NONE = "NONE"
class LocationSourceForCallsign(str, Enum):
"""Where the location data came from in a callsign data object."""
HOME_QTH = "HOME QTH"
DXCC = "DXCC"
NONE = "NONE"
+4 -3
View File
@@ -15,7 +15,8 @@ from core.constants import (
UNKNOWN_BAND, UNKNOWN_BAND,
) )
from core.data_store import DATA_STORE from core.data_store import DATA_STORE
from data.callsign import Callsign from core.enums import Continent
from data.callsign import Callsign, LocationSourceForCallsign
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -116,7 +117,7 @@ def get_callsign_object_from_pyhamtools_callinfo(callsign, callinfo):
country = data.get("country", None) country = data.get("country", None)
dxcc_id = data.get("adif", None) dxcc_id = data.get("adif", None)
continent = data.get("continent", None) continent = Continent(data.get("continent", None))
cq_zone = data.get("cqz", None) cq_zone = data.get("cqz", None)
itu_zone = data.get("ituz", None) itu_zone = data.get("ituz", None)
lat = float(data["latitude"]) if "latitude" in data else None lat = float(data["latitude"]) if "latitude" in data else None
@@ -136,7 +137,7 @@ def get_callsign_object_from_pyhamtools_callinfo(callsign, callinfo):
latitude=lat, latitude=lat,
longitude=lon, longitude=lon,
grid=grid, grid=grid,
location_source="DXCC", location_source=LocationSourceForCallsign.DXCC,
) )
except (KeyError, ValueError): except (KeyError, ValueError):
+3 -2
View File
@@ -7,6 +7,7 @@ from datetime import datetime, timedelta
import pytz import pytz
from core.call_lookup_helper import get_call_info from core.call_lookup_helper import get_call_info
from core.enums import Continent
from core.sig_lookup_helper import populate_missing_sig_ref_info from core.sig_lookup_helper import populate_missing_sig_ref_info
from core.utils import get_flag_for_dxcc from core.utils import get_flag_for_dxcc
@@ -28,7 +29,7 @@ class Alert:
# Country flag of the DX operator # Country flag of the DX operator
dx_flag: str | None = None dx_flag: str | None = None
# Continent of the DX operator # Continent of the DX operator
dx_continent: str | None = None dx_continent: Continent | None = None
# DXCC ID of the DX operator # DXCC ID of the DX operator
dx_dxcc_id: int | None = None dx_dxcc_id: int | None = None
# CQ zone of the DX operator # CQ zone of the DX operator
@@ -92,7 +93,7 @@ class Alert:
if self.dx_calls and self.dx_calls[0] and not self.dx_country: if self.dx_calls and self.dx_calls[0] and not self.dx_country:
self.dx_country = call_info.country self.dx_country = call_info.country
if self.dx_calls and self.dx_calls[0] and not self.dx_continent: if self.dx_calls and self.dx_calls[0] and not self.dx_continent:
self.dx_continent = call_info.continent self.dx_continent = Continent(call_info.continent)
if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone: if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone:
self.dx_cq_zone = call_info.cq_zone self.dx_cq_zone = call_info.cq_zone
if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone: if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone:
+3 -1
View File
@@ -1,5 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from core.enums import LocationSourceForCallsign
@dataclass @dataclass
class Callsign: class Callsign:
@@ -36,7 +38,7 @@ class Callsign:
# ITU zone in which the callsign indicates they are operating # ITU zone in which the callsign indicates they are operating
itu_zone: int | None = None itu_zone: int | None = None
# Location source. This can be "HOME QTH" or "DXCC" depending on which provider gave us a location # Location source. This can be "HOME QTH" or "DXCC" depending on which provider gave us a location
location_source: str | None = None location_source: LocationSourceForCallsign = LocationSourceForCallsign.NONE
def fully_populated(self): def fully_populated(self):
"""Utility method to indicate that the callsign data is fully populated. Multiple providers can return data for """Utility method to indicate that the callsign data is fully populated. Multiple providers can return data for
+20 -20
View File
@@ -12,6 +12,7 @@ from pyhamtools.locator import latlong_to_locator, locator_to_latlong
from core.call_lookup_helper import get_call_info from core.call_lookup_helper import get_call_info
from core.config import MAX_SPOT_AGE from core.config import MAX_SPOT_AGE
from core.constants import MODE_ALIASES, PROPAGATION_MODES, SIGS from core.constants import MODE_ALIASES, PROPAGATION_MODES, SIGS
from core.enums import Continent, LocationSourceForSpot, ModeSource
from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone
from core.sig_lookup_helper import populate_missing_sig_ref_info from core.sig_lookup_helper import populate_missing_sig_ref_info
from core.sig_utils import ( from core.sig_utils import (
@@ -52,7 +53,7 @@ class Spot:
# Country flag of the DX operator # Country flag of the DX operator
dx_flag: str | None = None dx_flag: str | None = None
# Continent of the DX operator # Continent of the DX operator
dx_continent: str | None = None dx_continent: Continent | None = None
# DXCC ID of the DX operator # DXCC ID of the DX operator
dx_dxcc_id: int | None = None dx_dxcc_id: int | None = None
# CQ zone of the DX operator # CQ zone of the DX operator
@@ -68,9 +69,8 @@ class Spot:
# lookup # lookup
dx_latitude: float | None = None dx_latitude: float | None = None
dx_longitude: float | None = None dx_longitude: float | None = None
# DX Location source. Indicates how accurate the location might be. Values: "SPOT", "GRID", "SIG REF LOOKUP", # DX Location source. Indicates how accurate the location might be.
# "HOME QTH", "DXCC", "NONE" dx_location_source: LocationSourceForSpot = LocationSourceForSpot.NONE
dx_location_source: str = "NONE"
# DX Location good. Indicates that the software thinks the location data is good enough to plot on a map. This is # 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 # 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. # DX callsign doesn't have a suffix like /P.
@@ -85,7 +85,7 @@ class Spot:
# Country flag of the spotter # Country flag of the spotter
de_flag: str | None = None de_flag: str | None = None
# Continent of the spotter # Continent of the spotter
de_continent: str | None = None de_continent: Continent | None = None
# DXCC ID of the spotter # DXCC ID of the spotter
de_dxcc_id: int | None = None de_dxcc_id: int | None = None
# If this is an APRS/Packet/etc spot, what SSID was the spotter/receiver using? # If this is an APRS/Packet/etc spot, what SSID was the spotter/receiver using?
@@ -107,7 +107,7 @@ class Spot:
# Inferred mode "family". One of "CW", "PHONE" or "DIGI". # Inferred mode "family". One of "CW", "PHONE" or "DIGI".
mode_type: str | None = None mode_type: str | None = None
# Source of the mode information. "SPOT", "COMMENT", "BANDPLAN" or "NONE" # Source of the mode information. "SPOT", "COMMENT", "BANDPLAN" or "NONE"
mode_source: str = "NONE" mode_source: ModeSource = ModeSource.NONE
# Frequency, in Hz # Frequency, in Hz
freq: float | None = None freq: float | None = None
# Band, defined by the frequency, e.g. "40m" or "70cm" # Band, defined by the frequency, e.g. "40m" or "70cm"
@@ -185,7 +185,7 @@ class Spot:
if self.dx_call and not self.dx_country: if self.dx_call and not self.dx_country:
self.dx_country = dx_call_info.country self.dx_country = dx_call_info.country
if self.dx_call and not self.dx_continent: if self.dx_call and not self.dx_continent:
self.dx_continent = dx_call_info.continent self.dx_continent = Continent(dx_call_info.continent)
if self.dx_call and not self.dx_dxcc_id: if self.dx_call and not self.dx_dxcc_id:
self.dx_dxcc_id = dx_call_info.dxcc_id self.dx_dxcc_id = dx_call_info.dxcc_id
if self.dx_dxcc_id and not self.dx_flag: if self.dx_dxcc_id and not self.dx_flag:
@@ -223,7 +223,7 @@ class Spot:
if not self.de_country: if not self.de_country:
self.de_country = de_call_info.country self.de_country = de_call_info.country
if not self.de_continent: if not self.de_continent:
self.de_continent = de_call_info.continent self.de_continent = Continent(de_call_info.continent)
if not self.de_dxcc_id: if not self.de_dxcc_id:
self.de_dxcc_id = de_call_info.dxcc_id self.de_dxcc_id = de_call_info.dxcc_id
if self.de_dxcc_id and not self.de_flag: if self.de_dxcc_id and not self.de_flag:
@@ -240,13 +240,13 @@ class Spot:
# Mode from comments or bandplan # Mode from comments or bandplan
if self.mode: if self.mode:
self.mode_source = "SPOT" self.mode_source = ModeSource.SPOT
if self.comment and not self.mode: if self.comment and not self.mode:
self.mode = infer_mode_from_comment(self.comment) self.mode = infer_mode_from_comment(self.comment)
self.mode_source = "COMMENT" self.mode_source = ModeSource.COMMENT
if self.freq and not self.mode: if self.freq and not self.mode:
self.mode = infer_mode_from_frequency(self.freq) self.mode = infer_mode_from_frequency(self.freq)
self.mode_source = "BANDPLAN" self.mode_source = ModeSource.BANDPLAN
# Normalise mode if necessary. # Normalise mode if necessary.
if self.mode in MODE_ALIASES: if self.mode in MODE_ALIASES:
@@ -258,7 +258,7 @@ class Spot:
# If we have a latitude or grid at this point, it can only have been provided by the spot itself # If we have a latitude or grid at this point, it can only have been provided by the spot itself
if self.dx_latitude or self.dx_grid: if self.dx_latitude or self.dx_grid:
self.dx_location_source = "SPOT" self.dx_location_source = LocationSourceForSpot.SPOT
# Set the top-level "SIG" if it is missing but we have at least one SIG ref. # Set the top-level "SIG" if it is missing but we have at least one SIG ref.
if not self.sig and self.sig_refs and len(self.sig_refs) > 0: if not self.sig and self.sig_refs and len(self.sig_refs) > 0:
@@ -324,9 +324,9 @@ class Spot:
self.dx_latitude = sig_ref.latitude self.dx_latitude = sig_ref.latitude
self.dx_longitude = sig_ref.longitude self.dx_longitude = sig_ref.longitude
if self.sig == "WAB" or self.sig == "WAI" or self.sig == "Tiles": if self.sig == "WAB" or self.sig == "WAI" or self.sig == "Tiles":
self.dx_location_source = "GRID" self.dx_location_source = LocationSourceForSpot.GRID
else: else:
self.dx_location_source = "SIG REF LOOKUP" self.dx_location_source = LocationSourceForSpot.SIG_REF_LOOKUP
# If the spot itself doesn't have a SIG yet, but we have at least one SIG reference, take that reference's SIG # If the spot itself doesn't have a SIG yet, but we have at least one SIG reference, take that reference's SIG
# and apply it to the whole spot. # and apply it to the whole spot.
@@ -347,7 +347,7 @@ class Spot:
self.de_grid = grid_mode_grid_match.group(1).upper() self.de_grid = grid_mode_grid_match.group(1).upper()
if not self.dx_grid: if not self.dx_grid:
self.dx_grid = grid_mode_grid_match.group(4).upper() self.dx_grid = grid_mode_grid_match.group(4).upper()
self.dx_location_source = "GRID" self.dx_location_source = LocationSourceForSpot.GRID
# And extract propagation mode (group 2 for <...>, group 3 for (...)): # And extract propagation mode (group 2 for <...>, group 3 for (...)):
mode_tag = (grid_mode_grid_match.group(2) or grid_mode_grid_match.group(3) or "").upper() mode_tag = (grid_mode_grid_match.group(2) or grid_mode_grid_match.group(3) or "").upper()
@@ -368,7 +368,7 @@ class Spot:
# regex matches, so extract grids: # regex matches, so extract grids:
if not self.dx_grid: if not self.dx_grid:
self.dx_grid = grid_mode_grid_match.group(1).upper() self.dx_grid = grid_mode_grid_match.group(1).upper()
self.dx_location_source = "GRID" self.dx_location_source = LocationSourceForSpot.GRID
if not self.de_grid: if not self.de_grid:
self.de_grid = grid_mode_grid_match.group(2).upper() self.de_grid = grid_mode_grid_match.group(2).upper()
@@ -440,10 +440,10 @@ class Spot:
self.dx_latitude self.dx_latitude
and self.dx_longitude and self.dx_longitude
and ( and (
self.dx_location_source == "SPOT" self.dx_location_source == LocationSourceForSpot.SPOT
or self.dx_location_source == "SIG REF LOOKUP" or self.dx_location_source == LocationSourceForSpot.SIG_REF_LOOKUP
or self.dx_location_source == "GRID" or self.dx_location_source == LocationSourceForSpot.GRID
or (self.dx_location_source == "HOME QTH" and "/" not in (self.dx_call or "")) or (self.dx_location_source == LocationSourceForSpot.HOME_QTH and "/" not in (self.dx_call or ""))
) )
) )
+4 -3
View File
@@ -11,8 +11,9 @@ from requests_cache import CachedSession
from core.config import SERVER_OWNER_CALLSIGN from core.config import SERVER_OWNER_CALLSIGN
from core.constants import HTTP_HEADERS, SOFTWARE_VERSION from core.constants import HTTP_HEADERS, SOFTWARE_VERSION
from core.data_store import CACHE_DIR, DATA_STORE from core.data_store import CACHE_DIR, DATA_STORE
from core.enums import Continent
from core.url_data_cache import URLDataCache from core.url_data_cache import URLDataCache
from data.callsign import Callsign from data.callsign import Callsign, LocationSourceForCallsign
from providers.callsigndata.api_query_callsign_data_provider import ( from providers.callsigndata.api_query_callsign_data_provider import (
APIQueryCallsignDataProvider, APIQueryCallsignDataProvider,
) )
@@ -142,12 +143,12 @@ class HamQTH(APIQueryCallsignDataProvider):
name=data.get("nick", None), name=data.get("nick", None),
qth=data.get("qth", None), qth=data.get("qth", None),
country=data.get("country", None), country=data.get("country", None),
continent=data.get("continent", None), continent=Continent(data.get("continent", None)),
latitude=lat, latitude=lat,
longitude=lon, longitude=lon,
grid=grid, grid=grid,
dxcc_id=int(data["adif"]) if "adif" in data else None, dxcc_id=int(data["adif"]) if "adif" in data else None,
cq_zone=int(data["cq"]) if "cq" in data else None, cq_zone=int(data["cq"]) if "cq" in data else None,
itu_zone=int(data["itu"]) if "itu" in data else None, itu_zone=int(data["itu"]) if "itu" in data else None,
location_source="HOME QTH", location_source=LocationSourceForCallsign.HOME_QTH,
) )
+3 -2
View File
@@ -10,6 +10,7 @@ from requests_cache import CachedSession
from core.constants import HTTP_HEADERS from core.constants import HTTP_HEADERS
from core.data_store import CACHE_DIR, DATA_STORE from core.data_store import CACHE_DIR, DATA_STORE
from core.enums import LocationSourceForCallsign, Continent
from core.url_data_cache import URLDataCache from core.url_data_cache import URLDataCache
from data.callsign import Callsign from data.callsign import Callsign
from providers.callsigndata.api_query_callsign_data_provider import ( from providers.callsigndata.api_query_callsign_data_provider import (
@@ -170,12 +171,12 @@ class QRZ(APIQueryCallsignDataProvider):
name=name, name=name,
qth=data.get("addr2", None), qth=data.get("addr2", None),
country=data.get("country", None), country=data.get("country", None),
continent=data.get("continent", None), continent=Continent(data.get("continent", None)),
latitude=lat, latitude=lat,
longitude=lon, longitude=lon,
grid=grid, grid=grid,
dxcc_id=int(data["adif"]) if "adif" in data else None, dxcc_id=int(data["adif"]) if "adif" in data else None,
cq_zone=int(data["cqzone"]) if "cqzone" in data else None, cq_zone=int(data["cqzone"]) if "cqzone" in data else None,
itu_zone=int(data["ituzone"]) if "ituzone" in data else None, itu_zone=int(data["ituzone"]) if "ituzone" in data else None,
location_source="HOME QTH", location_source=LocationSourceForCallsign.HOME_QTH,
) )
+2 -1
View File
@@ -4,6 +4,7 @@ from datetime import datetime
import requests import requests
from core.constants import HTTP_HEADERS from core.constants import HTTP_HEADERS
from core.enums import LocationSourceForSpot
from data.sig_ref import SIGRef from data.sig_ref import SIGRef
from data.spot import Spot from data.spot import Spot
from providers.spot.http_spot_provider import HTTPSpotProvider from providers.spot.http_spot_provider import HTTPSpotProvider
@@ -73,7 +74,7 @@ class Tiles(HTTPSpotProvider):
dx_grid=source_spot["maidenhead_grid"], dx_grid=source_spot["maidenhead_grid"],
dx_latitude=source_spot["latitude"], dx_latitude=source_spot["latitude"],
dx_longitude=source_spot["longitude"], dx_longitude=source_spot["longitude"],
dx_location_source="GRID", dx_location_source=LocationSourceForSpot.GRID,
) )
# Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do
+3 -8
View File
@@ -9,15 +9,14 @@ import tornado
from tornado import httputil from tornado import httputil
from tornado.web import Application from tornado.web import Application
from core.enums import Continent
from core.prometheus_metrics_handler import api_requests_counter from core.prometheus_metrics_handler import api_requests_counter
from core.utils import safe_json_dumps from core.utils import safe_json_dumps
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CONTINENTS = ["EU", "NA", "SA", "AS", "AF", "OC", "AN"] CONTINENTS = [c.value for c in Continent]
BANDS = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m"] BANDS = ["160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m"]
CONTINENTS_SET = frozenset(CONTINENTS)
BANDS_SET = frozenset(BANDS)
class APIDxStatsHandler(tornado.web.RequestHandler): class APIDxStatsHandler(tornado.web.RequestHandler):
@@ -53,11 +52,7 @@ class APIDxStatsHandler(tornado.web.RequestHandler):
continue continue
if not spot.time or spot.time < one_hour_ago: if not spot.time or spot.time < one_hour_ago:
continue continue
if ( if spot.de_continent in CONTINENTS and spot.dx_continent in CONTINENTS and spot.band in BANDS:
spot.de_continent in CONTINENTS_SET
and spot.dx_continent in CONTINENTS_SET
and spot.band in BANDS_SET
):
counts[spot.de_continent, spot.dx_continent, spot.band] += 1 counts[spot.de_continent, spot.dx_continent, spot.band] += 1
result = { result = {
+3 -9
View File
@@ -8,14 +8,8 @@ from tornado import httputil
from tornado.web import Application from tornado.web import Application
from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE from core.config import ALLOW_SPOTTING, MAX_SPOT_AGE
from core.constants import ( from core.constants import ALL_MODES, BANDS, MODE_TYPES, PROPAGATION_MODES, SIGS
ALL_MODES, from core.enums import Continent
BANDS,
CONTINENTS,
MODE_TYPES,
PROPAGATION_MODES,
SIGS,
)
from core.prometheus_metrics_handler import api_requests_counter from core.prometheus_metrics_handler import api_requests_counter
from core.utils import safe_json_dumps from core.utils import safe_json_dumps
@@ -96,7 +90,7 @@ class APIOptionsHandler(tornado.web.RequestHandler):
"spot_providers_enabled_by_default": spot_providers_enabled_by_default, "spot_providers_enabled_by_default": spot_providers_enabled_by_default,
"alert_providers": alert_providers, "alert_providers": alert_providers,
"callsign_data_providers": callsign_data_providers, "callsign_data_providers": callsign_data_providers,
"continents": CONTINENTS, "continents": [c.value for c in Continent],
"propagation_modes": list(PROPAGATION_MODES.values()), "propagation_modes": list(PROPAGATION_MODES.values()),
"max_spot_age": MAX_SPOT_AGE, "max_spot_age": MAX_SPOT_AGE,
"spot_allowed": ALLOW_SPOTTING, "spot_allowed": ALLOW_SPOTTING,
-1
View File
@@ -10,7 +10,6 @@ from core.constants import SOFTWARE_VERSION
from core.data_providers import DATA_PROVIDERS from core.data_providers import DATA_PROVIDERS
from core.data_store import DATA_STORE from core.data_store import DATA_STORE
from core.status_reporter import StatusReporter from core.status_reporter import StatusReporter
from core.utils import infer_mode_from_comment
from server.webserver import WEB_SERVER from server.webserver import WEB_SERVER
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
+2 -2
View File
@@ -981,7 +981,7 @@ components:
- NONE - NONE
example: SPOT example: SPOT
LocationSourceForAlert: LocationSourceForCallsign:
type: string type: string
enum: enum:
- "HOME QTH" - "HOME QTH"
@@ -2197,7 +2197,7 @@ components:
Where we got the location (grid/latitude/longitude) from. Unlike a spot where we might Where we got the location (grid/latitude/longitude) from. Unlike a spot where we might
have a summit position or WAB square, here the only options are an online QTH lookup, or have a summit position or WAB square, here the only options are an online QTH lookup, or
a location based purely on DXCC, or nothing. a location based purely on DXCC, or nothing.
$ref: "#/components/schemas/LocationSourceForAlert" $ref: "#/components/schemas/LocationSourceForCallsign"
GridLookup: GridLookup:
type: object type: object
+1 -1
View File
@@ -76,7 +76,7 @@
</div> </div>
<script src="/static/js/add-spot.js?v=1788209684"></script> <script src="/static/js/add-spot.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active"); $("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -84,7 +84,7 @@
</div> </div>
<script src="/static/js/alerts.js?v=1788209684"></script> <script src="/static/js/alerts.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active"); $("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1788209684"></script> <script src="/static/js/spotsbandsandmap.js?v=1788330362"></script>
<script src="/static/js/bands.js?v=1788209684"></script> <script src="/static/js/bands.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-bands").addClass("active"); $("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1788209684" type="text/css"> <link rel="stylesheet" href="/static/css/style.css?v=1788330362" type="text/css">
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet"> <link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -15,10 +15,10 @@
window.fetchEventSource = fetchEventSource; window.fetchEventSource = fetchEventSource;
</script> </script>
<script src="/static/js/utils.js?v=1788209684"></script> <script src="/static/js/utils.js?v=1788330362"></script>
<script src="/static/js/ui-ham.js?v=1788209684"></script> <script src="/static/js/ui-ham.js?v=1788330362"></script>
<script src="/static/js/geo.js?v=1788209684"></script> <script src="/static/js/geo.js?v=1788330362"></script>
<script src="/static/js/common.js?v=1788209684"></script> <script src="/static/js/common.js?v=1788330362"></script>
{% end %} {% end %}
{% block body %} {% block body %}
<div class="container"> <div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div> </div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script> <script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1788209684"></script> <script src="/static/js/conditions.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active"); $("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}"; const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}";
</script> </script>
<script src="/static/js/spotsbandsandmap.js?v=1788209683"></script> <script src="/static/js/spotsbandsandmap.js?v=1788330362"></script>
<script src="/static/js/map.js?v=1788209683"></script> <script src="/static/js/map.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-map").addClass("active"); $("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1788209683"></script> <script src="/static/js/spotsbandsandmap.js?v=1788330362"></script>
<script src="/static/js/spots.js?v=1788209683"></script> <script src="/static/js/spots.js?v=1788330362"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-spots").addClass("active"); $("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -86,7 +86,7 @@
</div> </div>
</div> </div>
<script src="/static/js/status.js?v=1788209684"></script> <script src="/static/js/status.js?v=1788330362"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$("#nav-link-status").addClass("active"); $("#nav-link-status").addClass("active");