@@ -495,3 +495,80 @@ def clear_custom_configs():
495495 os .remove (path )
496496 except Exception as e :
497497 logger .exception (f"failed to clear device configuration: { e } " )
498+
499+
500+ # 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
501+
502+
503+ def load_alerts () -> List [models .Alert ]:
504+ alert_file = f"{ get_folder ('config' )} /alerts.json"
505+ try :
506+ with open (alert_file , 'r' , encoding = 'utf-8' ) as file :
507+ data = json .load (file )
508+
509+ alerts : List [models .Alert ] = [models .Alert (** item ) for item in data ]
510+ for alert in alerts :
511+ if alert .expired :
512+ alert .hidden = True # Frontend can't see expired property, so autohide any expired alerts as to not have to close the same alert twice
513+ return alerts
514+
515+ except (FileNotFoundError , json .JSONDecodeError ):
516+ return []
517+
518+ except Exception as e :
519+ logger .exception (e )
520+ return []
521+
522+
523+ def select_alert (message : str , alerts : Optional [List [models .Alert ]] = None ) -> Optional [models .Alert ]:
524+ """
525+ Selects the most recent non-expired instance of a specific alert message. Takes two arguments:
526+ message: the string that makes up the Alert's message
527+
528+ alerts: An optional list of alerts, for use when you want the returned alert to be a pointer to the same alert in that instance of the list.
529+ Generally useful when mutating an alert before saving the full list.
530+ """
531+ if alerts is None :
532+ alerts = load_alerts ()
533+ return next (
534+ (
535+ item for item in alerts
536+ if item .message == message and not item .expired
537+ ),
538+ None
539+ )
540+
541+
542+ def add_alert (message : str , severity : models .AlertLevel = models .AlertLevel .ERROR ) -> List [models .Alert ]:
543+ alerts = load_alerts ()
544+ search = select_alert (message )
545+ if search is None :
546+ alert = models .Alert (message = message , severity = severity )
547+ alerts .append (alert )
548+ save_alerts (alerts )
549+ return alerts # Only returns anything to make the unit test for the hide endpoint easier
550+
551+
552+ def hide_alert (message : str ) -> List [models .Alert ]:
553+ alerts = load_alerts ()
554+ selected_alert = select_alert (message , alerts )
555+ if selected_alert is not None :
556+ selected_alert .hidden = True
557+ save_alerts (alerts )
558+ else :
559+ add_alert ("Alert not found, could not be hidden!" )
560+ logger .exception ("Alert not found, could not be hidden!" )
561+ return alerts
562+
563+
564+ def save_alerts (alerts : List [models .Alert ]):
565+ alert_file = f"{ get_folder ('config' )} /alerts.json"
566+ try :
567+ with open (alert_file , 'w' , encoding = 'utf-8' ) as file :
568+ json .dump (
569+ [json .loads (alert .json ()) for alert in alerts ],
570+ file ,
571+ indent = 2
572+ )
573+ except Exception as e :
574+ logger .exception (e )
0 commit comments