From b0caf697ae2754886a2df4e5ad3eeb07fc188ec0 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Mon, 31 Aug 2026 21:17:55 +0100 Subject: [PATCH] Support COTA. Closes #131 --- config-example.yml | 3 +++ core/constants.py | 9 +++++++ core/utils.py | 4 +-- data/spot.py | 2 +- providers/sigrefdata/cota.py | 51 ++++++++++++++++++++++++++++++++++++ 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 +- 13 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 providers/sigrefdata/cota.py diff --git a/config-example.yml b/config-example.yml index 50a220f..0c5688f 100644 --- a/config-example.yml +++ b/config-example.yml @@ -258,6 +258,9 @@ sig_ref_data_providers: - class: "DTMBA" enabled: true + - class: "COTA" + enabled: true + - class: "Toilets" enabled: true diff --git a/core/constants.py b/core/constants.py index ab518c8..7397c6f 100644 --- a/core/constants.py +++ b/core/constants.py @@ -233,6 +233,15 @@ SIGS = [ icon="fa-ship", region_flag="🇬🇧" ), + SIG( + name="COTA", + comment_names=["COTA"], + description="Castles on the Air", + ref_regex=r"[A-Z]{3}\-[0-9]{3,5}", + icon="fa-chess-rook", + region_flag="🇩🇪", + refs_globally_unique=False + ), SIG( name="Toilets", comment_names=[], diff --git a/core/utils.py b/core/utils.py index d7982f9..11ca7a5 100644 --- a/core/utils.py +++ b/core/utils.py @@ -31,10 +31,10 @@ def infer_mode_from_comment(comment): """Infer a mode from the comment""" for mode in ALL_MODES: - if re.match(r"(^|\W)" + mode + r"(^|\W)", comment, re.IGNORECASE): + if re.match(r"(^|\W)" + mode + r"($|\W)", comment, re.IGNORECASE): return mode for mode in MODE_ALIASES: - if re.match(r"(^|\W)" + mode + r"(^|\W)", comment, re.IGNORECASE): + if re.match(r"(^|\W)" + mode + r"($|\W)", comment, re.IGNORECASE): return MODE_ALIASES[mode] return None diff --git a/data/spot.py b/data/spot.py index 3786107..fce4fa9 100644 --- a/data/spot.py +++ b/data/spot.py @@ -270,7 +270,7 @@ class Spot: sig = self.sig_refs[0].sig.upper() regex = get_ref_regex_for_sig(sig) if regex: - all_comment_ref_matches = re.finditer(r"(^|\W)(" + regex + r")(^|\W)", self.comment, re.IGNORECASE) + all_comment_ref_matches = re.finditer(r"(^|\W)(" + regex + r")($|\W)", self.comment, re.IGNORECASE) for ref_match in all_comment_ref_matches: self._append_sig_ref_if_missing(SIGRef(id=ref_match.group(2).upper(), sig=sig)) diff --git a/providers/sigrefdata/cota.py b/providers/sigrefdata/cota.py new file mode 100644 index 0000000..d9c0043 --- /dev/null +++ b/providers/sigrefdata/cota.py @@ -0,0 +1,51 @@ +from time import sleep + +from pyhamtools.locator import latlong_to_locator + +from data.sig_ref import SIGRef +from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider + + +class COTA(FileDownloadSIGRefDataProvider): + """SIG ref data provider for Castles on the Air""" + + POLL_INTERVAL_DAYS = 30 + SIG = "COTA" + DATA_URL = "https://www.cotagroup.org/cotagroup/map/data/castles-all-7d90ee2a5e1175e5dece1bbf9dc87504.json" + + def __init__(self, provider_config): + super().__init__(self.SIG, provider_config, self.DATA_URL, self.POLL_INTERVAL_DAYS) + + def _http_response_to_data(self, http_response): + new_data = [] + data = http_response.json() + if isinstance(data, list): + for ref in data[2]: + ref_id = ref[3] + name = ref[4] + lat = float(ref[0]) + lon = float(ref[1]) + grid = latlong_to_locator(lat, lon) + + new_data.append( + SIGRef( + sig=self.SIG, + id=ref_id, + name=name, + ref_type="Castle", + grid=grid, + latitude=lat, + longitude=lon, + ) + ) + + # Bail out if a stop has been requested, i.e. the program is shutting down - no need to parse the rest + # of the data in this case + if self._stop_event.is_set(): + break + + # Very short pause. This will extend the time to handle sig refs by a few seconds but will ensure some time + # is available for other threads e.g. the web server. + sleep(0.001) + + return new_data diff --git a/templates/add_spot.html b/templates/add_spot.html index 6c8f963..6f0eb8c 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -76,7 +76,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index 0d62a91..02d39be 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -84,7 +84,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 53a1932..39ce6c5 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,8 +76,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index 4689d7f..1d2a93e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -15,10 +15,10 @@ window.fetchEventSource = fetchEventSource; - - - - + + + + {% end %} {% block body %}
diff --git a/templates/conditions.html b/templates/conditions.html index 3ec0dc1..49fef8a 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index b4c763e..104a2f9 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 3327cc6..6a747a9 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -113,8 +113,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index a7ff123..7a1c69a 100644 --- a/templates/status.html +++ b/templates/status.html @@ -86,7 +86,7 @@ - +