From a369e80ab34ef6907c8e6fdd1c2cdc641e3fc317 Mon Sep 17 00:00:00 2001 From: CasVT Date: Thu, 21 Aug 2025 17:53:31 +0200 Subject: [PATCH] Refactored reminder_handler.py --- MIND.py | 5 +- backend/features/reminder_handler.py | 84 ++++++++++++++-------------- backend/features/tz_shifter.py | 2 +- backend/implementations/reminders.py | 6 +- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/MIND.py b/MIND.py index 15d92b9..adbfe85 100644 --- a/MIND.py +++ b/MIND.py @@ -89,8 +89,7 @@ def _main( settings = s.get_settings() - reminder_handler = ReminderHandler() - reminder_handler.find_next_reminder() + ReminderHandler.set_reminder_timer() DatabaseBackupHandler.set_backup_timer() @@ -106,7 +105,7 @@ def _main( # ================= finally: - reminder_handler.stop_handling() + ReminderHandler.stop_reminder_timer() DatabaseBackupHandler.stop_backup_timer() tz_change_handler.stop_detector_timer() diff --git a/backend/features/reminder_handler.py b/backend/features/reminder_handler.py index 26656df..4c24151 100644 --- a/backend/features/reminder_handler.py +++ b/backend/features/reminder_handler.py @@ -6,7 +6,7 @@ from datetime import datetime from typing import TYPE_CHECKING, Union from backend.base.definitions import Constants, RepeatQuantity, SendResult -from backend.base.helpers import Singleton, find_next_time, when_not_none +from backend.base.helpers import find_next_time, when_not_none from backend.base.logging import LOGGER from backend.implementations.reminders import Reminder from backend.internals.db_models import UserlessRemindersDB @@ -16,48 +16,43 @@ if TYPE_CHECKING: from threading import Timer -class ReminderHandler(metaclass=Singleton): +class ReminderHandler: """ - Handle set reminders. This class is a singleton. + Handling of the reminders such that they are sent at their scheduled time. """ - def __init__(self) -> None: - "Create instance of handler" - self.thread: Union[Timer, None] = None - self.time: Union[int, None] = None - self.reminder_db = UserlessRemindersDB() - return + reminder_timer: Union[Timer, None] = None + next_trigger_time: Union[int, None] = None + reminder_db = UserlessRemindersDB() - def __trigger_reminders(self, time: int) -> None: + @classmethod + def _trigger_reminders(cls, time: int) -> None: """Trigger all reminders that are set for a certain time. Args: time (int): The time of the reminders to trigger. """ - for reminder in self.reminder_db.fetch(time): + for reminder in cls.reminder_db.fetch(time): try: - user_id = self.reminder_db.reminder_id_to_user_id( + user_id = cls.reminder_db.reminder_id_to_user_id( reminder.id ) result = Reminder(user_id, reminder.id).trigger_reminder() - self.thread = None - self.time = None - if result == SendResult.CONNECTION_ERROR: # Retry sending the notification in a few minutes - self.reminder_db.update( + cls.reminder_db.shift( reminder.id, - time + Constants.CONNECTION_ERROR_TIMEOUT + Constants.CONNECTION_ERROR_TIMEOUT ) - elif ( + elif not any(( reminder.repeat_quantity, reminder.weekdays, reminder.cron_schedule - ) == (None, None, None): + )): # Delete the reminder from the database - self.reminder_db.delete(reminder.id) + cls.reminder_db.delete(reminder.id) else: # Set next time @@ -72,7 +67,7 @@ class ReminderHandler(metaclass=Singleton): reminder.cron_schedule ) - self.reminder_db.update(reminder.id, new_time) + cls.reminder_db.update(reminder.id, new_time) except Exception: # If the notification fails, we don't want to crash the whole program @@ -83,48 +78,55 @@ class ReminderHandler(metaclass=Singleton): ) finally: - self.find_next_reminder() + cls.reminder_timer = None + cls.next_trigger_time = None + cls.set_reminder_timer() return - def find_next_reminder(self, time: Union[int, None] = None) -> None: - """Determine when the soonest reminder is and set the timer to that time. + @classmethod + def set_reminder_timer(cls, time: Union[int, None] = None) -> None: + """Update the timer for sending the soonest upcoming reminder. Start one + if it hasn't already. Replace it if it does already exist, in case the + time the soonest reminder triggers has changed. Args: - time (Union[int, None], optional): The timestamp to check for. - Otherwise check soonest in database. + time (Union[int, None], optional): Check whether the given timestamp + changes anything to the situation. If not given, the soonest + timestamp in the database is checked. + Defaults to None. """ if time is None: - time = self.reminder_db.get_soonest_time() + time = cls.reminder_db.get_soonest_time() if not time: return if ( - self.thread is None + cls.reminder_timer is None or ( - self.time is not None - and time < self.time + cls.next_trigger_time is not None + and time < cls.next_trigger_time ) ): - if self.thread is not None: - self.thread.cancel() + if cls.reminder_timer is not None: + cls.reminder_timer.cancel() delta_t = time - datetime.utcnow().timestamp() - self.thread = Server().get_db_timer_thread( + cls.reminder_timer = Server().get_db_timer_thread( delta_t, - self.__trigger_reminders, + cls._trigger_reminders, "ReminderHandler", args=(time,) ) - self.thread.start() - self.time = time + cls.reminder_timer.start() + cls.next_trigger_time = time return - def stop_handling(self) -> None: - """Stop the timer if it's active - """ - if self.thread is not None: - self.thread.cancel() + @classmethod + def stop_reminder_timer(cls) -> None: + "If the reminder timer is running, stop it" + if cls.reminder_timer is not None: + cls.reminder_timer.cancel() return diff --git a/backend/features/tz_shifter.py b/backend/features/tz_shifter.py index d6eb6f0..f08f4e0 100644 --- a/backend/features/tz_shifter.py +++ b/backend/features/tz_shifter.py @@ -73,7 +73,7 @@ class TimezoneChangeHandler(metaclass=Singleton): offset=shift_delta ) settings.update({"measured_timezone": current_timezone}) - ReminderHandler().find_next_reminder() + ReminderHandler.set_reminder_timer() LOGGER.info( "Detected timezone/DST change (%s to %s), shifted reminders", measured_timezone, current_timezone diff --git a/backend/implementations/reminders.py b/backend/implementations/reminders.py index a8af777..b15bf4a 100644 --- a/backend/implementations/reminders.py +++ b/backend/implementations/reminders.py @@ -244,7 +244,7 @@ class Reminder: data["enabled"] ) - ReminderHandler().find_next_reminder(next_time) + ReminderHandler.set_reminder_timer(next_time) return self.get() def delete(self) -> None: @@ -253,7 +253,7 @@ class Reminder: LOGGER.info(f'Deleting reminder {self.id}') self.reminder_db.delete(self.id) - ReminderHandler().find_next_reminder() + ReminderHandler.set_reminder_timer() return @@ -456,7 +456,7 @@ class Reminders: enabled ) - ReminderHandler().find_next_reminder(time) + ReminderHandler.set_reminder_timer(time) return self.get_one(new_id)