June 25 - Implement reminder

This commit is contained in:
2026-06-25 16:52:19 -04:00
parent ea3648ad90
commit 2a75d17301
7 changed files with 263 additions and 1 deletions
+104
View File
@@ -1573,6 +1573,110 @@ def get_missed_shifts_today() -> list:
conn.close()
# ─── Shift Incomplete Reminders ───────────────────────────────────────────────
def get_incomplete_shift_users_near_end(window_minutes: int = 40) -> list:
"""
Return users in today's active shifts whose end_time falls within the next
`window_minutes` minutes and who still have at least one unchecked website.
Each dict has: user_id, username, full_name, email, shift_id, shift_name,
end_time, total_count, checked_count, unchecked_count,
unchecked_sites (list of {id, name, url}).
"""
conn = None
try:
conn = get_connection()
cur = conn.cursor(dictionary=True)
cur.execute(
"""
SELECT
u.id AS user_id,
u.username,
COALESCE(u.full_name, u.username) AS full_name,
u.email,
s.id AS shift_id,
s.name AS shift_name,
s.end_time,
COUNT(DISTINCT sw.website_id) AS total_count,
COUNT(DISTINCT sc.website_id) AS checked_count,
COUNT(DISTINCT sw.website_id) - COUNT(DISTINCT sc.website_id) AS unchecked_count
FROM shifts s
JOIN shift_users su ON su.shift_id = s.id
JOIN users u ON u.id = su.user_id AND u.is_active = 1
JOIN shift_websites sw ON sw.shift_id = s.id
JOIN websites w ON w.id = sw.website_id AND w.is_active = 1
LEFT JOIN shift_checks sc
ON sc.website_id = sw.website_id
AND sc.user_id = su.user_id
AND DATE(sc.checked_at) = CURDATE()
WHERE s.is_active = 1
AND LOCATE(CAST(DAYOFWEEK(CURDATE()) AS CHAR), s.days_of_week) > 0
AND s.end_time > CURTIME()
AND s.end_time <= ADDTIME(CURTIME(), SEC_TO_TIME(%s * 60))
AND NOT EXISTS (
SELECT 1 FROM shift_reminder_log srl
WHERE srl.user_id = su.user_id
AND srl.shift_id = s.id
AND srl.sent_date = CURDATE()
)
GROUP BY u.id, s.id
HAVING unchecked_count > 0
ORDER BY s.end_time, u.username
""",
(window_minutes,)
)
rows = cur.fetchall()
if not rows:
cur.close()
return []
# Attach the list of unchecked site names/URLs for each user+shift pair.
result = []
for row in rows:
cur.execute(
"""
SELECT w.id, w.name, w.url
FROM shift_websites sw
JOIN websites w ON w.id = sw.website_id AND w.is_active = 1
WHERE sw.shift_id = %s
AND NOT EXISTS (
SELECT 1 FROM shift_checks sc
WHERE sc.website_id = sw.website_id
AND sc.user_id = %s
AND DATE(sc.checked_at) = CURDATE()
)
ORDER BY sw.sort_order, w.name
""",
(row["shift_id"], row["user_id"])
)
result.append({**row, "unchecked_sites": cur.fetchall()})
cur.close()
return result
finally:
if conn:
conn.close()
def record_shift_reminder(user_id: int, shift_id: int) -> None:
"""Mark that a reminder was sent to this user for this shift today (idempotent)."""
conn = None
try:
conn = get_connection()
cur = conn.cursor()
cur.execute(
"INSERT IGNORE INTO shift_reminder_log (user_id, shift_id, sent_date) "
"VALUES (%s, %s, CURDATE())",
(user_id, shift_id),
)
conn.commit()
cur.close()
finally:
if conn:
conn.close()
# ─── Bid Reminders ────────────────────────────────────────────────────────────
def get_bids_due_soon(days: int = 7) -> list: