Best way to create scheduled background task?

Hello everyone :grin:

Previously I had a doctype
Study Schedule:
Date
Start Time
End Time

My current case is, I want to create a background task to send a push notification one day before the study schedule starts, but Iā€™m confused about how to do it. Iā€™ve tried:

  1. Reading the documentation about enqueue and schedular event (Background Jobs), but what Iā€™m confused about is, in enqueue there is no scheduling, there is only short, default, and long, and schedular event, will run in a period of time such as per day, week, month, etc.
  2. I tried asking Ai but the answer uses enqueue_at, but I didnā€™t find any documentation about enqueue_at

So what should I use? Thank you very much

You can use a scheduler event to run tasks at specific times using cron expressions. For your case, where you want to send a push notification one day before the study schedule starts, you can schedule a task like this:

  1. In the scheduler_events section of your appā€™s hooks.py file, add a cron job.
  2. The cron expression allows you to set when the task should run. For example, if you want the task to run at midnight (12:00 AM) on the 27th of every month, you can use the following setup:
scheduler_events = {
    "cron": {
        # This runs at midnight (12:00 AM) on the 27th of every month
        "0 0 27 * *": [
            "my_app.my_app.doctype.study_schedule.study_schedule.send_push_notification"
        ]
    }
}

  • The cron expression "0 0 27 * *" means ā€œrun at 12:00 AM on the 27th of each monthā€.
  • Replace send_push_notification with your actual method to send the push notification, and you can adjust the cron expression based on your desired schedule (for example, a day before the study schedule starts).

To know the exact time and day to run your cron job, you can use a tool like CronGuru. Itā€™s a great online tool where you can input the schedule (e.g., ā€œRun at 12:00 AM every dayā€ or ā€œRun on the 27th of each monthā€), and it will generate the correct cron expression for you. This way, you can configure your cron job accurately without worrying about getting the expression wrong!

This means that the function that will be run by the cron job, for example every day, isā€¦

Thank you so much, but how do i check if my scheduler event is active or not? or check scheduler event list?

For Testing on your Local,
Go in Scheduled Job Type DocType there you can see all the jobs. you can execute from there if you want test and can also check logs in Scheduled Job Log DocType.

Inside your Frappe app directory, create a utils folder (if it doesnā€™t exist) and inside it, create a file called schedule_tasks.py.

import frappe
from frappe.utils import now_datetime, add_days
from datetime import datetime

def send_study_schedule_notification():
    """Send a push notification one day before the study schedule starts."""
    
    # Get the current date and add 1 day to get tomorrow's date
    tomorrow = add_days(now_datetime().date(), 1)

    # Fetch study schedules that are scheduled for tomorrow
    study_schedules = frappe.get_all(
        "Study Schedule",
        filters={"date": tomorrow},
        fields=["name", "start_time", "end_time"]
    )

    for schedule in study_schedules:
        # Customize the message
        message = f"Reminder: Your study session is scheduled on {tomorrow} from {schedule['start_time']} to {schedule['end_time']}."
        
        # Fetch the users who should receive the notification
        recipients = frappe.get_all(
            "User",
            filters={"enabled": 1},  # Only send to active users
            fields=["email"]
        )

        for user in recipients:
            send_push_notification(user["email"], message)

def send_push_notification(user, message):
    """Function to send push notification"""
    try:
        frappe.publish_realtime(
            event="msgprint",
            message=message,
            user=user
        )
        frappe.logger().info(f"Push notification sent to {user}: {message}")
    except Exception as e:
        frappe.logger().error(f"Failed to send notification to {user}: {str(e)}")

Modify your_app/hooks.py

scheduler_events = {
    "cron": {
        "0 8 * * *": [  # Runs at 8 AM every day
            "your_app.utils.schedule_tasks.send_study_schedule_notification"
        ]
    }
}