June 25 - Implement reminder
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user