From ac5409587d4ef9056cb290eef3ced675ab4fc82f Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Sat, 11 Sep 2021 23:44:15 +0100 Subject: [PATCH 01/14] Add WIP UI and manager for alerts. --- baps_types/alert.py | 65 ++++++++++++++++++++++++++++++++++++++++ helpers/alert_manager.py | 29 ++++++++++++++++++ ui-templates/alerts.html | 41 +++++++++++++++++++++++++ ui-templates/index.html | 20 +++++++++++++ web_server.py | 20 ++++++++++++- 5 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 baps_types/alert.py create mode 100644 helpers/alert_manager.py create mode 100644 ui-templates/alerts.html diff --git a/baps_types/alert.py b/baps_types/alert.py new file mode 100644 index 0000000..493a544 --- /dev/null +++ b/baps_types/alert.py @@ -0,0 +1,65 @@ +from typing import Any, Dict +from datetime import datetime + +CRITICAL = "Critical" +WARNING = "Warning" + +class Alert: + start_time: int = 0 + last_time: int = 0 + end_time: int = -1 + id: str + title: str + description: str + module: str + severity: str + + + @property + def ui_class(self) -> str: + if self.severity == CRITICAL: + return "danger" + if self.severity == WARNING: + return "warning" + return "info" + + # return self._weight + + # weight.setter + # def weight(self, value: int): + # self._weight = value + + + @property + def __dict__(self): + attrs = ["start_time", "last_time", "end_time", "id", "title", "description", "module", "severity"] + out = {} + for attr in attrs: + out[attr] = self.__getattribute__(attr) + + return out + + def __init__(self, new_data: Dict[str,Any]): + required_vars = [ + "start_time", # Just in case an alert wants to show starting earlier than it is reported. + "id", + "title", + "description", + "module", + "severity" + ] + + for key in required_vars: + if key not in new_data.keys(): + raise KeyError("Key {} is missing from data to create Alert.".format(key)) + + #if type(new_data[key]) != type(getattr(self,key)): + # raise TypeError("Key {} has type {}, was expecting {}.".format(key, type(new_data[key]), type(getattr(self,key)))) + + # Account for if the creator didn't want to set a custom time. + if key == "start_time" and new_data[key] == -1: + new_data[key] = datetime.now() + + setattr(self,key,new_data[key]) + + self.last_time = self.start_time diff --git a/helpers/alert_manager.py b/helpers/alert_manager.py new file mode 100644 index 0000000..820f45d --- /dev/null +++ b/helpers/alert_manager.py @@ -0,0 +1,29 @@ +from typing import List +from baps_types.alert import CRITICAL, Alert + +class AlertManager(): + _alerts: List[Alert] + + def __init__(self): + self._alerts = [Alert( + { + "start_time": -1, + "id": "test", + "title": "Test Alert", + "description": "This is a test alert.", + "module": "Test", + "severity": CRITICAL + } + )] + + @property + def alerts_current(self): + return self._alerts + + @property + def alert_count_current(self): + return len(self._alerts) + + @property + def alert_count_previous(self): + return len(self._alerts) diff --git a/ui-templates/alerts.html b/ui-templates/alerts.html new file mode 100644 index 0000000..edf1b7e --- /dev/null +++ b/ui-templates/alerts.html @@ -0,0 +1,41 @@ +{% extends 'base.html' %} +{% block head %} + +{% endblock %} +{% block content_inner %} + {% if data %} +

Current Alerts: {{ data.alert_count_current }}

+ +
+ {% for alert in data.alerts_current %} +
+
+

+ + {{ alert.severity }} +

+ Since {{ alert.start_time }} + Last Seen {{ alert.last_time }} + {% if alert.end_time > -1 %} + Ended {{ alert.end_time }} + {% endif %} +
+ +
+
+ Module: {{ alert.module }}
+ {{ alert.description }} +
+
+
+ {% endfor %} +
+
+

Previous Alerts: {{ data.alert_count_previous }}

