04/23 Fixed bugs
This commit is contained in:
+41
-2
@@ -175,10 +175,38 @@ def _send_report(cfg: dict):
|
||||
logger.error(f"Failed to send daily report email: {e}")
|
||||
|
||||
|
||||
def _get_last_sent_date() -> "datetime.date | None":
|
||||
"""Read the last-sent date from config.ini [email] last_sent_date key."""
|
||||
cfg = configparser.ConfigParser()
|
||||
if not os.path.exists(CONFIG_FILE):
|
||||
return None
|
||||
cfg.read(CONFIG_FILE, encoding="utf-8")
|
||||
raw = cfg.get("email", "last_sent_date", fallback="")
|
||||
if not raw:
|
||||
return None
|
||||
try:
|
||||
return datetime.date.fromisoformat(raw)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _set_last_sent_date(d: "datetime.date"):
|
||||
"""Persist the last-sent date to config.ini [email] last_sent_date key."""
|
||||
cfg = configparser.ConfigParser()
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
cfg.read(CONFIG_FILE, encoding="utf-8")
|
||||
if "email" not in cfg:
|
||||
cfg["email"] = {}
|
||||
cfg["email"]["last_sent_date"] = d.isoformat()
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as fh:
|
||||
cfg.write(fh)
|
||||
|
||||
|
||||
# ─── Scheduler loop ───────────────────────────────────────────────────────────
|
||||
|
||||
def _scheduler_loop():
|
||||
last_sent_date = None
|
||||
# Seed from persisted value so a restart after send_time does not re-send.
|
||||
last_sent_date = _get_last_sent_date()
|
||||
|
||||
while not _stop_event.is_set():
|
||||
_stop_event.wait(60) # sleep 60 seconds between checks
|
||||
@@ -200,6 +228,7 @@ def _scheduler_loop():
|
||||
if (now.hour > send_h or (now.hour == send_h and now.minute >= send_m)):
|
||||
if last_sent_date != today:
|
||||
last_sent_date = today
|
||||
_set_last_sent_date(today)
|
||||
logger.info(f"Scheduler: sending daily report at {now.strftime('%H:%M')}.")
|
||||
_send_report(cfg)
|
||||
|
||||
@@ -207,7 +236,17 @@ def _scheduler_loop():
|
||||
def start():
|
||||
"""Start the background scheduler thread. Call once after successful login."""
|
||||
global _scheduler_thread, _stop_event
|
||||
_stop_event.clear()
|
||||
# Guard against double-start: if a thread is already alive (e.g. admin
|
||||
# logs out and back in), stop it cleanly before spawning a new one.
|
||||
# Without this guard, _stop_event.clear() would unblock the sleeping
|
||||
# thread while a second thread also starts, resulting in two scheduler
|
||||
# threads firing simultaneously and potentially sending duplicate emails.
|
||||
if _scheduler_thread is not None and _scheduler_thread.is_alive():
|
||||
logger.info("Email scheduler already running - stopping before restart.")
|
||||
_stop_event.set()
|
||||
_scheduler_thread.join(timeout=5)
|
||||
# Create a fresh Event so there is no residual set-state from a prior stop()
|
||||
_stop_event = threading.Event()
|
||||
_scheduler_thread = threading.Thread(target=_scheduler_loop,
|
||||
name="EmailScheduler", daemon=True)
|
||||
_scheduler_thread.start()
|
||||
|
||||
Reference in New Issue
Block a user