from enum import Enum class Continent(str, Enum): EU = "EU" NA = "NA" SA = "SA" AS = "AS" AF = "AF" OC = "OC" AN = "AN" class Mode(str, Enum): CW = "CW" PHONE = "PHONE" SSB = "SSB" AM = "AM" FM = "FM" DV = "DV" DATA = "DATA" FT8 = "FT8" FT4 = "FT4" RTTY = "RTTY" SSTV = "SSTV" JS8 = "JS8" HELL = "HELL" PSK = "PSK" FSK = "FSK" OLIVIA = "OLIVIA" PKT = "PKT" MSK144 = "MSK144" @property def is_cw(self) -> bool: return self == Mode.CW @property def is_phone(self) -> bool: return self in {Mode.PHONE, Mode.SSB, Mode.AM, Mode.FM, Mode.DV} @property def is_data(self) -> bool: return not (self.is_cw or self.is_phone) @staticmethod def from_name(name): """Convert a string to an enum mode using the alias table.""" if not name: return None try: return Mode(name.upper()) except (KeyError, ValueError): try: return Mode(MODE_ALIASES[name.upper()]) except (KeyError, ValueError): return None class ModeType(str, Enum): PHONE = "PHONE" CW = "CW" DATA = "DATA" class ModeSource(str, Enum): """Where the mode data came from in a spot.""" SPOT = "SPOT" COMMENT = "COMMENT" BANDPLAN = "BANDPLAN" 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" class LocationSourceForCallsign(str, Enum): """Where the location data came from in a callsign data object.""" HOME_QTH = "HOME QTH" 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.""" PARK = "PARK" SUMMIT = "SUMMIT" CASTLE = "CASTLE" LIGHTHOUSE = "LIGHTHOUSE" ISLAND = "ISLAND" BUNKER = "BUNKER" MILL = "MILL" SILO = "SILO" BEACH = "BEACH" LAKE = "LAKE" TOWER = "TOWER" WATERWAY = "WATERWAY" TOWN = "TOWN" BUILDING = "BUILDING" REGION = "REGION" GRID = "GRID" TOILET = "TOILET" class ActivityType(str, Enum): """Type of an activity. Used to group them in the web UI.""" TRADITIONAL = "TRADITIONAL" ADVENTURE = "ADVENTURE" REGIONAL = "REGIONAL" EVENT = "EVENT" # Mode aliases. Sometimes we get spots with a mode described in a different way that is effectively the same as a mode # we already know, or we want to normalise things for consistency. The lookup table for this is here. Incoming spots # that match a key in this table will be converted to the corresponding value, so only the modes above will actually be # present in the spots. MODE_ALIASES = { "USB": "SSB", "LSB": "SSB", "DIGITALVOICE": "DV", "DIGITALVOI": "DV", "DMR": "DV", "DSTAR": "DV", "C4FM": "DV", "FUSION": "DV", "M17": "DV", "RTT": "RTTY", "BPSK": "PSK", "PSK31": "PSK", "BPSK31": "PSK", "MFSK": "FSK", "MFSK32": "FSK", "DIGI": "DATA", "DIGITAL": "DATA", }