mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 14:27:42 +00:00
Compare commits
3
Commits
9d117a6069
...
d3d1d20821
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3d1d20821 | ||
|
|
ab86bdc4d6 | ||
|
|
d7202f208c |
+7
@@ -65,6 +65,13 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyStubPackagesAdvertiser" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<list>
|
||||||
|
<option value="pandas" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
<option name="processCode" value="true" />
|
<option name="processCode" value="true" />
|
||||||
<option name="processLiterals" value="true" />
|
<option name="processLiterals" value="true" />
|
||||||
|
|||||||
@@ -249,6 +249,18 @@ sig_ref_data_providers:
|
|||||||
- class: "FEA"
|
- class: "FEA"
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
- class: "DMVE"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- class: "DMUE"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- class: "DCE"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- class: "DEFE"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
- class: "KRMNPA"
|
- class: "KRMNPA"
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
|||||||
+42
-1
@@ -247,13 +247,53 @@ SIGS = [
|
|||||||
region_flag="🇪🇸",
|
region_flag="🇪🇸",
|
||||||
refs_globally_unique=True,
|
refs_globally_unique=True,
|
||||||
),
|
),
|
||||||
|
SIG(
|
||||||
|
name="DMUE",
|
||||||
|
comment_names=["DMUE"],
|
||||||
|
description="Diploma Museos de España",
|
||||||
|
sig_type=SIGType.REGIONAL,
|
||||||
|
ref_regex=r"MUE[A-Z]{2}-\d{3}",
|
||||||
|
icon="fa-landmark",
|
||||||
|
region_flag="🇪🇸",
|
||||||
|
refs_globally_unique=True,
|
||||||
|
),
|
||||||
|
SIG(
|
||||||
|
name="DMVE",
|
||||||
|
comment_names=["DMVE"],
|
||||||
|
description="Diploma Monumentos y Vestigios de España",
|
||||||
|
sig_type=SIGType.REGIONAL,
|
||||||
|
ref_regex=r"MV[A-Z]{1,2}-\d{4}",
|
||||||
|
icon="fa-monument",
|
||||||
|
region_flag="🇪🇸",
|
||||||
|
refs_globally_unique=True,
|
||||||
|
),
|
||||||
|
SIG(
|
||||||
|
name="DCE",
|
||||||
|
comment_names=["DCE"],
|
||||||
|
description="Diploma Castillos de España",
|
||||||
|
sig_type=SIGType.REGIONAL,
|
||||||
|
ref_regex=r"C[A-Z]{1,2}-\d{3}",
|
||||||
|
icon="fa-chess-rook",
|
||||||
|
region_flag="🇪🇸",
|
||||||
|
refs_globally_unique=False,
|
||||||
|
),
|
||||||
|
SIG(
|
||||||
|
name="DEFE",
|
||||||
|
comment_names=["DEFE"],
|
||||||
|
description="Diploma Estaciones de Ferrocarril de España",
|
||||||
|
sig_type=SIGType.REGIONAL,
|
||||||
|
ref_regex=r"EF[A-Z]{1,2}-\d{3}",
|
||||||
|
icon="fa-train",
|
||||||
|
region_flag="🇪🇸",
|
||||||
|
refs_globally_unique=True,
|
||||||
|
),
|
||||||
SIG(
|
SIG(
|
||||||
name="DTMBA",
|
name="DTMBA",
|
||||||
comment_names=["DTMBA"],
|
comment_names=["DTMBA"],
|
||||||
description="Diploma Teatri Musei e Belle Arti",
|
description="Diploma Teatri Musei e Belle Arti",
|
||||||
sig_type=SIGType.REGIONAL,
|
sig_type=SIGType.REGIONAL,
|
||||||
ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}",
|
ref_regex=r"I-?[0-9]{3,4}\s?[A-Z]{2}",
|
||||||
icon="fa-masks-theater",
|
icon="fa-landmark",
|
||||||
region_flag="🇮🇹",
|
region_flag="🇮🇹",
|
||||||
refs_globally_unique=True,
|
refs_globally_unique=True,
|
||||||
),
|
),
|
||||||
@@ -341,5 +381,6 @@ PROPAGATION_MODES = {
|
|||||||
"MS": "Meteor scatter",
|
"MS": "Meteor scatter",
|
||||||
"RS": "Rain scatter",
|
"RS": "Rain scatter",
|
||||||
"AS": "Aircraft scatter",
|
"AS": "Aircraft scatter",
|
||||||
|
"ACS": "Aircraft scatter",
|
||||||
"SAT": "Satellite",
|
"SAT": "Satellite",
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -48,12 +48,15 @@ class Mode(str, Enum):
|
|||||||
def from_name(name):
|
def from_name(name):
|
||||||
"""Convert a string to an enum mode using the alias table."""
|
"""Convert a string to an enum mode using the alias table."""
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
return Mode.UNKNOWN
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return Mode(name.upper())
|
return Mode(name.upper())
|
||||||
except ValueError:
|
except (KeyError, ValueError):
|
||||||
try:
|
try:
|
||||||
return Mode(MODE_ALIASES[name.upper()])
|
return Mode(MODE_ALIASES[name.upper()])
|
||||||
except ValueError:
|
except (KeyError, ValueError):
|
||||||
return Mode.UNKNOWN
|
return Mode.UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -42,6 +42,9 @@ def infer_mode_type_from_mode(mode: str) -> ModeType:
|
|||||||
if not mode:
|
if not mode:
|
||||||
return ModeType.UNKNOWN
|
return ModeType.UNKNOWN
|
||||||
|
|
||||||
|
if mode in MODE_ALIASES:
|
||||||
|
mode = MODE_ALIASES[mode]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mode = Mode(mode.upper())
|
mode = Mode(mode.upper())
|
||||||
if mode.is_cw:
|
if mode.is_cw:
|
||||||
@@ -118,7 +121,7 @@ def get_callsign_object_from_pyhamtools_callinfo(callsign, callinfo):
|
|||||||
|
|
||||||
country = data.get("country", None)
|
country = data.get("country", None)
|
||||||
dxcc_id = data.get("adif", None)
|
dxcc_id = data.get("adif", None)
|
||||||
continent = Continent(data.get("continent", None))
|
continent = Continent(data["continent"]) if "continent" in data else None
|
||||||
cq_zone = data.get("cqz", None)
|
cq_zone = data.get("cqz", None)
|
||||||
itu_zone = data.get("ituz", None)
|
itu_zone = data.get("ituz", None)
|
||||||
lat = float(data["latitude"]) if "latitude" in data else None
|
lat = float(data["latitude"]) if "latitude" in data else None
|
||||||
|
|||||||
+2
-2
@@ -92,8 +92,8 @@ class Alert:
|
|||||||
call_info = get_call_info(self.dx_calls[0], credentials)
|
call_info = get_call_info(self.dx_calls[0], credentials)
|
||||||
if self.dx_calls and self.dx_calls[0] and not self.dx_country:
|
if self.dx_calls and self.dx_calls[0] and not self.dx_country:
|
||||||
self.dx_country = call_info.country
|
self.dx_country = call_info.country
|
||||||
if self.dx_calls and self.dx_calls[0] and not self.dx_continent:
|
if self.dx_calls and self.dx_calls[0] and call_info.continent and not self.dx_continent:
|
||||||
self.dx_continent = Continent(call_info.continent) if call_info.continent else None
|
self.dx_continent = Continent(call_info.continent)
|
||||||
if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone:
|
if self.dx_calls and self.dx_calls[0] and not self.dx_cq_zone:
|
||||||
self.dx_cq_zone = call_info.cq_zone
|
self.dx_cq_zone = call_info.cq_zone
|
||||||
if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone:
|
if self.dx_calls and self.dx_calls[0] and not self.dx_itu_zone:
|
||||||
|
|||||||
+2
-2
@@ -184,7 +184,7 @@ class Spot:
|
|||||||
dx_call_info = get_call_info(self.dx_call, credentials)
|
dx_call_info = get_call_info(self.dx_call, credentials)
|
||||||
if self.dx_call and not self.dx_country:
|
if self.dx_call and not self.dx_country:
|
||||||
self.dx_country = dx_call_info.country
|
self.dx_country = dx_call_info.country
|
||||||
if self.dx_call and not self.dx_continent:
|
if self.dx_call and dx_call_info.continent and not self.dx_continent:
|
||||||
self.dx_continent = Continent(dx_call_info.continent)
|
self.dx_continent = Continent(dx_call_info.continent)
|
||||||
if self.dx_call and not self.dx_dxcc_id:
|
if self.dx_call and not self.dx_dxcc_id:
|
||||||
self.dx_dxcc_id = dx_call_info.dxcc_id
|
self.dx_dxcc_id = dx_call_info.dxcc_id
|
||||||
@@ -222,7 +222,7 @@ class Spot:
|
|||||||
):
|
):
|
||||||
if not self.de_country:
|
if not self.de_country:
|
||||||
self.de_country = de_call_info.country
|
self.de_country = de_call_info.country
|
||||||
if not self.de_continent:
|
if de_call_info.continent and not self.de_continent:
|
||||||
self.de_continent = Continent(de_call_info.continent)
|
self.de_continent = Continent(de_call_info.continent)
|
||||||
if not self.de_dxcc_id:
|
if not self.de_dxcc_id:
|
||||||
self.de_dxcc_id = de_call_info.dxcc_id
|
self.de_dxcc_id = de_call_info.dxcc_id
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class ParksNPeaks(HTTPAlertProvider):
|
|||||||
"POTA",
|
"POTA",
|
||||||
"SOTA",
|
"SOTA",
|
||||||
"WWFF",
|
"WWFF",
|
||||||
|
"HEMA",
|
||||||
"SIOTA",
|
"SIOTA",
|
||||||
"ZLOTA",
|
"ZLOTA",
|
||||||
"KRMNPA",
|
"KRMNPA",
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class HamQTH(APIQueryCallsignDataProvider):
|
|||||||
name=data.get("nick", None),
|
name=data.get("nick", None),
|
||||||
qth=data.get("qth", None),
|
qth=data.get("qth", None),
|
||||||
country=data.get("country", None),
|
country=data.get("country", None),
|
||||||
continent=Continent(data.get("continent", None)),
|
continent=Continent(data["continent"]) if "continent" in data else None,
|
||||||
latitude=lat,
|
latitude=lat,
|
||||||
longitude=lon,
|
longitude=lon,
|
||||||
grid=grid,
|
grid=grid,
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class QRZ(APIQueryCallsignDataProvider):
|
|||||||
name=name,
|
name=name,
|
||||||
qth=data.get("addr2", None),
|
qth=data.get("addr2", None),
|
||||||
country=data.get("country", None),
|
country=data.get("country", None),
|
||||||
continent=Continent(data.get("continent", None)),
|
continent=Continent(data["continent"]) if "continent" in data else None,
|
||||||
latitude=lat,
|
latitude=lat,
|
||||||
longitude=lon,
|
longitude=lon,
|
||||||
grid=grid,
|
grid=grid,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import io
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from core.enums import SIGRefType
|
||||||
|
from data.sig_ref import SIGRef
|
||||||
|
from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider
|
||||||
|
|
||||||
|
|
||||||
|
class DCE(FileDownloadSIGRefDataProvider):
|
||||||
|
"""SIG ref data provider for Diploma Castillos de España"""
|
||||||
|
|
||||||
|
POLL_INTERVAL_DAYS = 365
|
||||||
|
SIG = "DCE"
|
||||||
|
DATA_URL = "https://www.acracb.org/dce/descargas/General/directorio_referencias_dce.xls"
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
file_stream = io.BytesIO(http_response.content)
|
||||||
|
df = pd.read_excel(file_stream, engine="xlrd", header=None)
|
||||||
|
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
if row.iloc[0] and row.iloc[2]:
|
||||||
|
new_data.append(
|
||||||
|
SIGRef(sig=self.SIG, id=row.iloc[0].strip(), name=row.iloc[2].strip(), ref_type=SIGRefType.CASTLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import io
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from core.enums import SIGRefType
|
||||||
|
from data.sig_ref import SIGRef
|
||||||
|
from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider
|
||||||
|
|
||||||
|
|
||||||
|
class DEFE(FileDownloadSIGRefDataProvider):
|
||||||
|
"""SIG ref data provider for Diploma Estationes de Ferrocarril de España"""
|
||||||
|
|
||||||
|
POLL_INTERVAL_DAYS = 365
|
||||||
|
SIG = "DEFE"
|
||||||
|
DATA_URL = "https://www.acracb.org/defe/descargas/General/directorio_referencias_defe.xls"
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
file_stream = io.BytesIO(http_response.content)
|
||||||
|
df = pd.read_excel(file_stream, engine="xlrd", header=None)
|
||||||
|
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
# Skip the header row
|
||||||
|
if str(row.iloc[0]) == "NºDEFE":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if row.iloc[0] and row.iloc[1]:
|
||||||
|
new_data.append(
|
||||||
|
SIGRef(sig=self.SIG, id=row.iloc[0].strip(), name=row.iloc[1].strip(), ref_type=SIGRefType.BUILDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import csv
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from core.enums import SIGRefType
|
||||||
|
from data.sig_ref import SIGRef
|
||||||
|
from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider
|
||||||
|
|
||||||
|
|
||||||
|
class DMUE(FileDownloadSIGRefDataProvider):
|
||||||
|
"""SIG ref data provider for Diploma Museos de España"""
|
||||||
|
|
||||||
|
POLL_INTERVAL_DAYS = 365
|
||||||
|
SIG = "DMUE"
|
||||||
|
DATA_URL = "https://dmue.radiogalena.es/nom_dmue.csv"
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
for row in csv.reader(http_response.content.decode("utf-8-sig").splitlines(), delimiter=";"):
|
||||||
|
if len(row) > 1 and row[0] and row[1]:
|
||||||
|
new_data.append(
|
||||||
|
SIGRef(sig=self.SIG, id=row[0].strip(), name=row[1].strip(), ref_type=SIGRefType.BUILDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import io
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from core.enums import SIGRefType
|
||||||
|
from data.sig_ref import SIGRef
|
||||||
|
from providers.sigrefdata.file_download_sig_ref_data_provider import FileDownloadSIGRefDataProvider
|
||||||
|
|
||||||
|
|
||||||
|
class DMVE(FileDownloadSIGRefDataProvider):
|
||||||
|
"""SIG ref data provider for Diploma Monumentos y Vestigios de España"""
|
||||||
|
|
||||||
|
POLL_INTERVAL_DAYS = 365
|
||||||
|
SIG = "DMVE"
|
||||||
|
DATA_URL = "https://www.acracb.org/dmve/descargas/General/directorio_referencias_dmve.xls"
|
||||||
|
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
file_stream = io.BytesIO(http_response.content)
|
||||||
|
# Despide the .xls extension this is actually an xlsx file, so we need openpyxl not xlrd
|
||||||
|
df = pd.read_excel(file_stream, engine="openpyxl", header=None)
|
||||||
|
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
ref = row.iloc[0]
|
||||||
|
name = row.iloc[1]
|
||||||
|
|
||||||
|
# Skip the header row and blank rows
|
||||||
|
if str(ref) == "REF.":
|
||||||
|
continue
|
||||||
|
if pd.isna(ref) or pd.isna(name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ref and name:
|
||||||
|
new_data.append(SIGRef(sig=self.SIG, id=ref.strip(), name=name.strip(), ref_type=SIGRefType.BUILDING))
|
||||||
|
|
||||||
|
# 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
|
||||||
@@ -16,9 +16,12 @@ beautifulsoup4~=4.14.2
|
|||||||
websocket-client~=1.8.0
|
websocket-client~=1.8.0
|
||||||
tornado~=6.4.2
|
tornado~=6.4.2
|
||||||
tornado_eventsource~=3.0.0
|
tornado_eventsource~=3.0.0
|
||||||
|
pandas~=3.0.0
|
||||||
geopandas~=0.13.2
|
geopandas~=0.13.2
|
||||||
simplejson~=4.1.1
|
simplejson~=4.1.1
|
||||||
cachetools~=7.1.6
|
cachetools~=7.1.6
|
||||||
fastkml~=1.4.0
|
fastkml~=1.4.0
|
||||||
ruff~=0.16.3
|
ruff~=0.16.3
|
||||||
pdfplumber~=0.11.10
|
pdfplumber~=0.11.10
|
||||||
|
xlrd~=2.0.2
|
||||||
|
openpyxl~=3.1.5
|
||||||
@@ -102,9 +102,11 @@
|
|||||||
on the Air (SIOTA), World Castles Award (WCA), New Zealand on the Air (ZLOTA), Keith Roget Memorial National
|
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
|
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
|
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), el Diploma
|
the Air, Worked All Britain (WAB), Worked All Ireland (WAI), Diploma Municipios de España (DME), Diploma
|
||||||
Faros de España (FEA), il Diploma Teatri Musei e Belle Arti (DTMBA), British Inland Waterways on the Air
|
Faros de España (FEA), Diploma Muesos de España (DMUE), Diploma Castillos de España (DCE), Diploma Monumentos y
|
||||||
(BIWOTA), Castles on the Air (COTA), Polish Gmina Award (PGA), and Toilets on the Air.</p>
|
Vestigios de España (DMVE), Diploma Estaciones de Ferrocarril de España (DEFE), Diploma Teatri Musei e Belle
|
||||||
|
Arti (DTMBA), British Inland Waterways on the Air (BIWOTA), Castles on the Air (COTA), Polish Gmina Award (PGA),
|
||||||
|
and Toilets on the Air.</p>
|
||||||
<p>As of the time of writing in August 2026, I think Spothole captures most outdoor radio programmes that have a
|
<p>As of the time of writing in August 2026, I think Spothole captures most outdoor radio programmes that have a
|
||||||
defined, downloadable reference list, and almost certainly those that have a spotting/alerting API. If you know
|
defined, downloadable reference list, and almost certainly those that have a spotting/alerting API. If you know
|
||||||
of one I've missed, please let me know!</p>
|
of one I've missed, please let me know!</p>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/add-spot.js?v=1788338285"></script>
|
<script src="/static/js/add-spot.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-add-spot").addClass("active");
|
$("#nav-link-add-spot").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/alerts.js?v=1788338286"></script>
|
<script src="/static/js/alerts.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-alerts").addClass("active");
|
$("#nav-link-alerts").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -76,8 +76,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1788338285"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1788546948"></script>
|
||||||
<script src="/static/js/bands.js?v=1788338285"></script>
|
<script src="/static/js/bands.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-bands").addClass("active");
|
$("#nav-link-bands").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
{% extends "skeleton.html" %}
|
{% extends "skeleton.html" %}
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/css/style.css?v=1788338285" type="text/css">
|
<link rel="stylesheet" href="/static/css/style.css?v=1788546948" type="text/css">
|
||||||
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
|
<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/fontawesome-6.7.2.min.css" rel="stylesheet">
|
||||||
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
|
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
window.fetchEventSource = fetchEventSource;
|
window.fetchEventSource = fetchEventSource;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/static/js/utils.js?v=1788338285"></script>
|
<script src="/static/js/utils.js?v=1788546948"></script>
|
||||||
<script src="/static/js/ui-ham.js?v=1788338285"></script>
|
<script src="/static/js/ui-ham.js?v=1788546948"></script>
|
||||||
<script src="/static/js/geo.js?v=1788338285"></script>
|
<script src="/static/js/geo.js?v=1788546948"></script>
|
||||||
<script src="/static/js/common.js?v=1788338285"></script>
|
<script src="/static/js/common.js?v=1788546948"></script>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
@@ -284,7 +284,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
|
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
|
||||||
<script src="/static/js/conditions.js?v=1788338285"></script>
|
<script src="/static/js/conditions.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-conditions").addClass("active");
|
$("#nav-link-conditions").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
"src": "/static/img/icon-192-pwa.png",
|
"src": "/static/img/icon-192-pwa.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/img/icon-512-pwa.png",
|
"src": "/static/img/icon-512-pwa.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable any"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"url": "{{ baseurl }}"
|
"url": "{{ baseurl }}"
|
||||||
|
|||||||
+2
-2
@@ -113,8 +113,8 @@
|
|||||||
const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}";
|
const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1788338286"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1788546948"></script>
|
||||||
<script src="/static/js/map.js?v=1788338286"></script>
|
<script src="/static/js/map.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-map").addClass("active");
|
$("#nav-link-map").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -113,8 +113,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/spotsbandsandmap.js?v=1788338285"></script>
|
<script src="/static/js/spotsbandsandmap.js?v=1788546948"></script>
|
||||||
<script src="/static/js/spots.js?v=1788338285"></script>
|
<script src="/static/js/spots.js?v=1788546948"></script>
|
||||||
<script>$(document).ready(function () {
|
<script>$(document).ready(function () {
|
||||||
$("#nav-link-spots").addClass("active");
|
$("#nav-link-spots").addClass("active");
|
||||||
}); <!-- highlight active page in nav --></script>
|
}); <!-- highlight active page in nav --></script>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/status.js?v=1788338285"></script>
|
<script src="/static/js/status.js?v=1788546948"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$("#nav-link-status").addClass("active");
|
$("#nav-link-status").addClass("active");
|
||||||
|
|||||||
Reference in New Issue
Block a user