mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
Start adding filters #7
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# (S)pothole
|
||||
# 
|
||||
|
||||
**Work in progress.**
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ from data.band import Band
|
||||
SOFTWARE_NAME = "(S)pothole by M0TRT"
|
||||
SOFTWARE_VERSION = "0.1"
|
||||
|
||||
# Sources
|
||||
SOURCES = ["POTA", "SOTA", "WWFF", "GMA", "WWBOTA", "HEMA", "ParksNPeaks", "Cluster", "RBN", "APRS-IS"]
|
||||
|
||||
# Special Interest Groups
|
||||
SIGS = ["POTA", "SOTA", "WWFF", "GMA", "WWBOTA", "HEMA", "MOTA", "ARLHS", "SiOTA", "WCA"]
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ class Spot:
|
||||
# Last resort for getting a position, use the DXCC entity.
|
||||
if self.dx_call and not self.latitude:
|
||||
latlon = infer_latlon_from_callsign_dxcc(self.dx_call)
|
||||
if latlon:
|
||||
self.latitude = latlon[0]
|
||||
self.longitude = latlon[1]
|
||||
self.grid = infer_grid_from_callsign_dxcc(self.dx_call)
|
||||
|
||||
@@ -8,7 +8,7 @@ import pytz
|
||||
from bottle import run, response, template
|
||||
|
||||
from core.config import MAX_SPOT_AGE
|
||||
from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, SOURCES, CONTINENTS
|
||||
from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, CONTINENTS
|
||||
from core.utils import serialize_everything
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ class WebServer:
|
||||
"modes": ALL_MODES,
|
||||
"mode_types": MODE_TYPES,
|
||||
"sigs": SIGS,
|
||||
"sources": SOURCES,
|
||||
# Sources are filtered for only ones that are enabled in config, no point letting the user toggle things that aren't even available.
|
||||
"sources": list(map(lambda p: p["name"], filter(lambda p: p["enabled"], self.status_data["providers"]))),
|
||||
"continents": CONTINENTS,
|
||||
"max_spot_age": MAX_SPOT_AGE}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<p class="d-inline-flex gap-1">
|
||||
<button id="filters-button" type="button" class="btn btn-primary">Filters</button>
|
||||
<button id="status-button" type="button" class="btn btn-primary">Status</button>
|
||||
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button">Filters</button>
|
||||
<button id="status-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button">Status</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="filters-container" class="row row-cols-1 row-cols-md-2 g-4"></div>
|
||||
<div id="filters-container" class="row row-cols-1 row-cols-md-4 g-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -56,3 +56,7 @@ div.appearing-panel {
|
||||
div.status-card {
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
p.filter-card-text {
|
||||
line-height: 2.5em !important;
|
||||
}
|
||||
@@ -58,6 +58,9 @@ function updateTable() {
|
||||
|
||||
// Format the mode
|
||||
mode_string = s["mode"];
|
||||
if (s["mode"] == null) {
|
||||
mode_string = "???"
|
||||
}
|
||||
if (s["mode_source"] == "BANDPLAN") {
|
||||
mode_string = mode_string + "<span class='mode-q'><i class='fa-solid fa-circle-question' title='The mode was not reported via the spotting service. This is a guess based on the frequency.'></i></span>"
|
||||
}
|
||||
@@ -151,12 +154,19 @@ function loadOptions() {
|
||||
options = jsonData;
|
||||
|
||||
// Separately store colour lookups for bands
|
||||
options["bands"].forEach(m => {
|
||||
band_colors[m["name"]] = m["color"]
|
||||
options["bands"].forEach(b => {
|
||||
band_colors[b["name"]] = b["color"];
|
||||
});
|
||||
|
||||
// Add CSS for band toggle buttons
|
||||
addBandToggleButtonCSS(options["bands"]);
|
||||
|
||||
// Populate the filters panel
|
||||
$("#filters-container").text(JSON.stringify(options));
|
||||
$("#filters-container").append(generateFilterCard("DX Continent", "dx_continent", options["continents"]));
|
||||
$("#filters-container").append(generateFilterCard("DE Continent", "de_continent", options["continents"]));
|
||||
$("#filters-container").append(generateFilterCard("Modes", "mode_type", options["mode_types"]));
|
||||
$("#filters-container").append(generateFilterCard("Sources", "source", options["sources"]));
|
||||
$("#filters-container").append(generateBandsFilterCard("Bands", "band", options["bands"]));
|
||||
|
||||
// Load spots and set up the timer
|
||||
loadSpots();
|
||||
@@ -164,6 +174,48 @@ function loadOptions() {
|
||||
});
|
||||
}
|
||||
|
||||
// Dynamically add CSS code for the band toggle buttons to be in the appropriate colour
|
||||
function addBandToggleButtonCSS(band_options) {
|
||||
var $style = $('<style>');
|
||||
band_options.forEach(o => {
|
||||
$style.append(`#filter-button-label-band-${o['name']} { border-color: ${o['color']}; color: var(--bs-primary);}`);
|
||||
$style.append(`.btn-check:checked + #filter-button-label-band-${o['name']} { background-color: ${o['color']}; color: ${o['contrast_color']};}`);
|
||||
});
|
||||
$('html > head').append($style);
|
||||
}
|
||||
|
||||
// Generate filter card
|
||||
function generateFilterCard(displayName, filterQuery, options) {
|
||||
let $col = $("<div class='col-3'>")
|
||||
let $card = $("<div class='card status-card'>");
|
||||
let $card_body = $("<div class='card-body'>");
|
||||
$card_body.append(`<h5 class='card-title'>${displayName}</h5>`);
|
||||
$p = $("<p class='card-text filter-card-text'>");
|
||||
options.forEach(o => {
|
||||
$p.append(`<input type="checkbox" class="btn-check filter-button-${filterQuery}" name="options" id="filter-button-${filterQuery}-${o}" value="${filterQuery}:${o}" autocomplete="off" checked><label class="btn btn-outline-primary" for="filter-button-${filterQuery}-${o}">${o}</label> `);
|
||||
});
|
||||
$card_body.append($p);
|
||||
$card.append($card_body);
|
||||
$col.append($card);
|
||||
return $col;
|
||||
}
|
||||
|
||||
// Generate bands filter card. This one is a special case.
|
||||
function generateBandsFilterCard(displayName, filterQuery, band_options) {
|
||||
let $col = $("<div class='col-12'>")
|
||||
let $card = $("<div class='card status-card'>");
|
||||
let $card_body = $("<div class='card-body'>");
|
||||
$card_body.append(`<h5 class='card-title'>${displayName}</h5>`);
|
||||
$p = $("<p class='card-text filter-card-text'>");
|
||||
band_options.forEach(o => {
|
||||
$p.append(`<input type="checkbox" class="btn-check filter-button-${filterQuery}" name="options" id="filter-button-${filterQuery}-${o['name']}" value="${filterQuery}:${o['name']}" autocomplete="off" checked><label class="btn btn-outline" id="filter-button-label-${filterQuery}-${o['name']}" for="filter-button-${filterQuery}-${o['name']}">${o['name']}</label> `);
|
||||
});
|
||||
$card_body.append($p);
|
||||
$card.append($card_body);
|
||||
$col.append($card);
|
||||
return $col;
|
||||
}
|
||||
|
||||
// Update the refresh timing display
|
||||
function updateRefreshDisplay() {
|
||||
if (lastUpdateTime != null) {
|
||||
@@ -208,13 +260,27 @@ $(document).ready(function() {
|
||||
|
||||
// Event listeners
|
||||
$("#status-button").click(function() {
|
||||
// If we are going to display status, load the data
|
||||
// If we are going to display status, load the data for the status panel
|
||||
if (!$("#status-area").is(":visible")) {
|
||||
loadStatus();
|
||||
}
|
||||
$("#status-area").toggle();
|
||||
});
|
||||
$("#close-status-button").click(function() { $("#status-area").hide(); });
|
||||
$("#filters-button").click(function() { $("#filters-area").toggle(); });
|
||||
$("#close-filters-button").click(function() { $("#filters-area").hide(); });
|
||||
$("#close-status-button").click(function() {
|
||||
$("#status-button").button("toggle");
|
||||
$("#status-area").hide();
|
||||
});
|
||||
$("#filters-button").click(function() {
|
||||
$("#filters-area").toggle();
|
||||
// If we have just dismissed the filters panel, reload spots
|
||||
if (!$("#filters-area").is(":visible")) {
|
||||
loadSpots()();
|
||||
}
|
||||
});
|
||||
$("#close-filters-button").click(function() {
|
||||
$("#filters-button").button("toggle");
|
||||
$("#filters-area").hide();
|
||||
// We have just dismissed the filters panel, reload spots
|
||||
loadSpots();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user