import os from datetime import datetime from threading import Thread, Event import psutil import pytz from core.config import SERVER_OWNER_CALLSIGN from core.constants import SOFTWARE_VERSION from core.data_store import DATA_STORE from core.prometheus_metrics_handler import memory_use_gauge, spots_gauge, alerts_gauge class StatusReporter: """Provides a timed update of the application's status data.""" def __init__(self, run_interval, web_server, spot_providers, alert_providers, solar_condition_providers, sig_ref_data_providers): """Constructor""" self._run_interval = run_interval self._web_server = web_server self._spot_providers = spot_providers self._alert_providers = alert_providers self._solar_condition_providers = solar_condition_providers self._sig_ref_data_providers = sig_ref_data_providers self._thread = None self._stop_event = Event() self._startup_time = datetime.now(pytz.UTC) DATA_STORE.status_data["software-version"] = SOFTWARE_VERSION DATA_STORE.status_data["server-owner-callsign"] = SERVER_OWNER_CALLSIGN def start(self): """Start the reporter thread""" self._thread = Thread(target=self._run, daemon=True) self._thread.start() def stop(self): """Stop any threads and prepare for application shutdown""" self._stop_event.set() def _run(self): """Thread entry point: report immediately on startup, then on each interval until stopped""" while True: self._report() if self._stop_event.wait(timeout=self._run_interval): break def _report(self): """Write status information""" DATA_STORE.status_data["uptime"] = (datetime.now(pytz.UTC) - self._startup_time).total_seconds() DATA_STORE.status_data["mem_use_mb"] = round(psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024), 3) DATA_STORE.status_data["num_spots"] = len(DATA_STORE.spots.values()) DATA_STORE.status_data["num_alerts"] = len(DATA_STORE.alerts.values()) DATA_STORE.status_data["spot_providers"] = list( 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.year > 2000 else 0, "last_spot": p.last_spot_time.replace( tzinfo=pytz.UTC).timestamp() if p.last_spot_time.year > 2000 else 0}, self._spot_providers)) DATA_STORE.status_data["alert_providers"] = list( 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.year > 2000 else 0}, self._alert_providers)) DATA_STORE.status_data["solar_condition_providers"] = list( 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.year > 2000 else 0}, self._solar_condition_providers)) DATA_STORE.status_data["sig_ref_data_providers"] = list( map(lambda p: {"sig_name": p.sig_name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace( tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0}, self._sig_ref_data_providers)) DATA_STORE.status_data["webserver"] = {"status": self._web_server.web_server_metrics["status"], "last_api_access": self._web_server.web_server_metrics[ "last_api_access_time"].replace( tzinfo=pytz.UTC).timestamp() if self._web_server.web_server_metrics[ "last_api_access_time"] else 0, "api_access_count": self._web_server.web_server_metrics["api_access_counter"], "last_page_access": self._web_server.web_server_metrics[ "last_page_access_time"].replace( tzinfo=pytz.UTC).timestamp() if self._web_server.web_server_metrics[ "last_page_access_time"] else 0, "page_access_count": self._web_server.web_server_metrics[ "page_access_counter"]} # Update Prometheus metrics memory_use_gauge.set(psutil.Process(os.getpid()).memory_info().rss) spots_gauge.set(len(DATA_STORE.spots.values())) alerts_gauge.set(len(DATA_STORE.alerts.values()))