Jul 27 - Update code for scheduled tasks 2
This commit is contained in:
@@ -17,6 +17,12 @@ POST /scheduled-inspections/run?token=DIGEST_SECRET:
|
||||
- overdue alert to admin/director once the due date passes uncompleted
|
||||
The *_notified flags make each of those fire at most once per occurrence and
|
||||
reset when a recurring schedule rolls forward.
|
||||
|
||||
Two dates, deliberately distinct (phase44):
|
||||
next_due_date — mutable state. The next occurrence. Rewritten by fulfill()
|
||||
after every completed inspection.
|
||||
end_date — fixed boundary. The last date an occurrence may fall on,
|
||||
set by the manager and never rewritten. NULL = forever.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
@@ -85,6 +91,11 @@ class ScheduledInspection(db.Model):
|
||||
nullable=True, index=True)
|
||||
frequency = db.Column(db.Enum(*FREQUENCY_CHOICES), nullable=False, default='once')
|
||||
next_due_date = db.Column(db.Date, nullable=False, index=True)
|
||||
# Fixed boundary set by the manager, never rewritten by the app — unlike
|
||||
# next_due_date, which fulfill() advances after every completed inspection.
|
||||
# NULL = repeat indefinitely. Only meaningful for recurring schedules; the
|
||||
# create/edit routes force it to NULL when frequency == 'once'.
|
||||
end_date = db.Column(db.Date, nullable=True)
|
||||
active = db.Column(db.Boolean, nullable=False, default=True)
|
||||
notes = db.Column(db.Text, nullable=True)
|
||||
|
||||
@@ -227,10 +238,48 @@ class ScheduledInspection(db.Model):
|
||||
today = today or now_eastern().date()
|
||||
return self.active and self.next_due_date < today
|
||||
|
||||
# ── End-date boundary (phase44) ──────────────────────────────────────────
|
||||
|
||||
def is_within_end_date(self, d):
|
||||
"""True if date *d* is on or before the end date (inclusive).
|
||||
|
||||
No end date means the schedule repeats indefinitely, so every date
|
||||
qualifies.
|
||||
"""
|
||||
return self.end_date is None or d <= self.end_date
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
"""True once the end date has passed.
|
||||
|
||||
Independent of `active`: a schedule can be inactive because it expired
|
||||
or because a manager switched it off, and the list view distinguishes
|
||||
the two. Compare against the *end date* rather than `next_due_date`,
|
||||
which may have been advanced past the boundary by fulfill().
|
||||
"""
|
||||
if self.end_date is None:
|
||||
return False
|
||||
return self.end_date < now_eastern().date()
|
||||
|
||||
def expire_if_past_end_date(self, today=None):
|
||||
"""Deactivate a schedule whose end date has passed. Caller commits.
|
||||
|
||||
Returns True if this call changed anything. Needed because a schedule
|
||||
can reach its end date *without ever being completed* — fulfill() never
|
||||
runs, so the boundary would otherwise be checked nowhere and the cron
|
||||
would keep firing overdue alerts forever. Called from run_reminders().
|
||||
"""
|
||||
today = today or now_eastern().date()
|
||||
if self.active and self.end_date is not None and self.end_date < today:
|
||||
self.active = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def fulfill(self):
|
||||
"""Mark this occurrence complete. One-time schedules deactivate;
|
||||
recurring ones roll their due date forward past today and reset the
|
||||
reminder flags. Caller commits."""
|
||||
reminder flags. A recurring schedule whose next occurrence would fall
|
||||
past its end date deactivates instead. Caller commits."""
|
||||
self.last_completed_at = now_eastern()
|
||||
if self.frequency == 'once':
|
||||
self.active = False
|
||||
@@ -243,6 +292,12 @@ class ScheduledInspection(db.Model):
|
||||
nxt = self.next_occurrence_after(nxt)
|
||||
guard += 1
|
||||
self.next_due_date = nxt
|
||||
# Past the manager's boundary: this was the last occurrence. next_due_date
|
||||
# is left at the computed value rather than clamped, so the row still
|
||||
# shows which occurrence it stopped before.
|
||||
if not self.is_within_end_date(nxt):
|
||||
self.active = False
|
||||
return
|
||||
self.advance_notified = False
|
||||
self.due_notified = False
|
||||
self.overdue_notified = False
|
||||
|
||||
Reference in New Issue
Block a user