diff --git a/server/webserver.py b/server/webserver.py index db90e81..c7812a5 100644 --- a/server/webserver.py +++ b/server/webserver.py @@ -69,13 +69,45 @@ class WebServer: # Utility method to apply filters to the overall spot list and return only a subset. Enables query parameters in # the main "spots" GET call. The "query" parameter should be the result of bottle's request.query, and is a MultiDict def get_spot_list_with_filters(self, query): - spot_subset = self.spot_list[:] + # Create a shallow copy of the spot list, ordered by spot time. We'll then filter it accordingly. + # We can filter by spot time and received time with "since" and "received_since", which take a UNIX timestamp + # in seconds UTC. + # We can also filter by source, sig, band, mode, dx_continent and de_continent. Each of these accepts a single + # value or a comma-separated list. + # We can provide a "limit" number as well. Spots are always returned newest-first; "limit" limits to only the + # most recent X spots. + spots = sorted(self.spot_list, key=lambda spot: spot.time, reverse=True) for k in query.keys(): - print(k + ": " + query.get(k)) - return spot_subset + match k: + case "since": + since = datetime.fromtimestamp(int(query.get(k)), pytz.UTC) + spots = [s for s in spots if s.time > since] + case "received_since": + since = datetime.fromtimestamp(int(query.get(k)), pytz.UTC) + spots = [s for s in spots if s.received_time > since] + case "source": + sources = query.get(k).split(",") + spots = [s for s in spots if s.source in sources] + case "sig": + sigs = query.get(k).split(",") + spots = [s for s in spots if s.sig in sigs] + case "band": + bands = query.get(k).split(",") + spots = [s for s in spots if s.band in bands] + case "mode": + modes = query.get(k).split(",") + spots = [s for s in spots if s.mode in modes] + case "dx_continent": + dxconts = query.get(k).split(",") + spots = [s for s in spots if s.dx_continent in dxconts] + case "de_continent": + deconts = query.get(k).split(",") + spots = [s for s in spots if s.de_continent in deconts] + # If we have a "limit" parameter, we apply that last, regardless of where it appeared in the list of keys. + if "limit" in query.keys(): + spots = spots[:int(query.get("limit"))] + return spots - - -# Todo serve Server-Sent Events to frontend - see https://medium.com/@tdenton8772/streaming-api-design-using-python-and-javascript-1b0ce8adb703 +# Todo serve Server-Sent Events to frontend? - see https://medium.com/@tdenton8772/streaming-api-design-using-python-and-javascript-1b0ce8adb703 # Todo serve apidocs \ No newline at end of file diff --git a/webassets/js/code.js b/webassets/js/code.js index 53fd0c8..f4cdd9f 100644 --- a/webassets/js/code.js +++ b/webassets/js/code.js @@ -1,8 +1,6 @@ // TODO get all data into JS on first load -// TODO subscribe to SSE to get updates, merge with local data, sort, refresh display -// TODO cap at a sensible number of lines in the table, throw away other data, make the number configurable // TODO populate table more nicely -// TODO filtering (applied locally) +// TODO filtering & limit (applied via new request to server) // TODO look and feel $.getJSON('/api/spots', function(jsonData) {