function schedule_reminders
Automated scheduled task that sends reminder and overdue notifications for pending reviews and approvals that are due within 3 days or past their due date.
/tf/active/vicechatdev/CDocs/utils/notifications.py
1572 - 1709
moderate
Purpose
This function is designed to be executed daily by a scheduled task (e.g., cron job) to maintain awareness of upcoming and overdue document reviews and approvals. It queries a Neo4j graph database for ReviewCycle and Approval nodes with approaching or past due dates, sends appropriate notifications via notify_review() and notify_approval() functions, and marks nodes to prevent duplicate notifications. It handles four scenarios: upcoming reviews (within 3 days), upcoming approvals (within 3 days), overdue reviews, and overdue approvals.
Source Code
def schedule_reminders():
"""
Schedule reminder notifications for upcoming reviews and approvals.
This should be called by a scheduled task daily.
"""
try:
# Get upcoming review deadlines (within next 3 days)
current_date = datetime.now()
reminder_date = current_date + timedelta(days=3)
# Find pending reviews due soon
query = """
MATCH (r:ReviewCycle)
WHERE r.status IN ['PENDING', 'IN_PROGRESS']
AND r.dueDate > $current_date
AND r.dueDate <= $reminder_date
AND NOT exists(r.reminderSent)
MATCH (r)<-[:FOR_REVIEW]-(v:DocumentVersion)
MATCH (d:ControlledDocument)-[:HAS_VERSION]->(v)
RETURN r.UID as reviewUID, d.UID as docUID
"""
review_results = db.run_query(query, {
"current_date": current_date,
"reminder_date": reminder_date
})
# Send review reminders
for record in review_results:
try:
from ..models.review import ReviewCycle
review_cycle = ReviewCycle(uid=record['reviewUID'])
notify_review(review_cycle, 'REVIEW_REMINDER')
# Mark reminder as sent
db.run_query(
"""
MATCH (r:ReviewCycle {UID: $review_uid})
SET r.reminderSent = $sent_date
""",
{"review_uid": record['reviewUID'], "sent_date": current_date}
)
except Exception as e:
logger.error(f"Error sending review reminder: {e}")
# Find pending approvals due soon
query = """
MATCH (a:Approval)
WHERE a.status = 'PENDING'
AND a.dueDate > $current_date
AND a.dueDate <= $reminder_date
AND NOT exists(a.reminderSent)
RETURN a.UID as approvalUID
"""
approval_results = db.run_query(query, {
"current_date": current_date,
"reminder_date": reminder_date
})
# Send approval reminders
for record in approval_results:
try:
from ..models.approval import Approval
approval = Approval(uid=record['approvalUID'])
notify_approval(approval, 'APPROVAL_REMINDER')
# Mark reminder as sent
db.run_query(
"""
MATCH (a:Approval {UID: $approval_uid})
SET a.reminderSent = $sent_date
""",
{"approval_uid": record['approvalUID'], "sent_date": current_date}
)
except Exception as e:
logger.error(f"Error sending approval reminder: {e}")
# Find overdue reviews
query = """
MATCH (r:ReviewCycle)
WHERE r.status IN ['PENDING', 'IN_PROGRESS']
AND r.dueDate < $current_date
AND NOT exists(r.overdueNotificationSent)
RETURN r.UID as reviewUID
"""
overdue_review_results = db.run_query(query, {"current_date": current_date})
# Send overdue review notifications
for record in overdue_review_results:
try:
from ..models.review import ReviewCycle
review_cycle = ReviewCycle(uid=record['reviewUID'])
notify_review(review_cycle, 'REVIEW_OVERDUE')
# Mark overdue notification as sent
db.run_query(
"""
MATCH (r:ReviewCycle {UID: $review_uid})
SET r.overdueNotificationSent = $sent_date
""",
{"review_uid": record['reviewUID'], "sent_date": current_date}
)
except Exception as e:
logger.error(f"Error sending overdue review notification: {e}")
# Find overdue approvals
query = """
MATCH (a:Approval)
WHERE a.status = 'PENDING'
AND a.dueDate < $current_date
AND NOT exists(a.overdueNotificationSent)
RETURN a.UID as approvalUID
"""
overdue_approval_results = db.run_query(query, {"current_date": current_date})
# Send overdue approval notifications
for record in overdue_approval_results:
try:
from ..models.approval import Approval
approval = Approval(uid=record['approvalUID'])
notify_approval(approval, 'APPROVAL_OVERDUE')
# Mark overdue notification as sent
db.run_query(
"""
MATCH (a:Approval {UID: $approval_uid})
SET a.overdueNotificationSent = $sent_date
""",
{"approval_uid": record['approvalUID'], "sent_date": current_date}
)
except Exception as e:
logger.error(f"Error sending overdue approval notification: {e}")
except Exception as e:
logger.error(f"Error scheduling reminders: {e}")
Return Value
This function does not return any value (implicitly returns None). It performs side effects by sending notifications and updating the database with reminder flags.
Dependencies
datetimeloggingCDocsCDocs.models.reviewCDocs.models.approval
Required Imports
from datetime import datetime, timedelta
import logging
Conditional/Optional Imports
These imports are only needed under specific conditions:
from ..models.review import ReviewCycle
Condition: Imported lazily inside the function when processing review reminders and overdue reviews
Required (conditional)from ..models.approval import Approval
Condition: Imported lazily inside the function when processing approval reminders and overdue approvals
Required (conditional)Usage Example
# This function is typically called by a scheduled task runner
# Example using APScheduler:
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime, timedelta
import logging
# Ensure logger is configured
logger = logging.getLogger(__name__)
# Ensure db, notify_review, and notify_approval are available
# from CDocs import db
# from your_notification_module import notify_review, notify_approval
# Schedule to run daily at 9:00 AM
scheduler = BlockingScheduler()
scheduler.add_job(schedule_reminders, 'cron', hour=9, minute=0)
# Or run immediately for testing
schedule_reminders()
# Or schedule with cron (in crontab):
# 0 9 * * * /usr/bin/python3 /path/to/script.py
Best Practices
- This function should be executed by a scheduled task runner (cron, APScheduler, Celery Beat, etc.) exactly once per day to avoid duplicate notifications
- Ensure proper error handling is in place at the scheduler level to catch and log any exceptions that escape the function's try-except blocks
- Monitor the logger output for individual notification failures, as the function continues processing even when individual notifications fail
- The function uses database flags (reminderSent, overdueNotificationSent) to prevent duplicate notifications - ensure these are not manually cleared unless intentional
- Consider implementing idempotency checks at the scheduler level to prevent concurrent executions
- The 3-day reminder window is hardcoded - consider making this configurable if different reminder periods are needed
- Ensure the database connection pool can handle the query load, especially in systems with many pending reviews/approvals
- Test thoroughly in a staging environment before deploying to production, as notification spam can be disruptive
- Consider implementing rate limiting or batching if the number of notifications becomes very large
- The function performs multiple database queries sequentially - consider optimizing with batch operations if performance becomes an issue
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function get_review_statistics 58.2% similar
-
function notify_review 58.0% similar
-
function create_review_cycle 55.1% similar
-
function get_approval_statistics_v1 54.5% similar
-
function notify_approval 54.5% similar