mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-12-15 16:43:38 +00:00
Extract "add spot" into its own page
This commit is contained in:
@@ -6,11 +6,10 @@ from threading import Thread
|
||||
import bottle
|
||||
import pytz
|
||||
from bottle import run, request, response, template
|
||||
from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
|
||||
|
||||
from core.config import MAX_SPOT_AGE, ALLOW_SPOTTING
|
||||
from core.constants import BANDS, ALL_MODES, MODE_TYPES, SIGS, CONTINENTS, SOFTWARE_VERSION
|
||||
from core.prometheus_metrics_handler import page_requests_counter, registry, get_metrics, api_requests_counter
|
||||
from core.prometheus_metrics_handler import page_requests_counter, get_metrics, api_requests_counter
|
||||
from data.spot import Spot
|
||||
|
||||
|
||||
@@ -33,6 +32,7 @@ class WebServer:
|
||||
|
||||
# Base template data
|
||||
bottle.BaseTemplate.defaults['software_version'] = SOFTWARE_VERSION
|
||||
bottle.BaseTemplate.defaults['allow_spotting'] = ALLOW_SPOTTING
|
||||
|
||||
# Routes for API calls
|
||||
bottle.get("/api/v1/spots")(lambda: self.serve_spots_api())
|
||||
@@ -45,6 +45,7 @@ class WebServer:
|
||||
bottle.get("/map")(lambda: self.serve_template('webpage_map'))
|
||||
bottle.get("/bands")(lambda: self.serve_template('webpage_bands'))
|
||||
bottle.get("/alerts")(lambda: self.serve_template('webpage_alerts'))
|
||||
bottle.get("/add-spot")(lambda: self.serve_template('webpage_add_spot'))
|
||||
bottle.get("/status")(lambda: self.serve_template('webpage_status'))
|
||||
bottle.get("/about")(lambda: self.serve_template('webpage_about'))
|
||||
bottle.get("/apidocs")(lambda: self.serve_template('webpage_apidocs'))
|
||||
|
||||
52
views/webpage_add_spot.tpl
Normal file
52
views/webpage_add_spot.tpl
Normal file
@@ -0,0 +1,52 @@
|
||||
% rebase('webpage_base.tpl')
|
||||
|
||||
<div class="alert alert-warning fade show mb-0 mt-4" role="alert">
|
||||
Please note that spots added to Spothole are not currently sent "upstream" to DX clusters or xOTA spotting sites.
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<div id="add-spot-area" class="card mb-3">
|
||||
<div class="card-header text-white bg-primary">
|
||||
<div class="row">
|
||||
<div class="col-auto me-auto">
|
||||
Add a Spot
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="row g-2">
|
||||
<div class="col-auto">
|
||||
<label for="dx-call" class="form-label">DX Call</label>
|
||||
<input type="text" class="form-control" id="dx-call" placeholder="N0CALL" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="freq" class="form-label">Frequency (kHz)</label>
|
||||
<input type="text" class="form-control" id="freq" placeholder="14100" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="mode" class="form-label">Mode</label>
|
||||
<input type="text" class="form-control" id="mode" placeholder="SSB" style="max-width: 6em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="comment" class="form-label">Comment</label>
|
||||
<input type="text" class="form-control" id="comment" placeholder="59 TNX QSO 73" style="max-width: 12em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="de-call" class="form-label">Your Call</label>
|
||||
<input type="text" class="form-control storeable-text" id="de-call" placeholder="N0CALL" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" style="margin-top: 2em;" onclick="addSpot();">Spot</button>
|
||||
<span id="result-good"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="result-bad"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/common.js"></script>
|
||||
<script src="/js/add-spot.js"></script>
|
||||
<script>$(document).ready(function() { $("#nav-link-add-spot").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||
@@ -62,6 +62,9 @@
|
||||
<li class="nav-item ms-4"><a href="/map" class="nav-link" id="nav-link-map"><i class="fa-solid fa-map"></i> Map</a></li>
|
||||
<li class="nav-item ms-4"><a href="/bands" class="nav-link" id="nav-link-bands"><i class="fa-solid fa-ruler-vertical"></i> Bands</a></li>
|
||||
<li class="nav-item ms-4"><a href="/alerts" class="nav-link" id="nav-link-alerts"><i class="fa-solid fa-bell"></i> Alerts</a></li>
|
||||
% if allow_spotting:
|
||||
<li class="nav-item ms-4"><a href="/add-spot" class="nav-link" id="nav-link-add-spot"><i class="fa-solid fa-comment"></i> Add Spot</a></li>
|
||||
% end
|
||||
<li class="nav-item ms-4"><a href="/status" class="nav-link" id="nav-link-status"><i class="fa-solid fa-chart-simple"></i> Status</a></li>
|
||||
<li class="nav-item ms-4"><a href="/about" class="nav-link" id="nav-link-about"><i class="fa-solid fa-circle-info"></i> About</a></li>
|
||||
<li class="nav-item ms-4"><a href="/apidocs" class="nav-link" id="nav-link-api"><i class="fa-solid fa-gear"></i> API</a></li>
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<p class="d-inline-flex gap-1">
|
||||
<div class="hideonmobile" style="position: relative; display: inline-block; top: 2px;">
|
||||
<i class="fa-solid fa-magnifying-glass" style="position: absolute; left: 0px; padding: 10px; pointer-events: none;"></i>
|
||||
<span style="position: relative;">
|
||||
<i class="fa-solid fa-magnifying-glass" style="position: absolute; left: 0px; top: 2px; padding: 10px; pointer-events: none;"></i>
|
||||
<input id="filter-dx-call" type="text" class="form-control me-3" oninput="filtersUpdated();" placeholder="Search for call" style="max-width: 10em; padding-left: 2em;">
|
||||
</div>
|
||||
<button id="add-spot-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleAddSpotPanel();"><i class="fa-solid fa-comment"></i> Add Spot</button>
|
||||
</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="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i> Display</button>
|
||||
</p>
|
||||
@@ -188,55 +187,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="add-spot-area" class="appearing-panel card mb-3">
|
||||
<div class="card-header text-white bg-primary">
|
||||
<div class="row">
|
||||
<div class="col-auto me-auto">
|
||||
Add a Spot
|
||||
</div>
|
||||
<div class="col-auto d-inline-flex">
|
||||
<button id="close-add-spot-button" type="button" class="btn-close btn-close-white" aria-label="Close" onclick="closeAddSpotPanel();"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="row g-2">
|
||||
<div class="col-auto">
|
||||
<label for="add-spot-dx-call" class="form-label">DX Call</label>
|
||||
<input type="text" class="form-control" id="add-spot-dx-call" placeholder="N0CALL" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="add-spot-freq" class="form-label">Frequency (kHz)</label>
|
||||
<input type="text" class="form-control" id="add-spot-freq" placeholder="14100" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="add-spot-mode" class="form-label">Mode</label>
|
||||
<input type="text" class="form-control" id="add-spot-mode" placeholder="SSB" style="max-width: 6em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="add-spot-comment" class="form-label">Comment</label>
|
||||
<input type="text" class="form-control" id="add-spot-comment" placeholder="59 TNX QSO 73" style="max-width: 12em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="add-spot-de-call" class="form-label">Your Call</label>
|
||||
<input type="text" class="form-control" id="add-spot-de-call" placeholder="N0CALL" style="max-width: 8em;">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" style="margin-top: 2em;" onclick="addSpot();">Spot</button>
|
||||
<span id="post-spot-result-good"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="post-spot-result-bad"></div>
|
||||
|
||||
<div class="alert alert-warning alert-dismissible fade show mb-0 mt-4" role="alert">
|
||||
Please note that spots added to Spothole are not currently sent "upstream" to DX clusters or xOTA spotting sites.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="table-container"></div>
|
||||
|
||||
</div>
|
||||
|
||||
80
webassets/js/add-spot.js
Normal file
80
webassets/js/add-spot.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// Method called to add a spot to the server
|
||||
function addSpot() {
|
||||
try {
|
||||
// Save settings (this will save "your call" for future use)
|
||||
saveSettings();
|
||||
|
||||
// Unpack the user's entered values
|
||||
var dx = $("#dx-call").val().toUpperCase();
|
||||
var freqStr = $("#freq").val();
|
||||
var mode = $("#mode").val().toUpperCase();
|
||||
var comment = $("#comment").val();
|
||||
var de = $("#de-call").val().toUpperCase();
|
||||
|
||||
var spot = {}
|
||||
if (dx != "") {
|
||||
spot["dx_call"] = dx;
|
||||
} else {
|
||||
showAddSpotError("A DX callsign is required in order to spot.");
|
||||
return;
|
||||
}
|
||||
if (freqStr != "") {
|
||||
spot["freq"] = parseFloat(freqStr) * 1000;
|
||||
} else {
|
||||
showAddSpotError("A frequency is required in order to spot.");
|
||||
return;
|
||||
}
|
||||
if (mode != "") {
|
||||
spot["mode"] = mode;
|
||||
}
|
||||
if (comment != "") {
|
||||
spot["comment"] = comment;
|
||||
}
|
||||
if (de != "") {
|
||||
spot["de_call"] = de;
|
||||
}
|
||||
spot["time"] = moment.utc().valueOf() / 1000.0;
|
||||
|
||||
$.ajax("/api/v1/spot", {
|
||||
data : JSON.stringify(spot),
|
||||
contentType : 'application/json',
|
||||
type : 'POST',
|
||||
timeout: 10000,
|
||||
success: async function (result) {
|
||||
$("#result-good").html("<button type='button' class='btn btn-success' style='margin-top: 2em;'><i class='fa-solid fa-check'></i> OK</button>");
|
||||
setTimeout(() => {
|
||||
$("#result-good").hide();
|
||||
window.location.replace("/");
|
||||
}, 1000);
|
||||
},
|
||||
error: function (result) {
|
||||
showAddSpotError(result.responseText);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
showAddSpotError(error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show an "add spot" error.
|
||||
function showAddSpotError(text) {
|
||||
$("#result-bad").html("<div class='alert alert-danger alert-dismissible fade show mb-0 mt-4' role='alert'><i class='fa-solid fa-triangle-exclamation'></i> " + text + "<button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button></div>");
|
||||
}
|
||||
|
||||
// Force callsign and mode capitalisation
|
||||
$("#dx-call").change(function () {
|
||||
$(this).val($(this).val().trim().toUpperCase());
|
||||
});
|
||||
$("#de-call").change(function () {
|
||||
$(this).val($(this).val().trim().toUpperCase());
|
||||
});
|
||||
$("#mode").change(function () {
|
||||
$(this).val($(this).val().trim().toUpperCase());
|
||||
});
|
||||
|
||||
// Startup
|
||||
$(document).ready(function() {
|
||||
// Load settings from settings storage
|
||||
loadSettings();
|
||||
});
|
||||
@@ -330,64 +330,6 @@ function userGridUpdated() {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
// Method called to add a spot to the server
|
||||
function addSpot() {
|
||||
try {
|
||||
var dx = $("#add-spot-dx-call").val().toUpperCase();
|
||||
var freqStr = $("#add-spot-freq").val();
|
||||
var mode = $("#add-spot-mode").val().toUpperCase();
|
||||
var comment = $("#add-spot-comment").val();
|
||||
var de = $("#add-spot-de-call").val().toUpperCase();
|
||||
|
||||
var spot = {}
|
||||
if (dx != "") {
|
||||
spot["dx_call"] = dx;
|
||||
} else {
|
||||
showAddSpotError("A DX callsign is required in order to spot.");
|
||||
return;
|
||||
}
|
||||
if (freqStr != "") {
|
||||
spot["freq"] = parseFloat(freqStr) * 1000;
|
||||
} else {
|
||||
showAddSpotError("A frequency is required in order to spot.");
|
||||
return;
|
||||
}
|
||||
if (mode != "") {
|
||||
spot["mode"] = mode;
|
||||
}
|
||||
if (comment != "") {
|
||||
spot["comment"] = comment;
|
||||
}
|
||||
if (de != "") {
|
||||
spot["de_call"] = de;
|
||||
}
|
||||
spot["time"] = moment.utc().valueOf() / 1000.0;
|
||||
|
||||
$.ajax("/api/v1/spot", {
|
||||
data : JSON.stringify(spot),
|
||||
contentType : 'application/json',
|
||||
type : 'POST',
|
||||
timeout: 10000,
|
||||
success: async function (result) {
|
||||
$("#post-spot-result-good").html("<button type='button' class='btn btn-success' style='margin-top: 2em;'><i class='fa-solid fa-check'></i> OK</button>");
|
||||
setTimeout(() => $("#post-spot-result-good").hide(), 2000);
|
||||
loadSpots();
|
||||
},
|
||||
error: function (result) {
|
||||
showAddSpotError(result.responseText);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
showAddSpotError(error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show an "add spot" error.
|
||||
function showAddSpotError(text) {
|
||||
$("#post-spot-result-bad").html("<div class='alert alert-danger alert-dismissible fade show mb-0 mt-4' role='alert'><i class='fa-solid fa-triangle-exclamation'></i> " + text + "<button type='button' class='btn-close' data-bs-dismiss='alert' aria-label='Close'></button></div>");
|
||||
}
|
||||
|
||||
// React to toggling/closing panels
|
||||
function toggleFiltersPanel() {
|
||||
// If we are going to show the filters panel, hide the display and add spot panels
|
||||
@@ -395,10 +337,6 @@ function toggleFiltersPanel() {
|
||||
$("#display-area").hide();
|
||||
$("#display-button").button("toggle");
|
||||
}
|
||||
if (!$("#filters-area").is(":visible") && $("#add-spot-area").is(":visible")) {
|
||||
$("#add-spot-area").hide();
|
||||
$("#add-spot-button").button("toggle");
|
||||
}
|
||||
$("#filters-area").toggle();
|
||||
}
|
||||
function closeFiltersPanel() {
|
||||
@@ -412,10 +350,6 @@ function toggleDisplayPanel() {
|
||||
$("#filters-area").hide();
|
||||
$("#filters-button").button("toggle");
|
||||
}
|
||||
if (!$("#display-area").is(":visible") && $("#add-spot-area").is(":visible")) {
|
||||
$("#add-spot-area").hide();
|
||||
$("#add-spot-button").button("toggle");
|
||||
}
|
||||
$("#display-area").toggle();
|
||||
}
|
||||
function closeDisplayPanel() {
|
||||
@@ -423,23 +357,6 @@ function closeDisplayPanel() {
|
||||
$("#display-area").hide();
|
||||
}
|
||||
|
||||
function toggleAddSpotPanel() {
|
||||
// If we are going to show the add spot panel, hide the filters and display panels
|
||||
if (!$("#add-spot-area").is(":visible") && $("#filters-area").is(":visible")) {
|
||||
$("#filters-area").hide();
|
||||
$("#filters-button").button("toggle");
|
||||
}
|
||||
if (!$("#add-spot-area").is(":visible") && $("#display-area").is(":visible")) {
|
||||
$("#display-area").hide();
|
||||
$("#display-button").button("toggle");
|
||||
}
|
||||
$("#add-spot-area").toggle();
|
||||
}
|
||||
function closeAddSpotPanel() {
|
||||
$("#add-spot-button").button("toggle");
|
||||
$("#add-spot-area").hide();
|
||||
}
|
||||
|
||||
// Display the intro box, unless the user has already dismissed it once.
|
||||
function displayIntroBox() {
|
||||
if (localStorage.getItem("intro-box-dismissed") == null) {
|
||||
|
||||
Reference in New Issue
Block a user