From bc326388ac67459a58ad3acc606e44833c6bb273 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Mon, 31 Aug 2026 09:39:05 +0100 Subject: [PATCH] Add support for FEA --- config-example.yml | 3 +++ core/constants.py | 8 +++++- providers/sigrefdata/fea.py | 50 +++++++++++++++++++++++++++++++++++++ requirements.txt | 3 ++- static/apidocs/openapi.yml | 1 + static/js/ui-ham.js | 3 +++ templates/about.html | 4 +-- 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 +- 15 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 providers/sigrefdata/fea.py diff --git a/config-example.yml b/config-example.yml index 151bdf4..50a220f 100644 --- a/config-example.yml +++ b/config-example.yml @@ -246,6 +246,9 @@ sig_ref_data_providers: - class: "DME" enabled: true + - class: "FEA" + enabled: true + - class: "KRMNPA" enabled: true diff --git a/core/constants.py b/core/constants.py index f767e8f..c025e1f 100644 --- a/core/constants.py +++ b/core/constants.py @@ -147,9 +147,15 @@ SIGS = [ SIG( name="DME", comment_names=["DME"], - description="Diplomas de Municipios Españoles", + description="Diploma Municipios de España", ref_regex=r"\d{4,5}", ), + SIG( + name="FEA", + comment_names=["FEA"], + description="Diploma Faros de España", + ref_regex=r"([DE]|FEA)\-\d{4}(\.\d)?", + ), SIG( name="DTMBA", comment_names=["DTMBA"], diff --git a/providers/sigrefdata/fea.py b/providers/sigrefdata/fea.py new file mode 100644 index 0000000..186f8c3 --- /dev/null +++ b/providers/sigrefdata/fea.py @@ -0,0 +1,50 @@ +from io import BytesIO +from time import sleep + +import pdfplumber + +from data.sig_ref import SIGRef +from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider + + +class FEA(FileDownloadSIGRefDataProvider): + """SIG ref data provider for Diploma Faros de España""" + + POLL_INTERVAL_DAYS = 30 + SIG = "FEA" + DATA_URL = "http://ea5ol.net/Lista%20Faros.pdf" + + 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 = [] + + # Use PDFPlumber to extract the tables in the PDF + with pdfplumber.open(BytesIO(http_response.content)) as pdf: + all_rows = [] + + for page_number, page in enumerate(pdf.pages, start=1): + tables = page.extract_tables() + + for table_number, table in enumerate(tables, start=1): + if not table: + continue + + rows = [row for row in table if any(cell and cell.strip() for cell in row)] + all_rows.extend(rows) + + for row in all_rows: + if not "REF" in row[0] and not "\n" in row[0]: + new_data.append(SIGRef(sig=self.SIG, id=row[0].strip(), name=row[1].strip(), ref_type="Lighthouse")) + + # 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/requirements.txt b/requirements.txt index 274f5ed..6a89668 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,4 +20,5 @@ geopandas~=0.13.2 simplejson~=4.1.1 cachetools~=7.1.6 fastkml~=1.4.0 -ruff~=0.16.3 \ No newline at end of file +ruff~=0.16.3 +pdfplumber~=0.11.10 \ No newline at end of file diff --git a/static/apidocs/openapi.yml b/static/apidocs/openapi.yml index e2d495a..f51fadf 100644 --- a/static/apidocs/openapi.yml +++ b/static/apidocs/openapi.yml @@ -852,6 +852,7 @@ components: - WAB - WAI - DME + - FEA - DTMBA - Toilets example: POTA diff --git a/static/js/ui-ham.js b/static/js/ui-ham.js index cee6cf5..32dd14f 100644 --- a/static/js/ui-ham.js +++ b/static/js/ui-ham.js @@ -364,6 +364,7 @@ const SIG_ICONS = { "WAB": "fa-table-cells-large", "WAI": "fa-table-cells-large", "DME": "fa-building", + "FEA": "fa-house-flood-water", "Tiles": "fa-square", "DTMBA": "fa-masks-theater", "Toilets": "fa-toilet" @@ -392,6 +393,8 @@ const SIG_NAMES = { "WWTOTA": "Towers on the Air", "WAB": "Worked All Britain", "WAI": "Worked All Ireland", + "DME": "Diploma Municipios de España", + "FEA": "Diploma Faros de España", "Tiles": "Tiles on the Air", "DTMBA": "Diploma Teatri Musei e Belle Arti", "TOTA": "Toilets on the Air" diff --git a/templates/about.html b/templates/about.html index 767ec08..ab0132c 100644 --- a/templates/about.html +++ b/templates/about.html @@ -102,8 +102,8 @@ on the Air (SIOTA), World Castles Award (WCA), New Zealand on the Air (ZLOTA), Keith Roget Memorial National Parks Award (KRMNPA), South Australia National Parks and Conservation Parks Award (SANPCPA), Wainwrights on the Air (WOTA), Beaches on the Air (BOTA), Lagos y Lagunas On the Air (LLOTA), Towers on the Air, Tiles on - the Air, Worked All Britain (WAB), Worked All Ireland (WAI), el Diploma Municipios de España (DME), Diploma - Teatri Musei e Belle Arti (DTMBA) and Toilets on the Air.

+ the Air, Worked All Britain (WAB), Worked All Ireland (WAI), el Diploma Municipios de España (DME), el Diploma + Faros de España (FEA), il Diploma Teatri Musei e Belle Arti (DTMBA) and Toilets on the Air.

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

diff --git a/templates/add_spot.html b/templates/add_spot.html index fe9a22d..7072301 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 5febae1..f5ca74d 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -84,7 +84,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index 8c5a92e..1df173d 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,8 +76,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index 9171716..c0c5d87 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 e7afb7b..aa8716c 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index ce28770..84452a5 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 e8ed6ce..1023cdc 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -113,8 +113,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index d83b0f7..10ea67b 100644 --- a/templates/status.html +++ b/templates/status.html @@ -86,7 +86,7 @@ - +