Allow some SIG refs to be picked up from comments without separate SIG name. Closes #134

This commit is contained in:
Ian Renton
2026-08-31 20:49:15 +01:00
parent e4738b2c7d
commit 892827f1f8
15 changed files with 111 additions and 49 deletions
+51 -26
View File
@@ -16,91 +16,104 @@ SIGS = [
comment_names=["POTA"],
description="Parks on the Air",
ref_regex=r"[A-Z]{2}\-\d{4,5}|K\-TEST",
icon="fa-tree"
icon="fa-tree",
refs_globally_unique=False
),
SIG(
name="SOTA",
comment_names=["SOTA"],
description="Summits on the Air",
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
),
SIG(
name="WWFF",
comment_names=["WWFF"],
description="World Wide Flora & Fauna",
ref_regex=r"[A-Z0-9]{1,3}FF\-\d{4}",
icon="fa-seedling"
icon="fa-seedling",
refs_globally_unique=True
),
SIG(
name="GMA",
comment_names=["GMA"],
description="Global Mountain Activity",
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
),
SIG(
name="WWBOTA",
comment_names=["WWBOTA", "BOTA"],
description="Worldwide Bunkers on the Air",
ref_regex=r"B\/[A-Z0-9]{1,3}\-\d{3,4}",
icon="fa-radiation"
icon="fa-radiation",
refs_globally_unique=True
),
SIG(
name="HEMA",
comment_names=["HEMA"],
description="HuMPs Excluding Marilyns Award",
ref_regex=r"[A-Z0-9]{1,3}\/[A-Z]{3}\-\d{3}",
icon="fa-mound"
icon="fa-mound",
refs_globally_unique=False
),
SIG(
name="IOTA",
comment_names=["IOTA"],
description="Islands on the Air",
ref_regex=r"[A-Z]{2}\-\d{3}",
icon="fa-book-atlas"
icon="fa-book-atlas",
refs_globally_unique=False
),
SIG(
name="GMA Islands",
comment_names=[],
description="Global Mountain Activity - Islands",
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
),
SIG(
name="ARLHS",
comment_names=["ARLHS"],
description="Amateur Radio Lighthouse Society",
ref_regex=r"[A-Z]{3}[\- ]\d{3,4}",
icon="fa-house-flood-water"
icon="fa-house-flood-water",
refs_globally_unique=False
),
SIG(
name="ILLW",
comment_names=["ILLW"],
description="International Lighthouse & Lightship Weekend",
ref_regex=r"[A-Z]{2}\d{4}",
icon="fa-house-flood-water"
icon="fa-house-flood-water",
refs_globally_unique=False
),
SIG(
name="MOTA",
comment_names=["MOTA"],
description="Mills on the Air",
ref_regex=r"X\d{4,6}",
icon="fa-fan"
icon="fa-fan",
refs_globally_unique=True
),
SIG(
name="SIOTA",
comment_names=["SIOTA"],
description="Silos on the Air",
ref_regex=r"[A-Z]{2}\-[A-Z]{3}\d",
icon="fa-wheat-awn"
icon="fa-wheat-awn",
refs_globally_unique=False
),
SIG(
name="WCA",
comment_names=["WCA"],
description="World Castles Award",
ref_regex=r"[A-Z0-9]{1,3}\-\d{5}",
icon="fa-chess-rook"
icon="fa-chess-rook",
refs_globally_unique=False
),
SIG(
name="ZLOTA",
@@ -108,7 +121,8 @@ SIGS = [
description="New Zealand on the Air",
ref_regex=r"ZL[A-Z]/[A-Z]{2}\-\d{3,4}",
icon="fa-kiwi-bird",
region_flag="🇳🇿"
region_flag="🇳🇿",
refs_globally_unique=True
),
SIG(
name="WOTA",
@@ -116,7 +130,8 @@ SIGS = [
description="Wainwrights on the Air",
ref_regex=r"[A-Z]{3}-[0-9]{2}",
icon="fa-w",
region_flag="🇬🇧"
region_flag="🇬🇧",
refs_globally_unique=False
),
SIG(name="BOTA",
comment_names=[],
@@ -144,21 +159,24 @@ SIGS = [
comment_names=["LLOTA"],
description="Lagos y Lagunas on the Air",
ref_regex=r"LL[A-Z]{2}\-\d{4}",
icon="fa-water"
icon="fa-water",
refs_globally_unique=True
),
SIG(
name="Towers",
comment_names=["TOTA"],
description="Towers on the Air",
ref_regex=r"[A-Z]{2,3}R\-\d{4}",
icon="fa-tower-observation"
icon="fa-tower-observation",
refs_globally_unique=False
),
SIG(
name="Tiles",
comment_names=[],
description="Tiles on the Air",
ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}",
icon="fa-square"
icon="fa-square",
refs_globally_unique=False
),
SIG(
name="WAB",
@@ -166,7 +184,8 @@ SIGS = [
description="Worked All Britain",
ref_regex=r"[A-Z]{1,2}[0-9]{2}",
icon="fa-table-cells-large",
region_flag="🇬🇧"
region_flag="🇬🇧",
refs_globally_unique=False
),
SIG(
name="WAI",
@@ -174,7 +193,8 @@ SIGS = [
description="Worked All Ireland",
ref_regex=r"[A-Z][0-9]{2}",
icon="fa-table-cells-large",
region_flag="🇮🇪"
region_flag="🇮🇪",
refs_globally_unique=False
),
SIG(
name="DME",
@@ -182,17 +202,20 @@ SIGS = [
description="Diploma Municipios de España",
ref_regex=r"\d{4,5}",
icon="fa-building",
region_flag="🇪🇸"
region_flag="🇪🇸",
refs_globally_unique=False
),
SIG(
name="FEA",
comment_names=["FEA"],
description="Diploma Faros de España",
# FEA references are technically [DE]\-\d{4}(\.\d)? but spotters always seem to miss out the D- or E-
# prefix and just use FEA 1234, so we treat FEA reference IDs as if they were just the number.
ref_regex=r"\d{4}(\.\d)?",
# prefix and just use FEA-1234 or FEA 1234, so allow for that. The FEA sigref data provider adds both
# forms to the database.
ref_regex=r"([DE]|FEA)[\- ]\d{4}(\.\d)?",
icon="fa-house-flood-water",
region_flag="🇪🇸"
region_flag="🇪🇸",
refs_globally_unique=True
),
SIG(
name="DTMBA",
@@ -200,7 +223,8 @@ SIGS = [
description="Diploma Teatri Musei e Belle Arti",
ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}",
icon="fa-masks-theater",
region_flag="🇮🇹"
region_flag="🇮🇹",
refs_globally_unique=True
),
SIG(
name="BIWOTA",
@@ -215,7 +239,8 @@ SIGS = [
description="Toilets on the Air",
ref_regex=r"T\-[0-9]{2}",
icon="fa-toilet",
region_flag="🏴‍☠️"
region_flag="🏴‍☠️",
refs_globally_unique=True
),
]
+5
View File
@@ -19,6 +19,11 @@ def get_sig_ref_info(sig, ref_id):
logger.debug("Failed to look up sig_ref info, sig or ref were not set.")
return None
# Sometimes we allow spaces instead of dashes in references due to common usage that way, but official reference
# lists never do, so convert them here.
ref_id.replace(" ", "-")
# Prepare the object to be returned
sig_ref = SIGRef(sig=sig, id=ref_id)
try:
+3 -2
View File
@@ -1,4 +1,5 @@
import logging
import re
import simplejson
from pyhamtools.frequency import freq_to_band
@@ -30,10 +31,10 @@ def infer_mode_from_comment(comment):
"""Infer a mode from the comment"""
for mode in ALL_MODES:
if mode in comment.upper():
if re.match(r"(^|\W)" + mode + r"(^|\W)", comment, re.IGNORECASE):
return mode
for mode in MODE_ALIASES:
if mode in comment.upper():
if re.match(r"(^|\W)" + mode + r"(^|\W)", comment, re.IGNORECASE):
return MODE_ALIASES[mode]
return None
+6
View File
@@ -19,6 +19,12 @@ class SIG:
comment_names: list[str] = field(default_factory=list)
# Regex matcher for references, e.g. for POTA r"[A-Z]{2}\-\d+".
ref_regex: str | None = None
# Identifies that the SIG's reference ID structure defined by its regex is unique across all programmes and
# anything else we expect a user to put in a spot comment, and therefore we can pull references out of
# spot comments without also needing to see the SIG name first. For example, "OHFF-1234" or "B/G-1234" are
# obviously WWFF and WWBOTA, nothing else looks like those. But "SZ09" could be WAB or Tiles, "GB1234" could
# conceivably be POTA or ILLW, etc.
refs_globally_unique: bool = False
# Icon to use in the UI when referencing this SIG. Chosen from the Font Awesome set.
icon: str | None = None
# Emoji flag for the country or region where this SIG is relevant, if any. If None, this implies the SIG is in
+14 -1
View File
@@ -11,7 +11,7 @@ from pyhamtools.locator import latlong_to_locator, locator_to_latlong
from core.call_lookup_helper import get_call_info
from core.config import MAX_SPOT_AGE
from core.constants import MODE_ALIASES, PROPAGATION_MODES
from core.constants import MODE_ALIASES, PROPAGATION_MODES, SIGS
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_utils import (
@@ -298,6 +298,19 @@ class Spot:
for ref_match in ref_matches:
self._append_sig_ref_if_missing(SIGRef(id=ref_match.group(3).upper(), sig=found_sig))
# See if the comment looks like it contains any SIG references *without* the corresponding SIG name, but
# where the SIG reference is unique-looking enough that we can't confuse it with any other SIG.
if self.comment:
for sig in SIGS:
if sig.refs_globally_unique and sig.ref_regex:
ref_matches = re.finditer(r"(^|\W)(" + sig.ref_regex + r")($|\W)", self.comment, re.IGNORECASE)
for ref_match in ref_matches:
# First of all, if we haven't got a SIG for this spot set yet, now we have. This covers things
# like cluster spots where the comment is just "OHFF-1234", now we know it's WWFF.
if not self.sig:
self.sig = sig.name
self._append_sig_ref_if_missing(SIGRef(id=ref_match.group(2).upper(), sig=sig.name))
# 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
+5 -3
View File
@@ -37,9 +37,11 @@ class FEA(FileDownloadSIGRefDataProvider):
for row in all_rows:
if not "REF" in row[0] and not "\n" in row[0]:
# FEA references are technically [DE]\-\d{4}(\.\d)? but spotters always seem to miss out the D- or E-
# prefix and just use FEA 1234, so we treat FEA reference IDs as if they were just the number.
ref_id = row[0].strip().replace("D-", "").replace("E-", "")
new_data.append(SIGRef(sig=self.SIG, id=ref_id, name=row[1].strip(), ref_type="Lighthouse"))
# prefix and just use FEA-1234 or FEA 1234, so we add both copies to the database.
ref_id_1 = row[0].strip()
ref_id_2 = ref_id_1.replace("D-", "FEA-").replace("E-", "FEA-")
new_data.append(SIGRef(sig=self.SIG, id=ref_id_1, name=row[1].strip(), ref_type="Lighthouse"))
new_data.append(SIGRef(sig=self.SIG, id=ref_id_2, 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
+12 -2
View File
@@ -19,7 +19,7 @@ info:
* Added DTMBA, FEA and BIWOTA SIGs
* Removed the distinction between LSB & USB (both will now show as SSB) and between the various digital voice modes, which will now show as DV.
* Added `icon` and `region_flag` to SIG information
* Added `icon`, `region_flag` and `refs_globally_unique` to SIG information
### 2.0
@@ -1505,13 +1505,23 @@ components:
Regex that matches this SIG's reference IDs. Generally for Spothole's own internal use,
clients probably won't need this.
example: "[A-Z]{2}\\-\\d+"
refs_globally_unique:
type: boolean
description: >
Identifies that the SIG's reference ID structure defined by its regex is unique across all programmes and
anything else we expect a user to put in a spot comment, and therefore we can pull references out of
spot comments without also needing to see the SIG name first. For example, "OHFF-1234" or "B/G-1234" are
obviously WWFF and WWBOTA, nothing else looks like those. But "SZ09" could be WAB or Tiles, "GB1234" could
conceivably be POTA or ILLW, etc. Generally for Spothole's own internal use, clients probably won't need
this.
icon:
type: string
description: Icon from the Font Awesome set that represents this SIG, for use in the front end.
example: "fa-tree"
region_flag:
type: string
description: Flag emoji, if this SIG is specific to a country or region. If null, this SIG is treated as worldwide.
description: >
Flag emoji, if this SIG is specific to a country or region. If null, this SIG is treated as worldwide.
example: "🇺🇳"
SolarConditions:
+1 -1
View File
@@ -76,7 +76,7 @@
</div>
<script src="/static/js/add-spot.js?v=1788197899"></script>
<script src="/static/js/add-spot.js?v=1788205756"></script>
<script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -84,7 +84,7 @@
</div>
<script src="/static/js/alerts.js?v=1788197899"></script>
<script src="/static/js/alerts.js?v=1788205756"></script>
<script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1788197899"></script>
<script src="/static/js/bands.js?v=1788197899"></script>
<script src="/static/js/spotsbandsandmap.js?v=1788205756"></script>
<script src="/static/js/bands.js?v=1788205756"></script>
<script>$(document).ready(function () {
$("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1788197899" type="text/css">
<link rel="stylesheet" href="/static/css/style.css?v=1788205756" type="text/css">
<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/solid-6.7.2.min.css" rel="stylesheet">
@@ -15,10 +15,10 @@
window.fetchEventSource = fetchEventSource;
</script>
<script src="/static/js/utils.js?v=1788197899"></script>
<script src="/static/js/ui-ham.js?v=1788197899"></script>
<script src="/static/js/geo.js?v=1788197899"></script>
<script src="/static/js/common.js?v=1788197899"></script>
<script src="/static/js/utils.js?v=1788205756"></script>
<script src="/static/js/ui-ham.js?v=1788205756"></script>
<script src="/static/js/geo.js?v=1788205756"></script>
<script src="/static/js/common.js?v=1788205756"></script>
{% end %}
{% block body %}
<div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1788197899"></script>
<script src="/static/js/conditions.js?v=1788205756"></script>
<script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active");
}); <!-- 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', '') }}";
</script>
<script src="/static/js/spotsbandsandmap.js?v=1788197899"></script>
<script src="/static/js/map.js?v=1788197899"></script>
<script src="/static/js/spotsbandsandmap.js?v=1788205755"></script>
<script src="/static/js/map.js?v=1788205755"></script>
<script>$(document).ready(function () {
$("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1788197899"></script>
<script src="/static/js/spots.js?v=1788197899"></script>
<script src="/static/js/spotsbandsandmap.js?v=1788205755"></script>
<script src="/static/js/spots.js?v=1788205755"></script>
<script>$(document).ready(function () {
$("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -86,7 +86,7 @@
</div>
</div>
<script src="/static/js/status.js?v=1788197899"></script>
<script src="/static/js/status.js?v=1788205756"></script>
<script>
$(document).ready(function () {
$("#nav-link-status").addClass("active");