🔍 Code Extractor

function schedule_reminders

Maturity: 52

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.

File:
/tf/active/vicechatdev/CDocs/utils/notifications.py
Lines:
1572 - 1709
Complexity:
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

  • datetime
  • logging
  • CDocs
  • CDocs.models.review
  • CDocs.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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function get_review_statistics 58.2% similar

    Retrieves comprehensive review statistics from a Neo4j graph database, including counts of review cycles, approval rates, and reviewer decisions.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function notify_review 58.0% similar

    Sends review notifications to specified reviewers for a document review cycle, supporting multiple notification types (requested, reminder, overdue, completed).

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function create_review_cycle 55.1% similar

    Creates a new review cycle for a controlled document, assigning reviewers with optional sequential workflow, custom instructions, and approval requirements.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function get_approval_statistics_v1 54.5% similar

    Retrieves comprehensive approval statistics from a Neo4j graph database, including counts of total, pending, completed, approved, and rejected approval cycles and decisions, along with calculated approval rates.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function notify_approval 54.5% similar

    Sends approval notifications to designated approvers for a document version, supporting multiple notification types (requested, reminder, overdue, completed, rejected, signed) with email and in-app notifications.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
← Back to Browse