+
+ +
+ {% endif %} +{% endblock %} diff --git a/ui-templates/index.html b/ui-templates/index.html index 53c5aa4..bb8b21b 100644 --- a/ui-templates/index.html +++ b/ui-templates/index.html @@ -1,4 +1,7 @@ {% extends 'base.html' %} +{% block head %} + +{% endblock %} {% block content %}
@@ -16,6 +19,23 @@ Open BAPS Presenter + +
+ {% if data.alert_count > 0 %} + diff --git a/ui-templates/index.html b/ui-templates/index.html index bb8b21b..fe62487 100644 --- a/ui-templates/index.html +++ b/ui-templates/index.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block head %} - + {% endblock %} {% block content %} From f1e04c3d8a6e2020c5aea3a576e61cde040391ad Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Thu, 16 Sep 2021 23:42:20 +0100 Subject: [PATCH 05/14] Switch sanic debug to follow package.BETA --- web_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_server.py b/web_server.py index 85ee75e..a46dc8e 100644 --- a/web_server.py +++ b/web_server.py @@ -17,7 +17,6 @@ import json import os from helpers.os_environment import ( - isBundelled, resolve_external_file_path, resolve_local_file_path, ) @@ -28,6 +27,7 @@ from helpers.the_terminator import Terminator from helpers.normalisation import get_normalised_filename_if_available from helpers.myradio_api import MyRadioAPI from helpers.alert_manager import AlertManager +import package env = Environment( loader=FileSystemLoader("%s/ui-templates/" % os.path.dirname(__file__)), @@ -525,9 +525,9 @@ def WebServer(player_to: List[Queue], player_from: List[Queue], state: StateMana app.run( host=server_state.get()["host"], port=server_state.get()["port"], - debug=(not isBundelled()), auto_reload=False, - access_log=(not isBundelled()), + debug=not package.BETA, + access_log=not package.BETA, ) ) except Exception: From 7dc0facf73295702e723795cf99ae75f0b0ea389 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 21 Sep 2021 22:49:05 +0100 Subject: [PATCH 06/14] Add basic player restart alerts --- alerts/player.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ player.py | 5 ++- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 alerts/player.py diff --git a/alerts/player.py b/alerts/player.py new file mode 100644 index 0000000..45184c1 --- /dev/null +++ b/alerts/player.py @@ -0,0 +1,93 @@ +# Any alerts produced by the player.py instances. +import json +from typing import Any, Dict, List, Optional +from datetime import datetime, timedelta +from helpers.os_environment import resolve_external_file_path +from helpers.alert_manager import AlertProvider +from baps_types.alert import CRITICAL, WARNING, Alert + +MODULE = "Player" # This should match the log file, so the UI will link to the logs page. + +class PlayerAlertProvider(AlertProvider): + + _server_state: Dict[str, Any] + _states: List[Optional[Dict[str,Any]]] = [] + _player_count: int + + def __init__(self): + # Player count only changes after server restart, may as well just load this once. + with open(resolve_external_file_path("state/BAPSicleServer.json")) as file: + self._server_state = json.loads(file.read()) + + self._player_count = int(self._server_state["num_channels"]) + self._states = [None] * self._player_count + + # To simplify monitoring (and allow detection of things going super weird), we are going to read from the state file to work out the alerts. + def get_alerts(self): + for channel in range(self._player_count): + with open(resolve_external_file_path("state/Player{}.json".format(channel))) as file: + self._states[channel] = json.loads(file.read()) + + funcs = [self._channel_count, self._initialised, self._start_time] + + alerts: List[Alert] = [] + + for func in funcs: + func_alerts = func() + if func_alerts: + alerts.extend(func_alerts) + + return alerts + + def _channel_count(self): + if self._player_count <= 0: + return [Alert({ + "start_time": -1, # Now + "id": "no_channels", + "title": "There are no players configured.", + "description": "The number of channels configured is {}. Please set to at least 1 on the 'Server Config' page.".format(self._player_count), + "module": MODULE+"Handler", + "severity": CRITICAL + })] + + def _initialised(self): + alerts: List[Alert] = [] + for channel in range(self._player_count): + if self._states[channel] and not self._states[channel]["initialised"]: + alerts.append(Alert({ + "start_time": -1, # Now + "id": "player_{}_not_initialised".format(channel), + "title": "Player {} is not initialised.".format(channel), + "description": "This typically means the player channel was not able find the configured sound output on the system. Please check the 'Player Config' and Player logs to determine the cause.", + "module": MODULE+str(channel), + "severity": CRITICAL + })) + return alerts + + def _start_time(self): + server_start_time = self._server_state["start_time"] + server_start_time = datetime.fromtimestamp(server_start_time) + delta = timedelta( + seconds=30, + ) + + alerts: List[Alert] = [] + for channel in range(self._player_count): + start_time = self._states[channel]["start_time"] + start_time = datetime.fromtimestamp(start_time) + if (start_time > server_start_time + delta): + alerts.append(Alert({ + "start_time": -1, + "id": "player_{}_restarted".format(channel), + "title": "Player {} restarted after the server started.".format(channel), + "description": +"""Player {} last restarted at {}, after the server first started at {}, suggesting a failure. + +This likely means there was an unhandled exception in the player code, causing the server to restart the player. + +Please check player logs to investigate the cause. Please restart the server to clear this warning.""" + .format(channel, str(start_time).rsplit(".",1)[0], str(server_start_time).rsplit(".",1)[0]), + "module": MODULE+str(channel), + "severity": WARNING + })) + return alerts diff --git a/player.py b/player.py index 2f70f31..5230432 100644 --- a/player.py +++ b/player.py @@ -35,6 +35,7 @@ from pygame import mixer from mutagen.mp3 import MP3 from syncer import sync from threading import Timer +from datetime import datetime from helpers.normalisation import get_normalised_filename_if_available from helpers.myradio_api import MyRadioAPI @@ -969,7 +970,7 @@ class Player: self.out_q = out_q self.logger = LoggingManager( - "Player" + str(channel), debug=package.build_beta) + "Player" + str(channel), debug=package.BETA) self.api = MyRadioAPI(self.logger, server_state) @@ -980,6 +981,8 @@ class Player: self.__rate_limited_params, ) + self.state.update("start_time", datetime.now().timestamp()) + self.state.add_callback(self._send_status) self.state.update("channel", channel) From 642aaf04d599a63114c17ddd4896f1667338a985 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 21 Sep 2021 22:50:52 +0100 Subject: [PATCH 07/14] Remove channel states from alerts page info --- web_server.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web_server.py b/web_server.py index a46dc8e..89d17f8 100644 --- a/web_server.py +++ b/web_server.py @@ -144,10 +144,6 @@ def ui_status(request): @app.route("/alerts") def ui_alerts(request): - channel_states = [] - for i in range(server_state.get()["num_channels"]): - channel_states.append(status(i)) - data = { "alerts_current": alerts.alerts_current, "alerts_count_current": alerts.alert_count_current, From 4e9a1c4b6e29c2259e96ffd2f6d5eaf7efc60695 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 21 Sep 2021 22:52:27 +0100 Subject: [PATCH 08/14] Switch to quit. --- ui-templates/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-templates/index.html b/ui-templates/index.html index fe62487..52ebd66 100644 --- a/ui-templates/index.html +++ b/ui-templates/index.html @@ -33,7 +33,7 @@
Logs Restart - Shutdown + Quit

From 382de936a336896e88998f5f9948a2bcb940c4fd Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Tue, 21 Sep 2021 23:02:24 +0100 Subject: [PATCH 09/14] Allow player config to better handle borked players. --- ui-templates/config_player.html | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ui-templates/config_player.html b/ui-templates/config_player.html index 1c09a90..78d2c79 100644 --- a/ui-templates/config_player.html +++ b/ui-templates/config_player.html @@ -9,21 +9,25 @@

Currently Selected

{% for channel in data.channels %} - Player {{channel.channel}}: - {% if channel.output %} - {{channel.output}} + Player {{loop.index0}}: + {% if channel %} + {% if channel.output %} + {{channel.output}} + {% else %} + Default Audio Device + {% endif %} + {% if not channel.initialised %} + - ERROR: Player did not start, potentially configured sound output is missing? Check Alerts. + {% endif %} {% else %} - Default Audio Device - {% endif %} - {% if not channel.initialised %} - - ERROR: Player did not start, potentially missing sound output? + - ERROR: Player did not respond, likely it is dead :/ Check Alerts. {% endif %}
{% endfor %}
Set for: -{% for channel in data.channels %} +{% for channel in data.channels if channel %} Player {{channel.channel}} / {% endfor %} Default Audio Output @@ -37,7 +41,9 @@ Default Audio Output {% if host_api.usable %} Set for: {% for channel in data.channels %} - {% if channel.output == output.name %} + {% if not channel %} + Player {{loop.index0}} + {% elif channel.output == output.name %} Player {{channel.channel}} {% else %} Player {{channel.channel}} From bc2e60cdc9c33820b4bba95de2b0fda856aa2304 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Wed, 22 Sep 2021 00:15:17 +0100 Subject: [PATCH 10/14] Update alerts to actually update/end, move list to macro. --- baps_types/alert.py | 20 +++++++++------ helpers/alert_manager.py | 39 +++++++++++++++++++++--------- ui-templates/alerts.html | 35 ++++----------------------- ui-templates/parts/alert_list.html | 31 ++++++++++++++++++++++++ web_server.py | 4 +-- 5 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 ui-templates/parts/alert_list.html diff --git a/baps_types/alert.py b/baps_types/alert.py index 493a544..f6958fc 100644 --- a/baps_types/alert.py +++ b/baps_types/alert.py @@ -1,13 +1,13 @@ -from typing import Any, Dict +from typing import Any, Dict, Optional from datetime import datetime CRITICAL = "Critical" WARNING = "Warning" class Alert: - start_time: int = 0 - last_time: int = 0 - end_time: int = -1 + start_time: datetime + last_time: datetime + end_time: Optional[datetime] id: str title: str description: str @@ -23,11 +23,14 @@ class Alert: return "warning" return "info" - # return self._weight + # This alert has happened again. + def reoccured(self): + self.last_time = datetime.now() + self.end_time = None - # weight.setter - # def weight(self, value: int): - # self._weight = value + # This alert has finished, just update end time and keep last_time. + def cleared(self): + self.end_time = datetime.now() @property @@ -63,3 +66,4 @@ class Alert: setattr(self,key,new_data[key]) self.last_time = self.start_time + self.end_time = None diff --git a/helpers/alert_manager.py b/helpers/alert_manager.py index fea93ed..ab9c476 100644 --- a/helpers/alert_manager.py +++ b/helpers/alert_manager.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any, List, Optional #Magic for importing alert providers from alerts directory. from pkgutil import iter_modules @@ -54,25 +54,40 @@ class AlertManager(): def poll_alerts(self): # Poll modules for any alerts. - alerts: List[Alert] = [] + new_alerts: List[Optional[Alert]] = [] for provider in self._providers: provider_alerts = provider.get_alerts() if provider_alerts: - alerts.extend(provider_alerts) + new_alerts.extend(provider_alerts) - self._alerts = alerts + # Here we replace new firing alerts with older ones, to keep any context. + # (This doesn't do anything yet really, for future use.) + for existing in self._alerts: + found = False + for new in new_alerts: + # given we're removing alerts, got to skip any we removed. + if not new: + continue + + if existing.id == new.id: + # Alert is continuing. Replace it with the old one. + index = new_alerts.index(new) + existing.reoccured() + new_alerts[index] = None # We're going to merge the existing and new, so clear the new one out. + found = True + break + if found == False: + # The existing alert is gone, mark it as ended. + existing.cleared() + + self._alerts.extend([value for value in new_alerts if value]) # Remove any nulled out new alerts @property def alerts_current(self): self.poll_alerts() - return self._alerts + return [alert for alert in self._alerts if not alert.end_time] @property - def alert_count_current(self): + def alerts_previous(self): self.poll_alerts() - return len(self._alerts) - - @property - def alert_count_previous(self): - self.poll_alerts() - return len(self._alerts) + return [alert for alert in self._alerts if alert.end_time] diff --git a/ui-templates/alerts.html b/ui-templates/alerts.html index ff100c0..36e5fdd 100644 --- a/ui-templates/alerts.html +++ b/ui-templates/alerts.html @@ -1,43 +1,18 @@ {% extends 'base.html' %} +{% from 'parts/alert_list.html' import alert_list %} {% block head %} {% endblock %} {% block content_inner %} {% if data %}

Current Alerts: {{ data.alert_count_current }}

-
- {% for alert in data.alerts_current %} -
-
-

- - {{ alert.severity }} -

- Since {{ alert.start_time }} - Last Seen {{ alert.last_time }} - {% if alert.end_time > -1 %} - Ended {{ alert.end_time }} - {% endif %} -
- -
-
- Module: {{ alert.module }} - {% autoescape false %} -

{{ alert.description | replace("\n\n", "

") | replace("\n", "
")}}

- {% endautoescape %} -
-
-
- {% endfor %} + {{ alert_list(data.alerts_current) }}

Previous Alerts: {{ data.alert_count_previous }}

-
- -
+
+ {{ alert_list(data.alerts_previous) }} +
{% endif %} {% endblock %} diff --git a/ui-templates/parts/alert_list.html b/ui-templates/parts/alert_list.html new file mode 100644 index 0000000..6c8ae48 --- /dev/null +++ b/ui-templates/parts/alert_list.html @@ -0,0 +1,31 @@ +{% macro alert_list(alerts) %} + {% for alert in alerts %} +
+
+

+ + {{ alert.severity }} +

+ Since {{ alert.start_time }} + Last Seen {{ alert.last_time }} + {% if alert.end_time %} + Ended {{ alert.end_time }} + {% endif %} +
+ +
+
+ Module: {{ alert.module }} + {% autoescape false %} +

{{ alert.description | replace("\n\n", "

") | replace("\n", "
")}}

+ {% endautoescape %} +
+
+
+ {% endfor %} + {% if not alerts %} + No alerts here. + {% endif %} +{% endmacro %} diff --git a/web_server.py b/web_server.py index 89d17f8..08252a7 100644 --- a/web_server.py +++ b/web_server.py @@ -122,7 +122,7 @@ def ui_index(request): data = { "ui_page": "index", "ui_title": "", - "alert_count": alerts.alert_count_current, + "alert_count": len(alerts.alerts_current), "server_version": config["server_version"], "server_build": config["server_build"], "server_name": config["server_name"], @@ -146,7 +146,7 @@ def ui_status(request): def ui_alerts(request): data = { "alerts_current": alerts.alerts_current, - "alerts_count_current": alerts.alert_count_current, + "alerts_previous": alerts.alerts_previous, "ui_page": "alerts", "ui_title": "Alerts" } From b8c6f087c6381add5c5e645c0df58a20dca60fc6 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Wed, 22 Sep 2021 00:57:51 +0100 Subject: [PATCH 11/14] Tidy up alert rendering. --- alerts/player.py | 3 ++- alerts/server.py | 3 ++- baps_types/happytime.py | 3 +++ ui-templates/alerts.html | 12 ++++-------- ui-templates/parts/alert_list.html | 16 ++++++---------- web_server.py | 6 ++++++ 6 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 baps_types/happytime.py diff --git a/alerts/player.py b/alerts/player.py index 45184c1..ff1b437 100644 --- a/alerts/player.py +++ b/alerts/player.py @@ -5,6 +5,7 @@ from datetime import datetime, timedelta from helpers.os_environment import resolve_external_file_path from helpers.alert_manager import AlertProvider from baps_types.alert import CRITICAL, WARNING, Alert +from baps_types.happytime import happytime MODULE = "Player" # This should match the log file, so the UI will link to the logs page. @@ -86,7 +87,7 @@ class PlayerAlertProvider(AlertProvider): This likely means there was an unhandled exception in the player code, causing the server to restart the player. Please check player logs to investigate the cause. Please restart the server to clear this warning.""" - .format(channel, str(start_time).rsplit(".",1)[0], str(server_start_time).rsplit(".",1)[0]), + .format(channel, happytime(start_time), happytime(server_start_time)), "module": MODULE+str(channel), "severity": WARNING })) diff --git a/alerts/server.py b/alerts/server.py index 7b56151..375f87d 100644 --- a/alerts/server.py +++ b/alerts/server.py @@ -5,6 +5,7 @@ from datetime import datetime, timedelta from helpers.os_environment import resolve_external_file_path from helpers.alert_manager import AlertProvider from baps_types.alert import CRITICAL, WARNING, Alert +from baps_types.happytime import happytime MODULE = "BAPSicleServer" # This should match the log file, so the UI will link to the logs page. @@ -66,7 +67,7 @@ class ServerAlertProvider(AlertProvider): It may have been automatically restarted by the OS. If this is not expected, please check logs to investigate why BAPSicle restarted/crashed.""" - .format(str(start_time).rsplit(".",1)[0]), + .format(happytime(start_time)), "module": MODULE, "severity": WARNING })] diff --git a/baps_types/happytime.py b/baps_types/happytime.py new file mode 100644 index 0000000..898730e --- /dev/null +++ b/baps_types/happytime.py @@ -0,0 +1,3 @@ +from datetime import datetime +def happytime(date: datetime): + return date.strftime("%Y-%m-%d %H:%M:%S") diff --git a/ui-templates/alerts.html b/ui-templates/alerts.html index 36e5fdd..c90fd60 100644 --- a/ui-templates/alerts.html +++ b/ui-templates/alerts.html @@ -5,14 +5,10 @@ {% endblock %} {% block content_inner %} {% if data %} -

Current Alerts: {{ data.alert_count_current }}

-
- {{ alert_list(data.alerts_current) }} -
+

Current Alerts: {{ data.alerts_current | length }}

+ {{ alert_list(data.alerts_current) }}
-

Previous Alerts: {{ data.alert_count_previous }}

-
- {{ alert_list(data.alerts_previous) }} -
+

Previous Alerts: {{ data.alerts_previous | length }}

+ {{ alert_list(data.alerts_previous) }} {% endif %} {% endblock %} diff --git a/ui-templates/parts/alert_list.html b/ui-templates/parts/alert_list.html index 6c8ae48..c559d18 100644 --- a/ui-templates/parts/alert_list.html +++ b/ui-templates/parts/alert_list.html @@ -2,21 +2,17 @@ {% for alert in alerts %}
-

- - {{ alert.severity }} -

- Since {{ alert.start_time }} - Last Seen {{ alert.last_time }} + {{ alert.severity }} +

{{ alert.title }}

+ Since {{ alert.start_time | happytime }} + Last Seen {{ alert.last_time | happytime }} {% if alert.end_time %} - Ended {{ alert.end_time }} + Ended {{ alert.end_time | happytime }} {% endif %}
-
+
Module: {{ alert.module }} {% autoescape false %}

{{ alert.description | replace("\n\n", "

") | replace("\n", "
")}}

diff --git a/web_server.py b/web_server.py index 08252a7..b0187a2 100644 --- a/web_server.py +++ b/web_server.py @@ -28,6 +28,7 @@ from helpers.normalisation import get_normalised_filename_if_available from helpers.myradio_api import MyRadioAPI from helpers.alert_manager import AlertManager import package +from baps_types.happytime import happytime env = Environment( loader=FileSystemLoader("%s/ui-templates/" % os.path.dirname(__file__)), @@ -95,6 +96,11 @@ def render_template(file, data, status=200): return html(html_content, status=status) +def _filter_happytime(date): + return happytime(date) + +env.filters["happytime"] = _filter_happytime + logger: LoggingManager server_state: StateManager api: MyRadioAPI From a172d03f0efe3254a28d36afb989da9e47f49d98 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Wed, 22 Sep 2021 19:49:24 +0100 Subject: [PATCH 12/14] pep8 --- alerts/dummy.py | 26 ++++---- alerts/player.py | 136 ++++++++++++++++++++------------------- alerts/server.py | 100 ++++++++++++++-------------- baps_types/alert.py | 61 +++++++++--------- baps_types/happytime.py | 2 + helpers/alert_manager.py | 120 +++++++++++++++++----------------- web_server.py | 13 ++-- 7 files changed, 237 insertions(+), 221 deletions(-) diff --git a/alerts/dummy.py b/alerts/dummy.py index 92cd898..7dacca3 100644 --- a/alerts/dummy.py +++ b/alerts/dummy.py @@ -2,17 +2,19 @@ from helpers.alert_manager import AlertProvider from package import BETA from baps_types.alert import WARNING, Alert # Dummy alert provider for testing basics like UI without needing to actually cause errors. + + class DummyAlertProvider(AlertProvider): - def get_alerts(self): - if BETA: - return [Alert( - { - "start_time": -1, - "id": "test", - "title": "BAPSicle is in Debug Mode", - "description": "This is a test alert. It will not appear on production builds.", - "module": "Test", - "severity": WARNING - } - )] + def get_alerts(self): + if BETA: + return [Alert( + { + "start_time": -1, + "id": "test", + "title": "BAPSicle is in Debug Mode", + "description": "This is a test alert. It will not appear on production builds.", + "module": "Test", + "severity": WARNING + } + )] diff --git a/alerts/player.py b/alerts/player.py index ff1b437..1c9b70b 100644 --- a/alerts/player.py +++ b/alerts/player.py @@ -7,88 +7,90 @@ from helpers.alert_manager import AlertProvider from baps_types.alert import CRITICAL, WARNING, Alert from baps_types.happytime import happytime -MODULE = "Player" # This should match the log file, so the UI will link to the logs page. +MODULE = "Player" # This should match the log file, so the UI will link to the logs page. + class PlayerAlertProvider(AlertProvider): - _server_state: Dict[str, Any] - _states: List[Optional[Dict[str,Any]]] = [] - _player_count: int + _server_state: Dict[str, Any] + _states: List[Optional[Dict[str, Any]]] = [] + _player_count: int - def __init__(self): - # Player count only changes after server restart, may as well just load this once. - with open(resolve_external_file_path("state/BAPSicleServer.json")) as file: - self._server_state = json.loads(file.read()) + def __init__(self): + # Player count only changes after server restart, may as well just load this once. + with open(resolve_external_file_path("state/BAPSicleServer.json")) as file: + self._server_state = json.loads(file.read()) - self._player_count = int(self._server_state["num_channels"]) - self._states = [None] * self._player_count + self._player_count = int(self._server_state["num_channels"]) + self._states = [None] * self._player_count - # To simplify monitoring (and allow detection of things going super weird), we are going to read from the state file to work out the alerts. - def get_alerts(self): - for channel in range(self._player_count): - with open(resolve_external_file_path("state/Player{}.json".format(channel))) as file: - self._states[channel] = json.loads(file.read()) + # To simplify monitoring (and allow detection of things going super + # weird), we are going to read from the state file to work out the alerts. + def get_alerts(self): + for channel in range(self._player_count): + with open(resolve_external_file_path("state/Player{}.json".format(channel))) as file: + self._states[channel] = json.loads(file.read()) - funcs = [self._channel_count, self._initialised, self._start_time] + funcs = [self._channel_count, self._initialised, self._start_time] - alerts: List[Alert] = [] + alerts: List[Alert] = [] - for func in funcs: - func_alerts = func() - if func_alerts: - alerts.extend(func_alerts) + for func in funcs: + func_alerts = func() + if func_alerts: + alerts.extend(func_alerts) - return alerts + return alerts - def _channel_count(self): - if self._player_count <= 0: - return [Alert({ - "start_time": -1, # Now - "id": "no_channels", - "title": "There are no players configured.", - "description": "The number of channels configured is {}. Please set to at least 1 on the 'Server Config' page.".format(self._player_count), - "module": MODULE+"Handler", - "severity": CRITICAL - })] + def _channel_count(self): + if self._player_count <= 0: + return [Alert({ + "start_time": -1, # Now + "id": "no_channels", + "title": "There are no players configured.", + "description": "The number of channels configured is {}. Please set to at least 1 on the 'Server Config' page.".format(self._player_count), + "module": MODULE+"Handler", + "severity": CRITICAL + })] - def _initialised(self): - alerts: List[Alert] = [] - for channel in range(self._player_count): - if self._states[channel] and not self._states[channel]["initialised"]: - alerts.append(Alert({ - "start_time": -1, # Now - "id": "player_{}_not_initialised".format(channel), - "title": "Player {} is not initialised.".format(channel), - "description": "This typically means the player channel was not able find the configured sound output on the system. Please check the 'Player Config' and Player logs to determine the cause.", - "module": MODULE+str(channel), - "severity": CRITICAL - })) - return alerts + def _initialised(self): + alerts: List[Alert] = [] + for channel in range(self._player_count): + if self._states[channel] and not self._states[channel]["initialised"]: + alerts.append(Alert({ + "start_time": -1, # Now + "id": "player_{}_not_initialised".format(channel), + "title": "Player {} is not initialised.".format(channel), + "description": "This typically means the player channel was not able find the configured sound output on the system. Please check the 'Player Config' and Player logs to determine the cause.", + "module": MODULE+str(channel), + "severity": CRITICAL + })) + return alerts - def _start_time(self): - server_start_time = self._server_state["start_time"] - server_start_time = datetime.fromtimestamp(server_start_time) - delta = timedelta( - seconds=30, - ) + def _start_time(self): + server_start_time = self._server_state["start_time"] + server_start_time = datetime.fromtimestamp(server_start_time) + delta = timedelta( + seconds=30, + ) - alerts: List[Alert] = [] - for channel in range(self._player_count): - start_time = self._states[channel]["start_time"] - start_time = datetime.fromtimestamp(start_time) - if (start_time > server_start_time + delta): - alerts.append(Alert({ - "start_time": -1, - "id": "player_{}_restarted".format(channel), - "title": "Player {} restarted after the server started.".format(channel), - "description": -"""Player {} last restarted at {}, after the server first started at {}, suggesting a failure. + alerts: List[Alert] = [] + for channel in range(self._player_count): + start_time = self._states[channel]["start_time"] + start_time = datetime.fromtimestamp(start_time) + if (start_time > server_start_time + delta): + alerts.append(Alert({ + "start_time": -1, + "id": "player_{}_restarted".format(channel), + "title": "Player {} restarted after the server started.".format(channel), + "description": + """Player {} last restarted at {}, after the server first started at {}, suggesting a failure. This likely means there was an unhandled exception in the player code, causing the server to restart the player. Please check player logs to investigate the cause. Please restart the server to clear this warning.""" - .format(channel, happytime(start_time), happytime(server_start_time)), - "module": MODULE+str(channel), - "severity": WARNING - })) - return alerts + .format(channel, happytime(start_time), happytime(server_start_time)), + "module": MODULE+str(channel), + "severity": WARNING + })) + return alerts diff --git a/alerts/server.py b/alerts/server.py index 375f87d..6a07d06 100644 --- a/alerts/server.py +++ b/alerts/server.py @@ -7,67 +7,69 @@ from helpers.alert_manager import AlertProvider from baps_types.alert import CRITICAL, WARNING, Alert from baps_types.happytime import happytime -MODULE = "BAPSicleServer" # This should match the log file, so the UI will link to the logs page. +MODULE = "BAPSicleServer" # This should match the log file, so the UI will link to the logs page. + class ServerAlertProvider(AlertProvider): - _state: Dict[str, Any] - # To simplify monitoring (and allow detection of things going super weird), we are going to read from the state file to work out the alerts. - def get_alerts(self): - with open(resolve_external_file_path("state/BAPSicleServer.json")) as file: - self._state = json.loads(file.read()) + _state: Dict[str, Any] + # To simplify monitoring (and allow detection of things going super + # weird), we are going to read from the state file to work out the alerts. + def get_alerts(self): + with open(resolve_external_file_path("state/BAPSicleServer.json")) as file: + self._state = json.loads(file.read()) - funcs = [self._api_key, self._start_time] + funcs = [self._api_key, self._start_time] - alerts: List[Alert] = [] + alerts: List[Alert] = [] - for func in funcs: - func_alerts = func() - if func_alerts: - alerts.extend(func_alerts) + for func in funcs: + func_alerts = func() + if func_alerts: + alerts.extend(func_alerts) - return alerts + return alerts - def _api_key(self): - if not self._state["myradio_api_key"]: - return [Alert({ - "start_time": -1, # Now - "id": "api_key_missing", - "title": "MyRadio API Key is not configured.", - "description": "This means you will be unable to load show plans, audio items, or tracklist. Please set one on the 'Server Config' page.", - "module": MODULE, - "severity": CRITICAL - })] + def _api_key(self): + if not self._state["myradio_api_key"]: + return [Alert({ + "start_time": -1, # Now + "id": "api_key_missing", + "title": "MyRadio API Key is not configured.", + "description": "This means you will be unable to load show plans, audio items, or tracklist. Please set one on the 'Server Config' page.", + "module": MODULE, + "severity": CRITICAL + })] - if len(self._state["myradio_api_key"]) < 10: - return [Alert({ - "start_time": -1, - "id": "api_key_missing", - "title": "MyRadio API Key seems incorrect.", - "description": "The API key is less than 10 characters, it's probably not a valid one. If it is valid, it shouldn't be.", - "module": MODULE, - "severity": WARNING - })] + if len(self._state["myradio_api_key"]) < 10: + return [Alert({ + "start_time": -1, + "id": "api_key_missing", + "title": "MyRadio API Key seems incorrect.", + "description": "The API key is less than 10 characters, it's probably not a valid one. If it is valid, it shouldn't be.", + "module": MODULE, + "severity": WARNING + })] - def _start_time(self): - start_time = self._state["start_time"] - start_time = datetime.fromtimestamp(start_time) - delta = timedelta( - days=1, - ) - if (start_time + delta > datetime.now()): - return [Alert({ - "start_time": -1, - "id": "server_restarted", - "title": "BAPSicle restarted recently.", - "description": -"""The BAPSicle server restarted at {}, less than a day ago. + def _start_time(self): + start_time = self._state["start_time"] + start_time = datetime.fromtimestamp(start_time) + delta = timedelta( + days=1, + ) + if (start_time + delta > datetime.now()): + return [Alert({ + "start_time": -1, + "id": "server_restarted", + "title": "BAPSicle restarted recently.", + "description": + """The BAPSicle server restarted at {}, less than a day ago. It may have been automatically restarted by the OS. If this is not expected, please check logs to investigate why BAPSicle restarted/crashed.""" - .format(happytime(start_time)), - "module": MODULE, - "severity": WARNING - })] + .format(happytime(start_time)), + "module": MODULE, + "severity": WARNING + })] diff --git a/baps_types/alert.py b/baps_types/alert.py index f6958fc..05e792a 100644 --- a/baps_types/alert.py +++ b/baps_types/alert.py @@ -4,6 +4,7 @@ from datetime import datetime CRITICAL = "Critical" WARNING = "Warning" + class Alert: start_time: datetime last_time: datetime @@ -14,56 +15,54 @@ class Alert: module: str severity: str - @property def ui_class(self) -> str: - if self.severity == CRITICAL: - return "danger" - if self.severity == WARNING: - return "warning" - return "info" + if self.severity == CRITICAL: + return "danger" + if self.severity == WARNING: + return "warning" + return "info" # This alert has happened again. def reoccured(self): - self.last_time = datetime.now() - self.end_time = None + self.last_time = datetime.now() + self.end_time = None # This alert has finished, just update end time and keep last_time. def cleared(self): - self.end_time = datetime.now() - + self.end_time = datetime.now() @property def __dict__(self): attrs = ["start_time", "last_time", "end_time", "id", "title", "description", "module", "severity"] out = {} for attr in attrs: - out[attr] = self.__getattribute__(attr) + out[attr] = self.__getattribute__(attr) return out - def __init__(self, new_data: Dict[str,Any]): - required_vars = [ - "start_time", # Just in case an alert wants to show starting earlier than it is reported. - "id", - "title", - "description", - "module", - "severity" - ] + def __init__(self, new_data: Dict[str, Any]): + required_vars = [ + "start_time", # Just in case an alert wants to show starting earlier than it is reported. + "id", + "title", + "description", + "module", + "severity" + ] - for key in required_vars: - if key not in new_data.keys(): - raise KeyError("Key {} is missing from data to create Alert.".format(key)) + for key in required_vars: + if key not in new_data.keys(): + raise KeyError("Key {} is missing from data to create Alert.".format(key)) - #if type(new_data[key]) != type(getattr(self,key)): - # raise TypeError("Key {} has type {}, was expecting {}.".format(key, type(new_data[key]), type(getattr(self,key)))) + # if type(new_data[key]) != type(getattr(self,key)): + # raise TypeError("Key {} has type {}, was expecting {}.".format(key, type(new_data[key]), type(getattr(self,key)))) - # Account for if the creator didn't want to set a custom time. - if key == "start_time" and new_data[key] == -1: - new_data[key] = datetime.now() + # Account for if the creator didn't want to set a custom time. + if key == "start_time" and new_data[key] == -1: + new_data[key] = datetime.now() - setattr(self,key,new_data[key]) + setattr(self, key, new_data[key]) - self.last_time = self.start_time - self.end_time = None + self.last_time = self.start_time + self.end_time = None diff --git a/baps_types/happytime.py b/baps_types/happytime.py index 898730e..9fb6426 100644 --- a/baps_types/happytime.py +++ b/baps_types/happytime.py @@ -1,3 +1,5 @@ from datetime import datetime + + def happytime(date: datetime): return date.strftime("%Y-%m-%d %H:%M:%S") diff --git a/helpers/alert_manager.py b/helpers/alert_manager.py index ab9c476..f2ca4fe 100644 --- a/helpers/alert_manager.py +++ b/helpers/alert_manager.py @@ -1,14 +1,15 @@ from typing import Any, List, Optional -#Magic for importing alert providers from alerts directory. +# Magic for importing alert providers from alerts directory. from pkgutil import iter_modules from importlib import import_module -from inspect import getmembers,isclass +from inspect import getmembers, isclass from sys import modules from baps_types.alert import CRITICAL, Alert import alerts + def iter_namespace(ns_pkg): # Specifying the second argument (prefix) to iter_modules makes the # returned name an absolute name instead of a relative one. This allows @@ -19,75 +20,78 @@ def iter_namespace(ns_pkg): class AlertProvider(): - def __init__(self): - return None + def __init__(self): + return None + + def get_alerts(self): + return [] - def get_alerts(self): - return [] class AlertManager(): - _alerts: List[Alert] - _providers: List[AlertProvider] = [] + _alerts: List[Alert] + _providers: List[AlertProvider] = [] - def __init__(self): - self._alerts = [] + def __init__(self): + self._alerts = [] - # Find all the alert providers from the /alerts/ directory. - providers = { - name: import_module(name) - for _, name, _ - in iter_namespace(alerts) - } + # Find all the alert providers from the /alerts/ directory. + providers = { + name: import_module(name) + for _, name, _ + in iter_namespace(alerts) + } - for provider in providers: - classes: List[Any] = [mem[1] for mem in getmembers(modules[provider], isclass) if mem[1].__module__ == modules[provider].__name__] + for provider in providers: + classes: List[Any] = [ + mem[1] for mem in getmembers( + modules[provider], + isclass) if mem[1].__module__ == modules[provider].__name__] - if (len(classes) != 1): - print(classes) - raise Exception("Can't import plugin " + provider + " because it doesn't have 1 class.") + if (len(classes) != 1): + print(classes) + raise Exception("Can't import plugin " + provider + " because it doesn't have 1 class.") - self._providers.append(classes[0]()) + self._providers.append(classes[0]()) + print("Discovered alert providers: ", self._providers) - print("Discovered alert providers: ", self._providers) + def poll_alerts(self): - def poll_alerts(self): + # Poll modules for any alerts. + new_alerts: List[Optional[Alert]] = [] + for provider in self._providers: + provider_alerts = provider.get_alerts() + if provider_alerts: + new_alerts.extend(provider_alerts) - # Poll modules for any alerts. - new_alerts: List[Optional[Alert]] = [] - for provider in self._providers: - provider_alerts = provider.get_alerts() - if provider_alerts: - new_alerts.extend(provider_alerts) + # Here we replace new firing alerts with older ones, to keep any context. + # (This doesn't do anything yet really, for future use.) + for existing in self._alerts: + found = False + for new in new_alerts: + # given we're removing alerts, got to skip any we removed. + if not new: + continue - # Here we replace new firing alerts with older ones, to keep any context. - # (This doesn't do anything yet really, for future use.) - for existing in self._alerts: - found = False - for new in new_alerts: - # given we're removing alerts, got to skip any we removed. - if not new: - continue + if existing.id == new.id: + # Alert is continuing. Replace it with the old one. + index = new_alerts.index(new) + existing.reoccured() + new_alerts[index] = None # We're going to merge the existing and new, so clear the new one out. + found = True + break + if not found: + # The existing alert is gone, mark it as ended. + existing.cleared() - if existing.id == new.id: - # Alert is continuing. Replace it with the old one. - index = new_alerts.index(new) - existing.reoccured() - new_alerts[index] = None # We're going to merge the existing and new, so clear the new one out. - found = True - break - if found == False: - # The existing alert is gone, mark it as ended. - existing.cleared() + self._alerts.extend([value for value in new_alerts if value]) # Remove any nulled out new alerts - self._alerts.extend([value for value in new_alerts if value]) # Remove any nulled out new alerts + @property + def alerts_current(self): + self.poll_alerts() + return [alert for alert in self._alerts if not alert.end_time] - @property - def alerts_current(self): - self.poll_alerts() - return [alert for alert in self._alerts if not alert.end_time] - - @property - def alerts_previous(self): - self.poll_alerts() - return [alert for alert in self._alerts if alert.end_time] + @property + def alerts_previous(self): + self.poll_alerts() + return [alert for alert in self._alerts if alert.end_time] diff --git a/web_server.py b/web_server.py index b0187a2..1ba3940 100644 --- a/web_server.py +++ b/web_server.py @@ -99,6 +99,7 @@ def render_template(file, data, status=200): def _filter_happytime(date): return happytime(date) + env.filters["happytime"] = _filter_happytime logger: LoggingManager @@ -114,14 +115,17 @@ player_from_q: List[Queue] = [] @app.exception(NotFound) def page_not_found(request, e: Any): - data = {"ui_page": "404", "ui_title": "404", "code": 404, "title": "Page Not Found", "message": "Looks like you fell off the tip of the iceberg." } + data = {"ui_page": "404", "ui_title": "404", "code": 404, "title": "Page Not Found", + "message": "Looks like you fell off the tip of the iceberg."} return render_template("error.html", data=data, status=404) -@app.exception(Exception, ServerError) -def server_error(request, e: Exception): - data = {"ui_page": "500", "ui_title": "500", "code": 500, "title": "Something went very wrong!", "message": "Looks like the server fell over. Try viewing the WebServer logs for more details." } +# Future use. +def error_page(code=500, ui_title="500", title="Something went very wrong!", + message="Looks like the server fell over. Try viewing the WebServer logs for more details."): + data = {"ui_page": ui_title, "ui_title": ui_title, "code": code, "title": title, "message": message} return render_template("error.html", data=data, status=500) + @app.route("/") def ui_index(request): config = server_state.get() @@ -148,6 +152,7 @@ def ui_status(request): "ui_page": "status", "ui_title": "Status"} return render_template("status.html", data=data) + @app.route("/alerts") def ui_alerts(request): data = { From 0cdfd7b2fe2e75ce0b6e2021cb47254f3f8f83e5 Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Wed, 22 Sep 2021 20:14:58 +0100 Subject: [PATCH 13/14] Fix linting --- alerts/player.py | 7 +++++-- alerts/server.py | 6 ++++-- baps_types/alert.py | 3 ++- helpers/alert_manager.py | 2 +- package.json | 2 +- web_server.py | 3 ++- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/alerts/player.py b/alerts/player.py index 1c9b70b..9ed9da8 100644 --- a/alerts/player.py +++ b/alerts/player.py @@ -48,7 +48,9 @@ class PlayerAlertProvider(AlertProvider): "start_time": -1, # Now "id": "no_channels", "title": "There are no players configured.", - "description": "The number of channels configured is {}. Please set to at least 1 on the 'Server Config' page.".format(self._player_count), + "description": "The number of channels configured is {}. \ + Please set to at least 1 on the 'Server Config' page." + .format(self._player_count), "module": MODULE+"Handler", "severity": CRITICAL })] @@ -61,7 +63,8 @@ class PlayerAlertProvider(AlertProvider): "start_time": -1, # Now "id": "player_{}_not_initialised".format(channel), "title": "Player {} is not initialised.".format(channel), - "description": "This typically means the player channel was not able find the configured sound output on the system. Please check the 'Player Config' and Player logs to determine the cause.", + "description": "This typically means the player channel was not able find the configured sound output \ + on the system. Please check the 'Player Config' and Player logs to determine the cause.", "module": MODULE+str(channel), "severity": CRITICAL })) diff --git a/alerts/server.py b/alerts/server.py index 6a07d06..d89e186 100644 --- a/alerts/server.py +++ b/alerts/server.py @@ -37,7 +37,8 @@ class ServerAlertProvider(AlertProvider): "start_time": -1, # Now "id": "api_key_missing", "title": "MyRadio API Key is not configured.", - "description": "This means you will be unable to load show plans, audio items, or tracklist. Please set one on the 'Server Config' page.", + "description": "This means you will be unable to load show plans, audio items, or tracklist. \ + Please set one on the 'Server Config' page.", "module": MODULE, "severity": CRITICAL })] @@ -47,7 +48,8 @@ class ServerAlertProvider(AlertProvider): "start_time": -1, "id": "api_key_missing", "title": "MyRadio API Key seems incorrect.", - "description": "The API key is less than 10 characters, it's probably not a valid one. If it is valid, it shouldn't be.", + "description": "The API key is less than 10 characters, it's probably not a valid one. \ + If it is valid, it shouldn't be.", "module": MODULE, "severity": WARNING })] diff --git a/baps_types/alert.py b/baps_types/alert.py index 05e792a..33ddd76 100644 --- a/baps_types/alert.py +++ b/baps_types/alert.py @@ -56,7 +56,8 @@ class Alert: raise KeyError("Key {} is missing from data to create Alert.".format(key)) # if type(new_data[key]) != type(getattr(self,key)): - # raise TypeError("Key {} has type {}, was expecting {}.".format(key, type(new_data[key]), type(getattr(self,key)))) + # raise TypeError("Key {} has type {}, was expecting {}." + # .format(key, type(new_data[key]), type(getattr(self,key)))) # Account for if the creator didn't want to set a custom time. if key == "start_time" and new_data[key] == -1: diff --git a/helpers/alert_manager.py b/helpers/alert_manager.py index f2ca4fe..53cdaea 100644 --- a/helpers/alert_manager.py +++ b/helpers/alert_manager.py @@ -6,7 +6,7 @@ from importlib import import_module from inspect import getmembers, isclass from sys import modules -from baps_types.alert import CRITICAL, Alert +from baps_types.alert import Alert import alerts diff --git a/package.json b/package.json index f4fb81f..cae6ea8 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "presenter-make": "npm run presenter-install && (rm -r presenter-build || true) && cd presenter && yarn build-baps && cp -r build ../presenter-build && cd ../ && npm install", "test": "echo \"Error: no test specified\" && exit 1", "presenter-start": "cd presenter && yarn start-baps", - "lint": "autopep8 -r -a -a --ignore E402,E226,E24,W50,W690 --max-line-length 127 --in-place --exclude=\"*node_modules*,*venv/*,presenter/*\" . " + "lint": "./venv/bin/autopep8 -r -a -a --ignore E402,E226,E24,W50,W690 --max-line-length 127 --in-place --exclude=\"*node_modules*,*venv/*,presenter/*\" . && ./venv/bin/flake8 . --exclude=\"*node_modules*,*venv/*,presenter/*\" --count --ignore=E402,E226,E24,W50,W690 --max-complexity=25 --max-line-length=127 --statistics" }, "repository": { "type": "git", diff --git a/web_server.py b/web_server.py index 1ba3940..142db8a 100644 --- a/web_server.py +++ b/web_server.py @@ -1,5 +1,5 @@ from sanic import Sanic -from sanic.exceptions import NotFound, ServerError, abort +from sanic.exceptions import NotFound, abort from sanic.response import html, file, redirect from sanic.response import json as resp_json from sanic_cors import CORS @@ -119,6 +119,7 @@ def page_not_found(request, e: Any): "message": "Looks like you fell off the tip of the iceberg."} return render_template("error.html", data=data, status=404) + # Future use. def error_page(code=500, ui_title="500", title="Something went very wrong!", message="Looks like the server fell over. Try viewing the WebServer logs for more details."): From fe562ac60f50e51b612dce32cf9a83e0ef63357a Mon Sep 17 00:00:00 2001 From: Matthew Stratford Date: Wed, 22 Sep 2021 20:31:28 +0100 Subject: [PATCH 14/14] Correct comment --- helpers/alert_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/alert_manager.py b/helpers/alert_manager.py index 53cdaea..b2e2775 100644 --- a/helpers/alert_manager.py +++ b/helpers/alert_manager.py @@ -65,7 +65,7 @@ class AlertManager(): new_alerts.extend(provider_alerts) # Here we replace new firing alerts with older ones, to keep any context. - # (This doesn't do anything yet really, for future use.) + # (This doesn't do much yet really, just remembering the start_time) for existing in self._alerts: found = False for new in new_alerts: