Runner: Kalender am Vorabend fuer den folgenden Tag pruefen
Mit next_day = 1 gelten Wochentage, Ferien und Feiertage fuer morgen (gemeinter_tag). kalender() merkt sich dafuer heute und morgen und haelt einen fehlgeschlagenen Abruf nicht mehr den ganzen Tag fest. Uhrzeit, Zeitfenster und once_per_day bleiben beim heutigen Tag. vorabend.sql legt die Spalte an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -515,9 +515,30 @@ def im_zeitfenster(jetzt, von, bis):
|
||||
NIE, EGAL, ZUSAETZLICH = 0, 1, 2
|
||||
|
||||
|
||||
def tag_passt(automatik, jetzt, kalender):
|
||||
def gemeinter_tag(automatik, jetzt):
|
||||
"""
|
||||
Faellt der heutige Tag in den Rahmen der Automatik?
|
||||
Fuer welchen Tag gelten Wochentage, Ferien und Feiertage?
|
||||
|
||||
Normalerweise fuer heute. Mit `next_day` fuer morgen - die Vorabend-Form:
|
||||
"Kinderrollos zu, wenn morgen Schule ist" heisst Mo-Fr, Ferien nie,
|
||||
Feiertage nie, und der Runner schaut dafuer auf den folgenden Tag. Mit
|
||||
dem heutigen Tag liess sich das nur annaehern (So-Do, heute keine
|
||||
Ferien), und das ging am letzten Ferientag, am Abend vor einem Feiertag
|
||||
und am Abend eines Feiertags daneben.
|
||||
|
||||
Nur der Rahmen verschiebt sich. Uhrzeit, Zeitfenster und "einmal am Tag"
|
||||
bleiben beim heutigen Tag - die Automatik laeuft ja heute Abend. Ein
|
||||
Zeitfenster ueber Mitternacht meint nach Mitternacht deshalb schon den
|
||||
uebernaechsten Tag; fuer eine Vorabend-Regel ist das kein sinnvoller Fall.
|
||||
"""
|
||||
heute = jetzt.date()
|
||||
return heute + timedelta(days=1) if automatik.get("next_day") else heute
|
||||
|
||||
|
||||
def tag_passt(automatik, tag, kalender):
|
||||
"""
|
||||
Faellt der Tag in den Rahmen der Automatik? `tag` ist heute oder morgen,
|
||||
siehe gemeinter_tag(); `kalender` gehoert zu genau diesem Tag.
|
||||
|
||||
Die Wochentage sind eine Maske, Ferien und Feiertage haben je drei
|
||||
Zustaende. Zwei davon gab es immer: EGAL (der Tag aendert nichts, die
|
||||
@@ -536,7 +557,7 @@ def tag_passt(automatik, jetzt, kalender):
|
||||
den Ferien nicht - ein Verbot schlaegt eine Erweiterung. Anders herum
|
||||
liesse sich "nie" nicht mehr verlassen.
|
||||
"""
|
||||
passt = bool(automatik["weekdays"] & (1 << jetzt.weekday()))
|
||||
passt = bool(automatik["weekdays"] & (1 << tag.weekday()))
|
||||
if kalender["feiertag"] and automatik["on_holiday"] == ZUSAETZLICH:
|
||||
passt = True
|
||||
if kalender["ferien"] and automatik["on_vacation"] == ZUSAETZLICH:
|
||||
@@ -718,7 +739,7 @@ class Runner:
|
||||
self.war_aktiv = {} # automation_id -> war im Zeitfenster
|
||||
self.lief_im_fenster = {} # automation_id -> hat im Fenster ausgeloest
|
||||
self._sonne = (None, "00:00", "00:00") # (datum, aufgang, untergang)
|
||||
self._kalender = (None, {"feiertag": False, "ferien": False})
|
||||
self._kalender = {} # datum -> {"feiertag": .., "ferien": ..}
|
||||
self._letzte_saeuberung = None
|
||||
self.reihenfolge = [] # automation_id, Ausloeser vor Nachfolger
|
||||
self.ausloeser_states = {} # automation_id -> state_id des Ausloesers
|
||||
@@ -991,21 +1012,30 @@ class Runner:
|
||||
return "%02d:%02d" % (minute // 60 % 24, minute % 60)
|
||||
return str(wert)[:5]
|
||||
|
||||
def kalender(self):
|
||||
"""Ferien und Feiertage von heute, einmal je Tag geholt."""
|
||||
heute = date.today()
|
||||
if self._kalender[0] == heute:
|
||||
return self._kalender[1]
|
||||
def kalender(self, tag):
|
||||
"""
|
||||
Ferien und Feiertage eines Tages, je Tag einmal geholt.
|
||||
|
||||
Gefragt wird nach heute und - fuer Vorabend-Regeln - nach morgen.
|
||||
Gemerkt werden deshalb beide; was vor heute liegt, fliegt raus.
|
||||
"""
|
||||
if tag in self._kalender:
|
||||
return self._kalender[tag]
|
||||
stand = {"feiertag": False, "ferien": False}
|
||||
try:
|
||||
with self.db.cursor() as c:
|
||||
c.execute("SELECT holiday, vacation FROM calendar_days WHERE date = %s", (heute,))
|
||||
c.execute("SELECT holiday, vacation FROM calendar_days WHERE date = %s", (tag,))
|
||||
zeile = c.fetchone()
|
||||
if zeile:
|
||||
stand = {"feiertag": bool(zeile["holiday"]), "ferien": bool(zeile["vacation"])}
|
||||
except Exception as fehler:
|
||||
# Nicht merken: beim naechsten Durchlauf noch einmal fragen,
|
||||
# statt den ganzen Tag mit "kein Feiertag" weiterzurechnen.
|
||||
logger.warning("Kalender nicht lesbar: %r", fehler)
|
||||
self._kalender = (heute, stand)
|
||||
return stand
|
||||
heute = date.today()
|
||||
self._kalender = {t: s for t, s in self._kalender.items() if t >= heute}
|
||||
self._kalender[tag] = stand
|
||||
return stand
|
||||
|
||||
# --- Werte -----------------------------------------------------------
|
||||
@@ -1163,13 +1193,13 @@ class Runner:
|
||||
|
||||
def durchlauf(self, fenster=NACHHOLFENSTER):
|
||||
jetzt = datetime.now()
|
||||
kalender = self.kalender()
|
||||
|
||||
for automation_id in self.reihenfolge:
|
||||
automatik = self.regelwerk.automatiken.get(automation_id)
|
||||
if automatik is None:
|
||||
continue
|
||||
aktiv = (tag_passt(automatik, jetzt, kalender)
|
||||
tag = gemeinter_tag(automatik, jetzt)
|
||||
aktiv = (tag_passt(automatik, tag, self.kalender(tag))
|
||||
and im_zeitfenster(jetzt, automatik["window_from"], automatik["window_to"]))
|
||||
vorher_aktiv = self.war_aktiv.get(automatik["id"], aktiv)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user