diff --git a/alertproviders/pota.py b/alertproviders/pota.py index fb47c95..ec56e53 100644 --- a/alertproviders/pota.py +++ b/alertproviders/pota.py @@ -33,7 +33,9 @@ class POTA(HTTPAlertProvider): end_time=datetime.strptime(source_alert["endDate"] + source_alert["endTime"], "%Y-%m-%d%H:%M").replace(tzinfo=pytz.UTC).timestamp()) - # Add to our list. Don't worry about de-duping, removing old alerts etc. at this point; other code will do + # Add to our list, but exclude any old spots that POTA can sometimes give us where even the end time is + # in the past. Don't worry about de-duping, removing old alerts etc. at this point; other code will do # that for us. - new_alerts.append(alert) + if alert.end_time > datetime.now(pytz.UTC).timestamp(): + new_alerts.append(alert) return new_alerts diff --git a/core/cleanup.py b/core/cleanup.py index 89a7ba8..3c7eba2 100644 --- a/core/cleanup.py +++ b/core/cleanup.py @@ -10,8 +10,9 @@ import pytz class CleanupTimer: # Constructor - def __init__(self, spots, cleanup_interval): + def __init__(self, spots, alerts, cleanup_interval): self.spots = spots + self.alerts = alerts self.cleanup_interval = cleanup_interval self.cleanup_timer = None self.last_cleanup_time = datetime.min.replace(tzinfo=pytz.UTC) @@ -30,6 +31,7 @@ class CleanupTimer: try: # Perform cleanup self.spots.expire() + self.alerts.expire() self.status = "OK" self.last_cleanup_time = datetime.now(pytz.UTC) diff --git a/server/webserver.py b/server/webserver.py index e29df7b..cfbfbdc 100644 --- a/server/webserver.py +++ b/server/webserver.py @@ -17,10 +17,11 @@ from data.spot import Spot class WebServer: # Constructor - def __init__(self, spots, status_data, port): + def __init__(self, spots, alerts, status_data, port): self.last_page_access_time = None self.last_api_access_time = None self.spots = spots + self.alerts = alerts self.status_data = status_data self.port = port self.thread = Thread(target=self.run) @@ -175,17 +176,21 @@ class WebServer: # Get the query (and the right one, with Bottle magic. This is a MultiDict object) query = bottle.request.query - # Create a shallow copy of the alert list, ordered by alert time. We'll then filter it accordingly. + # Create a shallow copy of the alert list, ordered by start time. We'll then filter it accordingly. # We can filter by received time with "received_since", which take a UNIX timestamp in seconds UTC. # We can also filter by source, sig, and dx_continent. Each of these accepts a single # value or a comma-separated list. # We can provide a "limit" number as well. Alerts are always returned newest-first; "limit" limits to only the # most recent X alerts. - alert_ids = list(self.spots.iterkeys()) + alert_ids = list(self.alerts.iterkeys()) alerts = [] for k in alert_ids: - alerts.append(self.spots.get(k)) - alerts = sorted(alerts, key=lambda spot: spot.time, reverse=True) + # While we persist old spots in the system for a while to produce a useful list, any alert that has already + # passed its end time can be explicitly removed from the list to return. + # TODO deal with there being no end time + if self.alerts.get(k).end_time > datetime.now(pytz.UTC).timestamp(): + alerts.append(self.alerts.get(k)) + alerts = sorted(alerts, key=lambda alert: alert.start_time) for k in query.keys(): match k: case "received_since": diff --git a/spothole.py b/spothole.py index 5a85444..9ce94d4 100644 --- a/spothole.py +++ b/spothole.py @@ -82,11 +82,11 @@ if __name__ == '__main__': p.start() # Set up timer to clear spot list of old data - cleanup_timer = CleanupTimer(spots=spots, cleanup_interval=60) + cleanup_timer = CleanupTimer(spots=spots, alerts=alerts, cleanup_interval=60) cleanup_timer.start() # Set up web server - web_server = WebServer(spots=spots, status_data=status_data, port=WEB_SERVER_PORT) + web_server = WebServer(spots=spots, alerts= alerts, status_data=status_data, port=WEB_SERVER_PORT) web_server.start() # Set up status reporter diff --git a/views/webpage_alerts.tpl b/views/webpage_alerts.tpl index c3cbed4..4808e0a 100644 --- a/views/webpage_alerts.tpl +++ b/views/webpage_alerts.tpl @@ -12,7 +12,7 @@ -
| Start UTC | `); + table.find('thead tr').append(`End UTC | `); + table.find('thead tr').append(`DX | `); + table.find('thead tr').append(`Frequencies & Modes | `); + table.find('thead tr').append(`Comment | `); + table.find('thead tr').append(`Source | `); + table.find('thead tr').append(`Ref. | `); + + if (alerts.length == 0) { + table.find('tbody').append('|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| No alerts match your filters. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${start_time_formatted} | `); + $tr.append(`${end_time_formatted} | `); + $tr.append(`${s["dx_call"]} | `); + $tr.append(`${freqsModesText} | `); + $tr.append(`${commentText} | `); + $tr.append(``); + $tr.append(`${sig_refs} | `); + table.find('tbody').append($tr); + + // Second row for mobile view only, containing source, ref, freqs/modes & comment + $tr2 = $("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ${sig_refs} ${freqsModesText} ${commentText} | `);
+ table.find('tbody').append($tr2);
+ });
+
+ // Update DOM
+ $('#table-container').html(table);
+}
+
+// Load server options. Once a successful callback is made from this, we then query alerts.
+function loadOptions() {
+ $.getJSON('/api/options', function(jsonData) {
+ // Store options
+ options = jsonData;
+
+ // Populate the filters panel
+ $("#settings-container").append(generateFilterCard("DX Continent", "dx_continent", options["continents"]));
+ $("#settings-container").append(generateFilterCard("Sources", "source", options["spot_sources"]));
+
+ // Load settings from settings storage
+ loadSettings();
+
+ // Load alerts
+ loadAlerts();
+ });
+}
+
+// Generate filter card
+function generateFilterCard(displayName, filterQuery, options) {
+ let $col = $("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||