mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-03-15 20:34:31 +00:00
Compare commits
5 Commits
1.1.1
...
65957b4c01
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65957b4c01 | ||
|
|
522f90af97 | ||
|
|
4d344021c7 | ||
|
|
abdf8d3065 | ||
|
|
67b9c3bc50 |
@@ -20,27 +20,29 @@ class BOTA(HTTPAlertProvider):
|
|||||||
new_alerts = []
|
new_alerts = []
|
||||||
# Find the table of upcoming alerts
|
# Find the table of upcoming alerts
|
||||||
bs = BeautifulSoup(http_response.content.decode(), features="lxml")
|
bs = BeautifulSoup(http_response.content.decode(), features="lxml")
|
||||||
tbody = bs.body.find('div', attrs={'class': 'view-activations-public'}).find('table', attrs={'class': 'views-table'}).find('tbody')
|
forthcoming_activations_div = bs.body.find('div', attrs={'class': 'view-activations-public'})
|
||||||
for row in tbody.find_all('tr'):
|
if forthcoming_activations_div:
|
||||||
cells = row.find_all('td')
|
tbody = forthcoming_activations_div.find('table', attrs={'class': 'views-table'}).find('tbody')
|
||||||
first_cell_text = str(cells[0].find('a').contents[0]).strip()
|
for row in tbody.find_all('tr'):
|
||||||
ref_name = first_cell_text.split(" by ")[0]
|
cells = row.find_all('td')
|
||||||
dx_call = str(cells[1].find('a').contents[0]).strip().upper()
|
first_cell_text = str(cells[0].find('a').contents[0]).strip()
|
||||||
|
ref_name = first_cell_text.split(" by ")[0]
|
||||||
|
dx_call = str(cells[1].find('a').contents[0]).strip().upper()
|
||||||
|
|
||||||
# Get the date, dealing with the fact we get no year so have to figure out if it's last year or next year
|
# Get the date, dealing with the fact we get no year so have to figure out if it's last year or next year
|
||||||
date_text = str(cells[2].find('span').contents[0]).strip()
|
date_text = str(cells[2].find('span').contents[0]).strip()
|
||||||
date_time = datetime.strptime(date_text,"%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
|
date_time = datetime.strptime(date_text,"%d %b - %H:%M UTC").replace(tzinfo=pytz.UTC)
|
||||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year)
|
date_time = date_time.replace(year=datetime.now(pytz.UTC).year)
|
||||||
# If this was more than a day ago, activation is actually next year
|
# If this was more than a day ago, activation is actually next year
|
||||||
if date_time < datetime.now(pytz.UTC) - timedelta(days=1):
|
if date_time < datetime.now(pytz.UTC) - timedelta(days=1):
|
||||||
date_time = date_time.replace(year=datetime.now(pytz.UTC).year + 1)
|
date_time = date_time.replace(year=datetime.now(pytz.UTC).year + 1)
|
||||||
|
|
||||||
# Convert to our alert format
|
# Convert to our alert format
|
||||||
alert = Alert(source=self.name,
|
alert = Alert(source=self.name,
|
||||||
dx_calls=[dx_call],
|
dx_calls=[dx_call],
|
||||||
sig_refs=[SIGRef(id=ref_name, sig="BOTA")],
|
sig_refs=[SIGRef(id=ref_name, sig="BOTA")],
|
||||||
start_time=date_time.timestamp(),
|
start_time=date_time.timestamp(),
|
||||||
is_dxpedition=False)
|
is_dxpedition=False)
|
||||||
|
|
||||||
new_alerts.append(alert)
|
new_alerts.append(alert)
|
||||||
return new_alerts
|
return new_alerts
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from data.sig import SIG
|
|||||||
|
|
||||||
# General software
|
# General software
|
||||||
SOFTWARE_NAME = "Spothole by M0TRT"
|
SOFTWARE_NAME = "Spothole by M0TRT"
|
||||||
SOFTWARE_VERSION = "1.1.1"
|
SOFTWARE_VERSION = "1.2-pre"
|
||||||
|
|
||||||
# HTTP headers used for spot providers that use HTTP
|
# HTTP headers used for spot providers that use HTTP
|
||||||
HTTP_HEADERS = {"User-Agent": SOFTWARE_NAME + ", v" + SOFTWARE_VERSION + " (operated by " + SERVER_OWNER_CALLSIGN + ")"}
|
HTTP_HEADERS = {"User-Agent": SOFTWARE_NAME + ", v" + SOFTWARE_VERSION + " (operated by " + SERVER_OWNER_CALLSIGN + ")"}
|
||||||
|
|||||||
@@ -416,7 +416,12 @@ class LookupHelper:
|
|||||||
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
|
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
|
||||||
def infer_grid_from_callsign_dxcc(self, call):
|
def infer_grid_from_callsign_dxcc(self, call):
|
||||||
latlon = self.infer_latlon_from_callsign_dxcc(call)
|
latlon = self.infer_latlon_from_callsign_dxcc(call)
|
||||||
return latlong_to_locator(latlon[0], latlon[1], 8)
|
grid = None
|
||||||
|
try:
|
||||||
|
grid = latlong_to_locator(latlon[0], latlon[1], 8)
|
||||||
|
except:
|
||||||
|
logging.debug("Invalid lat/lon received for DXCC")
|
||||||
|
return grid
|
||||||
|
|
||||||
# Infer a mode from the frequency (in Hz) according to the band plan. Just a guess really.
|
# Infer a mode from the frequency (in Hz) according to the band plan. Just a guess really.
|
||||||
def infer_mode_from_frequency(self, freq):
|
def infer_mode_from_frequency(self, freq):
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ def populate_sig_ref_info(sig_ref):
|
|||||||
if row["reference"] == ref_id:
|
if row["reference"] == ref_id:
|
||||||
sig_ref.name = row["name"] if "name" in row else None
|
sig_ref.name = row["name"] if "name" in row else None
|
||||||
sig_ref.url = "https://wwff.co/directory/?showRef=" + ref_id
|
sig_ref.url = "https://wwff.co/directory/?showRef=" + ref_id
|
||||||
sig_ref.grid = row["iaruLocator"] if "iaruLocator" in row else None
|
sig_ref.grid = row["iaruLocator"] if "iaruLocator" in row and row["iaruLocator"] != "-" else None
|
||||||
sig_ref.latitude = float(row["latitude"]) if "latitude" in row else None
|
sig_ref.latitude = float(row["latitude"]) if "latitude" in row and row["latitude"] != "-" else None
|
||||||
sig_ref.longitude = float(row["longitude"]) if "longitude" in row else None
|
sig_ref.longitude = float(row["longitude"]) if "longitude" in row and row["longitude"] != "-" else None
|
||||||
break
|
break
|
||||||
elif sig.upper() == "SIOTA":
|
elif sig.upper() == "SIOTA":
|
||||||
siota_csv_data = SEMI_STATIC_URL_DATA_CACHE.get("https://www.silosontheair.com/data/silos.csv",
|
siota_csv_data = SEMI_STATIC_URL_DATA_CACHE.get("https://www.silosontheair.com/data/silos.csv",
|
||||||
@@ -112,9 +112,12 @@ def populate_sig_ref_info(sig_ref):
|
|||||||
if asset["code"] == ref_id:
|
if asset["code"] == ref_id:
|
||||||
sig_ref.name = asset["name"]
|
sig_ref.name = asset["name"]
|
||||||
sig_ref.url = "https://ontheair.nz/assets/ZLI_OT-030" + ref_id.replace("/", "_")
|
sig_ref.url = "https://ontheair.nz/assets/ZLI_OT-030" + ref_id.replace("/", "_")
|
||||||
sig_ref.grid = latlong_to_locator(asset["y"], asset["x"], 6)
|
try:
|
||||||
sig_ref.latitude = asset["y"]
|
sig_ref.grid = latlong_to_locator(asset["y"], asset["x"], 6)
|
||||||
sig_ref.longitude = asset["x"]
|
except:
|
||||||
|
logging.debug("Invalid lat/lon received for reference")
|
||||||
|
sig_ref.latitude = asset["y"]
|
||||||
|
sig_ref.longitude = asset["x"]
|
||||||
break
|
break
|
||||||
elif sig.upper() == "BOTA":
|
elif sig.upper() == "BOTA":
|
||||||
if not sig_ref.name:
|
if not sig_ref.name:
|
||||||
@@ -124,9 +127,12 @@ def populate_sig_ref_info(sig_ref):
|
|||||||
ll = wab_wai_square_to_lat_lon(ref_id)
|
ll = wab_wai_square_to_lat_lon(ref_id)
|
||||||
if ll:
|
if ll:
|
||||||
sig_ref.name = ref_id
|
sig_ref.name = ref_id
|
||||||
sig_ref.grid = latlong_to_locator(ll[0], ll[1], 6)
|
try:
|
||||||
sig_ref.latitude = ll[0]
|
sig_ref.grid = latlong_to_locator(ll[0], ll[1], 6)
|
||||||
sig_ref.longitude = ll[1]
|
sig_ref.latitude = ll[0]
|
||||||
|
sig_ref.longitude = ll[1]
|
||||||
|
except:
|
||||||
|
logging.debug("Invalid lat/lon received for reference")
|
||||||
except:
|
except:
|
||||||
logging.warn("Failed to look up sig_ref info for " + sig + " ref " + ref_id + ".")
|
logging.warn("Failed to look up sig_ref info for " + sig + " ref " + ref_id + ".")
|
||||||
return sig_ref
|
return sig_ref
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ class StatusReporter:
|
|||||||
self.status_data["spot_providers"] = list(
|
self.status_data["spot_providers"] = list(
|
||||||
map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status,
|
map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status,
|
||||||
"last_updated": p.last_update_time.replace(
|
"last_updated": p.last_update_time.replace(
|
||||||
tzinfo=pytz.UTC).timestamp() if p.last_update_time else 0,
|
tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0,
|
||||||
"last_spot": p.last_spot_time.replace(
|
"last_spot": p.last_spot_time.replace(
|
||||||
tzinfo=pytz.UTC).timestamp() if p.last_spot_time else 0}, self.spot_providers))
|
tzinfo=pytz.UTC).timestamp() if p.last_spot_time.year > 2000 else 0}, self.spot_providers))
|
||||||
self.status_data["alert_providers"] = list(
|
self.status_data["alert_providers"] = list(
|
||||||
map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status,
|
map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status,
|
||||||
"last_updated": p.last_update_time.replace(
|
"last_updated": p.last_update_time.replace(
|
||||||
tzinfo=pytz.UTC).timestamp() if p.last_update_time else 0},
|
tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0},
|
||||||
self.alert_providers))
|
self.alert_providers))
|
||||||
self.status_data["cleanup"] = {"status": self.cleanup_timer.status,
|
self.status_data["cleanup"] = {"status": self.cleanup_timer.status,
|
||||||
"last_ran": self.cleanup_timer.last_cleanup_time.replace(
|
"last_ran": self.cleanup_timer.last_cleanup_time.replace(
|
||||||
|
|||||||
10
data/spot.py
10
data/spot.py
@@ -284,9 +284,13 @@ class Spot:
|
|||||||
|
|
||||||
# DX Grid to lat/lon and vice versa in case one is missing
|
# DX Grid to lat/lon and vice versa in case one is missing
|
||||||
if self.dx_grid and not self.dx_latitude:
|
if self.dx_grid and not self.dx_latitude:
|
||||||
ll = locator_to_latlong(self.dx_grid)
|
try:
|
||||||
self.dx_latitude = ll[0]
|
print(json.dumps(self))
|
||||||
self.dx_longitude = ll[1]
|
ll = locator_to_latlong(self.dx_grid)
|
||||||
|
self.dx_latitude = ll[0]
|
||||||
|
self.dx_longitude = ll[1]
|
||||||
|
except:
|
||||||
|
logging.debug("Invalid grid received for spot")
|
||||||
if self.dx_latitude and self.dx_longitude and not self.dx_grid:
|
if self.dx_latitude and self.dx_longitude and not self.dx_grid:
|
||||||
try:
|
try:
|
||||||
self.dx_grid = latlong_to_locator(self.dx_latitude, self.dx_longitude, 8)
|
self.dx_grid = latlong_to_locator(self.dx_latitude, self.dx_longitude, 8)
|
||||||
|
|||||||
@@ -17,17 +17,17 @@
|
|||||||
<p class="d-inline-flex gap-1">
|
<p class="d-inline-flex gap-1">
|
||||||
<span class="btn-group" role="group">
|
<span class="btn-group" role="group">
|
||||||
<input type="radio" class="btn-check" name="runPause" id="runButton" autocomplete="off" checked>
|
<input type="radio" class="btn-check" name="runPause" id="runButton" autocomplete="off" checked>
|
||||||
<label class="btn btn-outline-primary" for="runButton"><i class="fa-solid fa-play"></i> Run</label>
|
<label class="btn btn-outline-primary" for="runButton"><i class="fa-solid fa-play"></i><span class="hideonmobile"> Run</span></label>
|
||||||
|
|
||||||
<input type="radio" class="btn-check" name="runPause" id="pauseButton" autocomplete="off">
|
<input type="radio" class="btn-check" name="runPause" id="pauseButton" autocomplete="off">
|
||||||
<label class="btn btn-outline-primary" for="pauseButton"><i class="fa-solid fa-pause"></i> Pause</label>
|
<label class="btn btn-outline-primary" for="pauseButton"><i class="fa-solid fa-pause"></i><span class="hideonmobile"> Pause</span></label>
|
||||||
</span>
|
</span>
|
||||||
<span class="hideonmobile" style="position: relative;">
|
<span style="position: relative;">
|
||||||
<i id="searchicon" class="fa-solid fa-magnifying-glass"></i>
|
<i id="searchicon" class="fa-solid fa-magnifying-glass"></i>
|
||||||
<input id="search" type="search" class="form-control" oninput="filtersUpdated();" placeholder="Search">
|
<input id="search" type="search" class="form-control" oninput="filtersUpdated();" placeholder="Search">
|
||||||
</span>
|
</span>
|
||||||
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i> Filters</button>
|
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i><span class="hideonmobile"> Filters</span></button>
|
||||||
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i> Display</button>
|
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i><span class="hideonmobile"> Display</span></button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
$schema: "https://spec.openapis.org/oas/3.1.0"
|
||||||
openapi: 3.1.0
|
openapi: 3.1.0
|
||||||
info:
|
info:
|
||||||
title: Spothole API
|
title: Spothole API
|
||||||
@@ -864,7 +865,6 @@ components:
|
|||||||
- DSTAR
|
- DSTAR
|
||||||
- C4FM
|
- C4FM
|
||||||
- M17
|
- M17
|
||||||
- DIGI
|
|
||||||
- DATA
|
- DATA
|
||||||
- FT8
|
- FT8
|
||||||
- FT4
|
- FT4
|
||||||
@@ -1234,11 +1234,11 @@ components:
|
|||||||
example: OK
|
example: OK
|
||||||
last_updated:
|
last_updated:
|
||||||
type: number
|
type: number
|
||||||
description: The last time at which this provider received data, UTC seconds since UNIX epoch.
|
description: The last time at which this provider received data, UTC seconds since UNIX epoch. If this is zero, the spot provider has never updated.
|
||||||
example: 1759579508
|
example: 1759579508
|
||||||
last_spot:
|
last_spot:
|
||||||
type: number
|
type: number
|
||||||
description: The time of the latest spot received by this provider, UTC seconds since UNIX epoch.
|
description: The time of the latest spot received by this provider, UTC seconds since UNIX epoch. If this is zero, the spot provider has never received a spot that was accepted by the system.
|
||||||
example: 1759579508
|
example: 1759579508
|
||||||
|
|
||||||
AlertProviderStatus:
|
AlertProviderStatus:
|
||||||
@@ -1257,7 +1257,7 @@ components:
|
|||||||
example: OK
|
example: OK
|
||||||
last_updated:
|
last_updated:
|
||||||
type: number
|
type: number
|
||||||
description: The last time at which this provider received data, UTC seconds since UNIX epoch.
|
description: The last time at which this provider received data, UTC seconds since UNIX epoch. If this is zero, the alert provider has never updated.
|
||||||
example: 1759579508
|
example: 1759579508
|
||||||
|
|
||||||
Band:
|
Band:
|
||||||
|
|||||||
@@ -349,6 +349,9 @@ div.band-spot:hover span.band-spot-info {
|
|||||||
max-height: 26em;
|
max-height: 26em;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
input#search {
|
||||||
|
max-width: 7em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function loadSpots() {
|
|||||||
// Build a query string for the API, based on the filters that the user has selected.
|
// Build a query string for the API, based on the filters that the user has selected.
|
||||||
function buildQueryString() {
|
function buildQueryString() {
|
||||||
var str = "?";
|
var str = "?";
|
||||||
["dx_continent", "de_continent", "mode_type", "source", "band", "sig"].forEach(fn => {
|
["dx_continent", "de_continent", "mode", "source", "band", "sig"].forEach(fn => {
|
||||||
if (!allFilterOptionsSelected(fn)) {
|
if (!allFilterOptionsSelected(fn)) {
|
||||||
str = str + getQueryStringFor(fn) + "&";
|
str = str + getQueryStringFor(fn) + "&";
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ function loadOptions() {
|
|||||||
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
||||||
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateModesMultiToggleFilterCard(options["modes"]);
|
||||||
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
||||||
|
|
||||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function loadURLParams() {
|
|||||||
updateFilterFromParam(params, "band", "band");
|
updateFilterFromParam(params, "band", "band");
|
||||||
updateFilterFromParam(params, "sig", "sig");
|
updateFilterFromParam(params, "sig", "sig");
|
||||||
updateFilterFromParam(params, "source", "source");
|
updateFilterFromParam(params, "source", "source");
|
||||||
updateFilterFromParam(params, "mode_type", "mode_type");
|
updateFilterFromParam(params, "mode", "mode");
|
||||||
updateFilterFromParam(params, "dx_continent", "dx_continent");
|
updateFilterFromParam(params, "dx_continent", "dx_continent");
|
||||||
updateFilterFromParam(params, "de_continent", "de_continent");
|
updateFilterFromParam(params, "de_continent", "de_continent");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function loadSpots() {
|
|||||||
// Build a query string for the API, based on the filters that the user has selected.
|
// Build a query string for the API, based on the filters that the user has selected.
|
||||||
function buildQueryString() {
|
function buildQueryString() {
|
||||||
var str = "?";
|
var str = "?";
|
||||||
["dx_continent", "de_continent", "mode_type", "source", "band", "sig"].forEach(fn => {
|
["dx_continent", "de_continent", "mode", "source", "band", "sig"].forEach(fn => {
|
||||||
if (!allFilterOptionsSelected(fn)) {
|
if (!allFilterOptionsSelected(fn)) {
|
||||||
str = str + getQueryStringFor(fn) + "&";
|
str = str + getQueryStringFor(fn) + "&";
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ function loadOptions() {
|
|||||||
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
||||||
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateModesMultiToggleFilterCard(options["modes"]);
|
||||||
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
||||||
|
|
||||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ function updateTimingDisplayRunPause() {
|
|||||||
// Build a query string for the API, based on the filters that the user has selected.
|
// Build a query string for the API, based on the filters that the user has selected.
|
||||||
function buildQueryString() {
|
function buildQueryString() {
|
||||||
var str = "?";
|
var str = "?";
|
||||||
["dx_continent", "de_continent", "mode_type", "source", "band", "sig"].forEach(fn => {
|
["dx_continent", "de_continent", "mode", "source", "band", "sig"].forEach(fn => {
|
||||||
if (!allFilterOptionsSelected(fn)) {
|
if (!allFilterOptionsSelected(fn)) {
|
||||||
str = str + getQueryStringFor(fn) + "&";
|
str = str + getQueryStringFor(fn) + "&";
|
||||||
}
|
}
|
||||||
@@ -421,7 +421,7 @@ function loadOptions() {
|
|||||||
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
generateSIGsMultiToggleFilterCard(options["sigs"]);
|
||||||
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
||||||
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
generateModesMultiToggleFilterCard(options["modes"]);
|
||||||
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
generateSourcesMultiToggleFilterCard(options["spot_sources"], options["web-ui-options"]["spot-providers-enabled-by-default"]);
|
||||||
|
|
||||||
// Load URL params. These may select things from the various filter & display options, so the function needs
|
// Load URL params. These may select things from the various filter & display options, so the function needs
|
||||||
|
|||||||
@@ -47,6 +47,49 @@ function generateSIGsMultiToggleFilterCard(sig_options) {
|
|||||||
$("#sig-options").append(` <span style="display: inline-block"><button id="filter-button-sig-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', true);">All</button> <button id="filter-button-sig-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', false);">None</button></span>`);
|
$("#sig-options").append(` <span style="display: inline-block"><button id="filter-button-sig-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', true);">All</button> <button id="filter-button-sig-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('sig', false);">None</button></span>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate modes filter card. This one is also a special case.
|
||||||
|
function generateModesMultiToggleFilterCard(mode_options) {
|
||||||
|
// Create a button for each option
|
||||||
|
mode_options.forEach(o => {
|
||||||
|
var domSafeName = o.replace(/^[^A-Za-z0-9]+|[^\w]+/gi, "");
|
||||||
|
$("#mode-options").append(`<input type="checkbox" class="btn-check filter-button-mode storeable-checkbox" name="options" id="filter-button-mode-${domSafeName}" value="${o}" autocomplete="off" onClick="filtersUpdated()" checked><label class="btn btn-outline-primary" id="filter-button-label-mode-${domSafeName}" for="filter-button-mode-${domSafeName}">${o}</label> `);
|
||||||
|
});
|
||||||
|
// Create All/None buttons
|
||||||
|
$("#mode-options").append(` <span style="display: inline-block"><button id="filter-button-mode-all" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('mode', true);">All</button> <button id="filter-button-mode-none" type="button" class="btn btn-outline-secondary" onclick="toggleFilterButtons('mode', false);">None</button></span>`);
|
||||||
|
// Create category buttons
|
||||||
|
$("#mode-options").append(` <button id="filter-button-mode-av" type="button" class="btn btn-outline-secondary" onclick="toggleAnalogVoiceModeToggles();">Analog Voice</button> <button id="filter-button-mode-dv" type="button" class="btn btn-outline-secondary" onclick="toggleDigitalVoiceModeToggles();">Digital Voice</button> <button id="filter-button-mode-digi" type="button" class="btn btn-outline-secondary" onclick="toggleDigiModeToggles();">Digimodes</button></span>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the mode toggles that relate to Analog Voice.
|
||||||
|
function toggleAnalogVoiceModeToggles() {
|
||||||
|
toggleToggles("mode", ["PHONE", "SSB", "LSB", "USB", "AM", "FM"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the mode toggles that relate to Digital Voice.
|
||||||
|
function toggleDigitalVoiceModeToggles() {
|
||||||
|
toggleToggles("mode", ["DV", "DMR", "DSTAR", "C4FM", "M17"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the mode toggles that relate to Digimodes.
|
||||||
|
function toggleDigiModeToggles() {
|
||||||
|
toggleToggles("mode", ["DATA", "FT8", "FT4", "RTTY", "SSTV", "JS8", "HELL", "PSK", "OLIVIA", "PKT", "MSK144"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the a set of toggles of the given type (e.g. "mode") that match the given values (e.g. ["SSB", "AM", "FM"]).
|
||||||
|
function toggleToggles(type, values) {
|
||||||
|
let toggle = null;
|
||||||
|
$(".filter-button-" + type).each(function() {
|
||||||
|
console.log($(this));
|
||||||
|
if (values.includes($(this).val().replace("filter-button-" + type, ""))) {
|
||||||
|
if (toggle == null) {
|
||||||
|
toggle = !$(this).prop('checked');
|
||||||
|
}
|
||||||
|
$(this).prop('checked', toggle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
filtersUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
// Generate Sources filter card. This one is a minor special case as we create the buttons in the normal way, but then
|
// Generate Sources filter card. This one is a minor special case as we create the buttons in the normal way, but then
|
||||||
// set which ones are enabled by default based on config rather than having them all enabled by default. We also sanitise
|
// set which ones are enabled by default based on config rather than having them all enabled by default. We also sanitise
|
||||||
// names here for HTML elements.
|
// names here for HTML elements.
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ function loadStatus() {
|
|||||||
jsonData["spot_providers"].forEach(p => {
|
jsonData["spot_providers"].forEach(p => {
|
||||||
$("#status-container").append(generateStatusCard("Spot Provider: " + p["name"], [
|
$("#status-container").append(generateStatusCard("Spot Provider: " + p["name"], [
|
||||||
`Status: ${p["status"]}`,
|
`Status: ${p["status"]}`,
|
||||||
`Last Updated: ${p["enabled"] ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}`,
|
`Last Updated: ${(p["enabled"] && p["last_updated"] > 0) ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}`,
|
||||||
`Latest Spot: ${p["enabled"] ? moment.unix(p["last_spot"]).utc().fromNow() : "N/A"}`
|
`Latest Spot: ${(p["enabled"] && p["last_spot"] > 0) ? moment.unix(p["last_spot"]).utc().fromNow() : "N/A"}`
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
jsonData["alert_providers"].forEach(p => {
|
jsonData["alert_providers"].forEach(p => {
|
||||||
$("#status-container").append(generateStatusCard("Alert Provider: " + p["name"], [
|
$("#status-container").append(generateStatusCard("Alert Provider: " + p["name"], [
|
||||||
`Status: ${p["status"]}`,
|
`Status: ${p["status"]}`,
|
||||||
`Last Updated: ${p["enabled"] ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}`
|
`Last Updated: ${(p["enabled"] && p["last_updated"] > 0) ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}`
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user