-
-
Notifications
You must be signed in to change notification settings - Fork 30
Error Surfacing #1091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: VersionWarning
Are you sure you want to change the base?
Error Surfacing #1091
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ | |
| from types import SimpleNamespace | ||
| from enum import Enum | ||
| from pathlib import Path | ||
| import datetime | ||
|
|
||
| # pylint: disable=no-name-in-module | ||
| from pydantic import BaseSettings, BaseModel, Field | ||
|
|
@@ -1004,6 +1005,28 @@ class FirmwareInfo(BaseModel): | |
| git_dirty: bool = Field(default=False, description="True if local changes were made. Used for development.") | ||
|
|
||
|
|
||
| class AlertLevel(Enum): | ||
| WARNING = "warning" | ||
| ERROR = "error" | ||
| INFO = "info" | ||
| SUCCESS = "success" | ||
|
|
||
|
|
||
| class Alert(BaseModel): | ||
| message: str | ||
| severity: AlertLevel = AlertLevel.ERROR | ||
| """What color should the alert be as per the Mui style guide: https://mui.com/material-ui/react-alert/#severity""" | ||
|
Comment on lines
+1008
to
+1018
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docstring under the severity var would probably also make sense as a docstring for the AlertLevel |
||
| hidden: bool = False | ||
| """Has this Alert been hidden by the user?""" | ||
| timestamp: datetime.datetime = Field( | ||
| default_factory=lambda: datetime.datetime.now(datetime.timezone.utc) | ||
| ) | ||
|
|
||
| @property | ||
| def expired(self) -> bool: # Used to limit alerts to have only a single instance per week. If the state that caused the alert is still valid after a week, the same alert will be made. | ||
| return (datetime.datetime.now(datetime.timezone.utc) - self.timestamp) > datetime.timedelta(weeks=1) | ||
|
|
||
|
|
||
| class Info(BaseModel): | ||
| """ AmpliPi System information """ | ||
| version: str = Field(description="software version") | ||
|
|
@@ -1024,6 +1047,7 @@ class Info(BaseModel): | |
| default=[], description='The stream types available on this particular appliance') | ||
| extra_fields: Optional[Dict] = Field(default=None, description='Optional fields for customization') | ||
| connected_drives: List[str] = Field(default=[], description='A list of all external drives connected') | ||
| global_alerts: List[Alert] = Field(default=[], description='A list of alerts to be shown to all users via the frontend global alert bar') | ||
|
|
||
| class Config: | ||
| schema_extra = { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -495,3 +495,64 @@ def clear_custom_configs(): | |
| os.remove(path) | ||
| except Exception as e: | ||
| logger.exception(f"failed to clear device configuration: {e}") | ||
|
|
||
|
|
||
| # Every alert(s) function was in ctrl.py, but due to many files needing access to the add_alert flow they had to be here in utils | ||
|
|
||
|
|
||
| def load_alerts() -> List[models.Alert]: | ||
| alert_file = f"{get_folder('config')}/alerts.json" | ||
| try: | ||
| with open(alert_file, 'r', encoding='utf-8') as file: | ||
| data = json.load(file) | ||
|
|
||
| alerts: List[models.Alert] = [models.Alert(**item) for item in data] | ||
| for alert in alerts: | ||
| if alert.expired: | ||
| alert.hidden = True # Frontend can't see expired property, so autohide any expired alerts as to not have to close the same alert twice | ||
| return alerts | ||
|
|
||
| except (FileNotFoundError, json.JSONDecodeError): | ||
| return [] | ||
|
|
||
| except Exception as e: | ||
| logger.exception(e) | ||
| return [] | ||
|
|
||
|
|
||
| def add_alert(message: str, severity: models.AlertLevel = models.AlertLevel.ERROR): | ||
| alerts = load_alerts() | ||
| search = [alert for alert in alerts if alert.message == message and not alert.expired] | ||
| if len(search) == 0: | ||
| alert = models.Alert(message=message, severity=severity) | ||
| alerts.append(alert) | ||
| save_alerts(alerts) | ||
|
|
||
|
|
||
| def hide_alert(alert: models.Alert): | ||
| alerts = load_alerts() | ||
| selected_alert = next( | ||
| ( | ||
| item for item in alerts | ||
| if item.message == alert.message and not item.expired | ||
| ), | ||
| None | ||
| ) | ||
| if selected_alert is not None: | ||
| selected_alert.hidden = True | ||
| save_alerts(alerts) | ||
| else: | ||
| add_alert("Alert not found, could not be hidden!") | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably makes sense as an Exception as well |
||
|
|
||
|
|
||
| def save_alerts(alerts: List[models.Alert]): | ||
| alert_file = f"{get_folder('config')}/alerts.json" | ||
| try: | ||
| with open(alert_file, 'w', encoding='utf-8') as file: | ||
| json.dump( | ||
| [json.loads(alert.json()) for alert in alerts], | ||
| file, | ||
| indent=2 | ||
| ) | ||
| except Exception as e: | ||
| logger.exception(e) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MUI alert component that we use has a "severity" property that has 4 possible values - error, success, warning, and info. I have had no particular use for warning and info thus far, so I flattened it into just success or error. Within that, I generally only needed error so I had it be a Luckily the other two AlertBar style components we use don't have the same controls so I didn't need to upkeep those for full feature parity in this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pathing might need some work. It made more sense when I had multiple alert endpoints being added here but I found 2/3 of the endpoints didn't make sense as I kept devving on this