from datetime import date def months_before(d, n): """Return a date on the 1st of the month n months before d.""" m = (d.month - 1 - n) % 12 + 1 y = d.year + (d.month - 1 - n) // 12 return date(y, m, 1) def pruning_status(plant, today=None): """ Returns one of: 'overdue', 'due_this_month', 'upcoming', 'no_schedule'. A scheduled month is 'overdue' only if its most recent occurrence is within the past 6 months and was not logged. Beyond 6 months, the next occurrence is sooner than the missed one, so it is 'upcoming'. """ if today is None: today = date.today() months = plant.pruning_months or [] if not months: return 'no_schedule' logs = list(plant.pruning_logs.all()) if today.month in months: if not any(l.pruned_on.year == today.year and l.pruned_on.month == today.month for l in logs): return 'due_this_month' return 'upcoming' for i in range(1, 13): past = months_before(today, i) if past.month in months: if i >= 6: return 'upcoming' # Any log on or after the start of the scheduled month clears overdue cutoff = date(past.year, past.month, 1) if any(l.pruned_on >= cutoff for l in logs): return 'upcoming' return 'overdue' return 'upcoming'