From 3bab3cb7841002d7f74a9009964cf64dda8d2138 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Fri, 11 Sep 2026 08:33:00 +0100 Subject: [PATCH] Add EME SIG and do some refactoring --- core/constants.py | 24 ++++++++++++++++-------- data/alert.py | 10 +++++----- data/sig_ref.py | 4 ++-- data/spot.py | 36 +++++++++++++++++++----------------- templates/add_spot.html | 2 +- templates/alerts.html | 2 +- templates/bands.html | 4 ++-- templates/base.html | 10 +++++----- templates/conditions.html | 2 +- templates/map.html | 4 ++-- templates/spots.html | 4 ++-- templates/status.html | 2 +- 12 files changed, 57 insertions(+), 47 deletions(-) diff --git a/core/constants.py b/core/constants.py index e899c53..6c17376 100644 --- a/core/constants.py +++ b/core/constants.py @@ -12,6 +12,22 @@ HAMQTH_PRG = f"Spothole v{SOFTWARE_VERSION} operated by {SERVER_OWNER_CALLSIGN}" # Special Interest Groups SIGS = [ + SIG( + name="AMSAT", + comment_names=[], + description="Amateur Radio Satellites", + sig_type=SIGType.WORLDWIDE, + icon="fa-satellite", + refs_globally_unique=False, + ), + SIG( + name="EME", + comment_names=[], + description="Moonbounce", + sig_type=SIGType.WORLDWIDE, + icon="fa-moon", + refs_globally_unique=False, + ), SIG( name="POTA", comment_names=["POTA"], @@ -359,14 +375,6 @@ SIGS = [ region_flag="🇵🇱", refs_globally_unique=False, ), - SIG( - name="AMSAT", - comment_names=[], - description="Amateur Radio Satellites", - sig_type=SIGType.WORLDWIDE, - icon="fa-satellite", - refs_globally_unique=False, - ), SIG( name="Toilets", comment_names=[], diff --git a/data/alert.py b/data/alert.py index 186273f..7300330 100644 --- a/data/alert.py +++ b/data/alert.py @@ -1,7 +1,7 @@ import hashlib import json import logging -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime, timedelta import pytz @@ -64,7 +64,7 @@ class Alert: # Special Interest Group (SIG), e.g. outdoor activity programme such as POTA sig: str | None = None # SIG references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO - sig_refs: list | None = None + sig_refs: list = field(default_factory=list) # Timing info @@ -128,13 +128,13 @@ class Alert: # Fetch SIG data. In case a particular API doesn't provide a full set of name, lat, lon & grid for a reference # in its initial call, we use this code to populate the rest of the data. This includes working out grid refs # from WAB and WAI, which count as a SIG even though there's no real lookup, just maths - if self.sig_refs and len(self.sig_refs) > 0: + if self.sig_refs: for sig_ref in self.sig_refs: populate_missing_sig_ref_info(sig_ref) # 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. - if self.sig_refs and len(self.sig_refs) > 0 and self.sig_refs[0] and not self.sig: + if self.sig_refs and self.sig_refs[0] and not self.sig: self.sig = self.sig_refs[0].sig # Create an ID based on the source and source ID if possible, as these guaranee uniqueness. If there is no @@ -160,7 +160,7 @@ class Alert: self.icon = "fa-trophy" elif self.alert_type == AlertType.CONTEST: self.icon = "fa-satellite" - elif self.sig_refs and len(self.sig_refs) > 0 and self.sig_refs[0].icon: + elif self.sig_refs and self.sig_refs[0].icon: self.icon = self.sig_refs[0].icon except Exception: diff --git a/data/sig_ref.py b/data/sig_ref.py index 1ab29df..d5fcb00 100644 --- a/data/sig_ref.py +++ b/data/sig_ref.py @@ -8,10 +8,10 @@ class SIGRef: """Data class that defines a Special Interest Group "info" or reference. As well as the basic reference ID we include a name and a lookup URL.""" - # Reference ID, e.g. "GB-0001". - id: str # SIG that this reference is in, e.g. "POTA". sig: str + # Reference ID, e.g. "GB-0001". + id: str | None = None # Name of the reference, e.g. "Null Country Park", if known. name: str | None = None # Type of the reference, e.g. "Park", if known. diff --git a/data/spot.py b/data/spot.py index 2ff0a28..7ae825b 100644 --- a/data/spot.py +++ b/data/spot.py @@ -2,7 +2,7 @@ import hashlib import json import logging import re -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime, timedelta from math import isnan @@ -124,7 +124,7 @@ class Spot: # Special Interest Group (SIG), e.g. outdoor activity programme such as POTA sig: str | None = None # SIG references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO - sig_refs: list | None = None + sig_refs: list = field(default_factory=list) # Timing info @@ -262,12 +262,12 @@ class Spot: self.dx_location_source = LocationSourceForSpot.SPOT # 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: self.sig = self.sig_refs[0].sig.upper() # See if we already have a SIG reference, but the comment looks like it contains more for the same SIG. This # should catch e.g. POTA comments like "2-fer: GB-0001 GB-0002". - if self.comment and self.sig_refs and len(self.sig_refs) > 0 and self.sig_refs[0].sig: + if self.comment and self.sig_refs and self.sig_refs[0].sig: sig = self.sig_refs[0].sig.upper() regex = get_ref_regex_for_sig(sig) if regex: @@ -315,7 +315,7 @@ class Spot: # Fetch SIG data. In case a particular API doesn't provide a full set of name, lat, lon & grid for a reference # in its initial call, we use this code to populate the rest of the data. This includes working out grid refs # from WAB and WAI, which count as a SIG even though there's no real lookup, just maths - if self.sig_refs and len(self.sig_refs) > 0: + if self.sig_refs: for sig_ref in self.sig_refs: sig_ref = populate_missing_sig_ref_info(sig_ref) # If the spot itself doesn't have location yet, but the SIG ref does, extract it @@ -331,7 +331,7 @@ class Spot: # 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. - if self.sig_refs and len(self.sig_refs) > 0 and not self.sig: + if self.sig_refs and not self.sig: self.sig = self.sig_refs[0].sig # Parse "de_griddx_grid" structures from the comment, e.g. "JN61ES(ES)JM56XT" or "JO02GQ<>KN17LG". @@ -359,11 +359,15 @@ class Spot: self.propagation_mode = mode_tag logger.info(f"Seen a new propagation mode tag not yet in the system: {mode_tag}") - # Tag satellite contacts as AMSAT SIG - if self.propagation_mode == "Satellite": - if not self.sig: - self.sig = "AMSAT" - self.sig_refs.append(SIGRef(sig="AMSAT")) + # Set SIGs based on propagation mode + if self.propagation_mode == "Satellite": + if not self.sig: + self.sig = "AMSAT" + self.sig_refs.append(SIGRef(sig="AMSAT")) + if self.propagation_mode == "Earth-Moon-Earth": + if not self.sig: + self.sig = "EME" + self.sig_refs.append(SIGRef(sig="EME")) # Parse "de_grid -> dx_grid" structures from the comment if self.comment: @@ -421,7 +425,7 @@ class Spot: # Determine a "QTH" string. If we have a SIG ref, pick the first one and turn it into a suitable string, # otherwise see what they have set on an online lookup service. - if self.sig_refs and len(self.sig_refs) > 0: + if self.sig_refs: qth = self.sig_refs[0].id if self.sig_refs[0].name: qth += f" {self.sig_refs[0].name}" @@ -468,7 +472,7 @@ class Spot: # Icon for the spot should be the icon of the first SIG ref if present, otherwise a radio tower self.icon = "fa-tower-cell" - if self.sig_refs and len(self.sig_refs) > 0 and self.sig_refs[0].icon: + if self.sig_refs and self.sig_refs[0].icon: self.icon = self.sig_refs[0].icon except Exception: @@ -482,16 +486,14 @@ class Spot: def _append_sig_ref_if_missing(self, new_sig_ref): """Append a sig_ref to the list, so long as it's not already there.""" - sig_refs = self.sig_refs or [] - self.sig_refs = sig_refs new_sig_ref.id = new_sig_ref.id.strip().upper() new_sig_ref.sig = new_sig_ref.sig.strip().upper() if new_sig_ref.id == "": return - for sig_ref in sig_refs: + for sig_ref in self.sig_refs: if sig_ref.id == new_sig_ref.id and sig_ref.sig == new_sig_ref.sig: return - sig_refs.append(new_sig_ref) + self.sig_refs.append(new_sig_ref) def expired(self): """Decide if this spot has expired (in which case it should not be added to the system in the first place, and not diff --git a/templates/add_spot.html b/templates/add_spot.html index 55b3e2a..29134da 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -77,7 +77,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index bfdca7d..5695df9 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -83,7 +83,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 5b63416..cd1b312 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,8 +76,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index 798a429..675d4dd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -16,10 +16,10 @@ window.fetchEventSource = fetchEventSource; - - - - + + + + {% end %} {% block body %}
diff --git a/templates/conditions.html b/templates/conditions.html index a5587c1..ba7d05f 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index c8567e3..161d605 100644 --- a/templates/map.html +++ b/templates/map.html @@ -113,8 +113,8 @@ const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}"; - - + + diff --git a/templates/spots.html b/templates/spots.html index 37a66c8..256ff5b 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -113,8 +113,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index 5a7ad3e..71f3ff5 100644 --- a/templates/status.html +++ b/templates/status.html @@ -86,7 +86,7 @@ - +