From b45fda2dd2e867da7462c76322e0d25bbcc0b283 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Wed, 8 Oct 2025 16:26:43 +0100 Subject: [PATCH] Fix status query. Closes #36 --- core/status_reporter.py | 22 ++++++++++----- server/webserver.py | 1 + webassets/apidocs/openapi.yml | 50 +++++++++++++++++------------------ webassets/js/spots.js | 20 +++++++++----- 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/core/status_reporter.py b/core/status_reporter.py index 621cfec..acd23e2 100644 --- a/core/status_reporter.py +++ b/core/status_reporter.py @@ -39,21 +39,29 @@ class StatusReporter: # Write status information and reschedule next timer def run(self): - self.status_data["uptime"] = str(datetime.now(pytz.UTC) - self.startup_time).split(".")[0] + self.status_data["uptime"] = (datetime.now(pytz.UTC) - self.startup_time).total_seconds() self.status_data["mem_use_mb"] = round(psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024), 3) self.status_data["num_spots"] = len(self.spots) self.status_data["num_alerts"] = len(self.alerts) self.status_data["spot_providers"] = list( - map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time, - "last_spot": p.last_spot_time}, self.spot_providers)) + map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status, + "last_updated": p.last_update_time.replace( + tzinfo=pytz.UTC).timestamp() if p.last_update_time else 0, + "last_spot": p.last_spot_time.replace( + tzinfo=pytz.UTC).timestamp() if p.last_spot_time else 0}, self.spot_providers)) self.status_data["alert_providers"] = list( map(lambda p: {"name": p.name, "enabled": p.enabled, "status": p.status, - "last_updated": p.last_update_time}, self.alert_providers)) + "last_updated": p.last_update_time.replace( + tzinfo=pytz.UTC).timestamp() if p.last_update_time else 0}, + self.alert_providers)) self.status_data["cleanup"] = {"status": self.cleanup_timer.status, - "last_ran": self.cleanup_timer.last_cleanup_time} + "last_ran": self.cleanup_timer.last_cleanup_time.replace( + tzinfo=pytz.UTC).timestamp() if self.cleanup_timer.last_cleanup_time else 0} self.status_data["webserver"] = {"status": self.web_server.status, - "last_api_access": self.web_server.last_api_access_time, - "last_page_access": self.web_server.last_page_access_time} + "last_api_access": self.web_server.last_api_access_time.replace( + tzinfo=pytz.UTC).timestamp() if self.web_server.last_api_access_time else 0, + "last_page_access": self.web_server.last_page_access_time.replace( + tzinfo=pytz.UTC).timestamp() if self.web_server.last_page_access_time else 0} self.run_timer = Timer(self.run_interval, self.run) self.run_timer.start() diff --git a/server/webserver.py b/server/webserver.py index fbb0547..de431c5 100644 --- a/server/webserver.py +++ b/server/webserver.py @@ -58,6 +58,7 @@ class WebServer: self.status = "OK" response.content_type = 'application/json' response.set_header('Cache-Control', 'no-store') + print(data) return json.dumps(data, default=serialize_everything) # Accept a spot diff --git a/webassets/apidocs/openapi.yml b/webassets/apidocs/openapi.yml index ae298dc..5f53c12 100644 --- a/webassets/apidocs/openapi.yml +++ b/webassets/apidocs/openapi.yml @@ -33,19 +33,19 @@ paths: description: Limit the spots to only ones at this time or later. Time in UTC seconds since UNIX epoch. Equivalent to "max_age" but saves the client having to work out how many seconds ago "midnight" was. required: false schema: - type: integer + type: number - name: max_age in: query description: Limit the spots to only ones received in the last 'n' seconds. Equivalent to "since" but saves the client having to work out what time was 'n' seconds ago on every call. Refer to the "max_spot_age" in the /options call to figure out what the maximum useful value you can provide is. Larger values will still be accepted, there just won't be any spots in the system older than max_spot_age. required: false schema: - type: integer + type: number - name: received_since in: query description: Limit the spots to only ones that the system found out about at this time or later. Time in UTC seconds since UNIX epoch. If you are using a front-end that tracks the last time it queried the API and requests spots since then, you want *this* version of the query parameter, not "since", because otherwise it may miss things. The logic is "greater than" rather than "greater than or equal to", so you can submit the time of the last received item back to this call and you will get all the more recent spots back, without duplicating the previous latest spot. required: false schema: - type: integer + type: number - name: source in: query description: "Limit the spots to only ones from one or more sources. To select more than one source, supply a comma-separated list." @@ -283,10 +283,10 @@ paths: type: string description: The callsign of this server's operator. example: "M0TRT" - "uptime": - type: string - description: The amount of time the software has been running for. - example: "12:34:56" + "uptime_sec": + type: integer + description: The amount of time the software has been running for, in seconds. + example: 12345 "mem_use_mb": type: number description: The amount of memory the software is using, in megabytes. @@ -307,9 +307,9 @@ paths: description: The status of the cleanup thread example: OK last_ran: - type: string - description: The last time the cleanup operation ran - example: 2025-09-28T20:31:00+00:00 + type: number + description: The last time the cleanup operation ran, UTC seconds since UNIX epoch. + example: 1759579508 "webserver": type: object properties: @@ -318,13 +318,13 @@ paths: description: The status of the web server example: OK last_page_access: - type: string - description: The last time a page was accessed on the web server - example: 2025-09-28T20:31:00+00:00 + type: number + description: The last time a page was accessed on the web server, UTC seconds since UNIX epoch. + example: 1759579508 last_api_access: - type: string - description: The last time an API endpoint was accessed on the web server - example: 2025-09-28T20:31:00+00:00 + type: number + description: The last time an API endpoint was accessed on the web server, UTC seconds since UNIX epoch. + example: 1759579508 spot_providers: type: array description: An array of all the spot providers. @@ -823,13 +823,13 @@ components: description: The status of the provider. example: OK last_updated: - type: string - description: The last time at which this provider received data. - example: 2025-09-28T20:31:00+00:00 + type: number + description: The last time at which this provider received data, UTC seconds since UNIX epoch. + example: 1759579508 last_spot: - type: string - description: The time of the latest spot received by this provider. - example: 2025-09-28T20:31:00+00:00 + type: number + description: The time of the latest spot received by this provider, UTC seconds since UNIX epoch. + example: 1759579508 AlertProviderStatus: type: object @@ -847,9 +847,9 @@ components: description: The status of the provider. example: OK last_updated: - type: string - description: The last time at which this provider received data. - example: 2025-09-28T20:31:00+00:00 + type: number + description: The last time at which this provider received data, UTC seconds since UNIX epoch. + example: 1759579508 Band: type: object diff --git a/webassets/js/spots.js b/webassets/js/spots.js index 05d3b93..ec08d0c 100644 --- a/webassets/js/spots.js +++ b/webassets/js/spots.js @@ -153,24 +153,30 @@ function loadStatus() { $("#status-container").append(generateStatusCard("Server Information", [ `Software Version: ${jsonData["software-version"]}`, `Server Owner Callsign: ${jsonData["server-owner-callsign"]}`, - `Server Uptime: ${jsonData["uptime"]}`, + `Server Uptime: ${moment().subtract(jsonData["uptime"], 'seconds').fromNow()}`, `Memory Use: ${jsonData["mem_use_mb"]} MB`, `Total Spots: ${jsonData["num_spots"]}` ])); $("#status-container").append(generateStatusCard("Web Server", [ `Status: ${jsonData["webserver"]["status"]}`, - `Last API Access: ${moment.utc(jsonData["webserver"]["last_api_access"], moment.ISO_8601).format("HH:mm")}`, - `Last Page Access: ${moment.utc(jsonData["webserver"]["last_page_access"], moment.ISO_8601).format("HH:mm")}` + `Last API Access: ${moment.unix(jsonData["webserver"]["last_api_access"]).utc().fromNow()}`, + `Last Page Access: ${moment.unix(jsonData["webserver"]["last_page_access"]).utc().fromNow()}` ])); $("#status-container").append(generateStatusCard("Cleanup Service", [ `Status: ${jsonData["cleanup"]["status"]}`, - `Last Ran: ${moment.utc(jsonData["cleanup"]["last_ran"], moment.ISO_8601).format("HH:mm")}` + `Last Ran: ${moment.unix(jsonData["cleanup"]["last_ran"]).utc().fromNow()}` ])); - jsonData["providers"].forEach(p => { + jsonData["spot_providers"].forEach(p => { $("#status-container").append(generateStatusCard("Provider: " + p["name"], [ `Status: ${p["status"]}`, - `Last Updated: ${p["enabled"] ? moment.utc(p["last_updated"], moment.ISO_8601).format("HH:mm") : "N/A"}`, - `Latest Spot: ${p["enabled"] ? moment.utc(p["last_spot"], moment.ISO_8601).format("HH:mm YYYY-MM-DD") : "N/A"}` + `Last Updated: ${p["enabled"] ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}`, + `Latest Spot: ${p["enabled"] ? moment.unix(p["last_spot"]).utc().fromNow() : "N/A"}` + ])); + }); + jsonData["alert_providers"].forEach(p => { + $("#status-container").append(generateStatusCard("Provider: " + p["name"], [ + `Status: ${p["status"]}`, + `Last Updated: ${p["enabled"] ? moment.unix(p["last_updated"]).utc().fromNow() : "N/A"}` ])); }); });