Refactored reminder_handler.py

This commit is contained in:
CasVT
2025-08-21 17:53:31 +02:00
parent 61882e609a
commit a369e80ab3
4 changed files with 49 additions and 48 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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)