Schedule icon
Request icon
Return icon
Switch icon
Log icon
SlackIncomingWebhook icon

Moodle Grading Backlog Sentinel

Check Moodle assignments and submissions on a schedule, change nothing, and alert Slack when student work is overdue for marking or the site is unreachable.

Categories
BusinessData

Students submit work in Moodle and then wait. Moodle shows each teacher their own "needs grading" count, but nobody sees the whole site: which courses are past their marking deadline, how old the oldest unmarked submission is, or whether the backlog is growing week after week. This blueprint turns two read-only Moodle web service calls into a scheduled marking backlog gate: mod_assign_get_assignments returns every assignment with its "remind me to grade by" date, mod_assign_get_submissions returns every submitted piece of work with its grading status, a single jq query works out what is overdue and by how much, and the result routes to a quiet log, an informational Slack note or a Slack page.

How it works

  1. fetch_assignments (io.kestra.plugin.core.http.Request) calls mod_assign_get_assignments and receives every course the token's user is enrolled in, with each assignment's id, name and gradingduedate. This is a GET: nothing is changed. allowFailed: true, a timeout, three retries and allowFailure: true keep a network blip or an outage from aborting the flow.
  2. fetch_submissions (io.kestra.plugin.core.http.Request) builds the assignmentids[] list from the first response and calls mod_assign_get_submissions with status=submitted, which returns each submission's timemodified and gradingstatus. A runIf skips it when the assignment list is missing, is a Moodle error, or is empty.
  3. classify_backlog (io.kestra.plugin.core.debug.Return) first guards for a missing response, a non-200 code, and a Moodle exception body (Moodle reports an invalid token with HTTP 200), then joins submissions to assignments in one jq query. It keeps submissions that are submitted and not yet marked (notgraded, or notmarked/inmarking when the marking workflow is on), and treats one as overdue once the assignment's gradingduedate has passed, or grace_days after it was submitted when the assignment has no marking deadline. It returns the counts, the oldest overdue age in days, the assignment with the biggest backlog, the number of assignments the token could not read, and the status: no visible assignments or no access to any of them is UNREACHABLE; at least critical_overdue overdue, or one at least critical_age_days late, is CRITICAL; at least warning_overdue overdue, or some assignments unreadable, is WARNING; otherwise HEALTHY.
  4. route_by_backlog (io.kestra.plugin.core.flow.Switch) branches on that status: HEALTHY logs a quiet confirmation, WARNING posts an informational Slack note, CRITICAL and UNREACHABLE each page Slack, and a defaults fallback logs any unexpected value.
  5. log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run with both HTTP codes and the full classification, independent of which branch fired.
  6. The flow-level errors block alerts Slack separately if the flow fails outright, so a broken sentinel is never mistaken for an empty backlog.
  7. The weekday_backlog_check trigger runs at 08:00 on weekdays and ships disabled: true, with moodle_url pinned in its inputs.

What you get

  • A scheduled, read-only view of the marking backlog across every course the token's user can see, with no Moodle plugin to install.
  • A status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on, and a backlog JSON output with assignments, waiting, overdue, oldest_days, no_access and worst.
  • Slack messages that say how many submissions are overdue, how late the oldest one is, and which assignment has the biggest backlog, for example BIO101 / Lab report 3 (4).
  • An audit log line on every run, so the backlog's history survives a Slack outage.

Who it's for

  • Heads of department and programme leads who want to know when feedback is falling behind, before students complain.
  • Academic quality and learning technology teams who run Moodle for a whole school, college or university.
  • Teams already using Kestra for data pipelines who want the same alerting for their learning platform.

Why orchestrate this with Kestra

Moodle's own grading reminders are per teacher and per assignment, and there is no built-in site-wide alert when marking falls behind. Kestra runs the same read-only check on a schedule, keeps every result in the execution history, classifies it in one typed status, and routes it to the right channel, while the Switch branches are ready to grow into real follow-up (a digest per department, a ticket for the course lead) without rewriting the check.

Prerequisites

  • A Moodle site (4.x or 5.x) with web services and the REST protocol enabled (Site administration > General > Web services).
  • A dedicated Moodle account with the non-editing teacher role in the courses to watch (it needs mod/assign:grade to see submissions), and a web service token for an external service that contains only mod_assign_get_assignments and mod_assign_get_submissions.
  • A Slack incoming webhook for the WARNING, CRITICAL, UNREACHABLE and flow-failure alerts.

Secrets

  • MOODLE_TOKEN: Moodle web service token for the monitoring account.
  • SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.

In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_ and base64-encoded, then read in flows with {{ secret('NAME') }}, for example SECRET_MOODLE_TOKEN=$(echo -n '<token>' | base64).

Quick start

  1. To try it without a production site, run Moodle locally (for example the official moodlehq/moodle-php-apache image or a PHP and PostgreSQL install), and create a course with a few students and an assignment.
  2. In Site administration > Server > Web services, enable web services and the REST protocol, create an external service with the two functions above, and create a token for a monitoring account enrolled as non-editing teacher.
  3. Add MOODLE_TOKEN and SLACK_WEBHOOK as secrets, set moodle_url, and run the flow once manually. The status output reads HEALTHY when nothing is past its marking deadline.
  4. Set an assignment's "remind me to grade by" date in the past while it has ungraded submissions and run again: the status becomes WARNING and Slack receives the overdue count.
  5. Enable weekday_backlog_check once pointed at the real site.

How to extend

  • Pass courseids[] to mod_assign_get_assignments to watch one faculty or programme instead of every course the token can see.
  • Group the overdue submissions by course in the jq query and send each department its own Slack channel through a ForEach.
  • Store status and overdue in the KV store and alert only when the backlog grows, not on every run.
  • Add the same pattern for quizzes with manually graded questions through mod_quiz_get_attempts style calls.

Inputs

  • moodle_url: type STRING, default "https://moodle.example.edu".
  • grace_days: type INT, default 7.
  • warning_overdue: type INT, default 1.
  • critical_overdue: type INT, default 20.
  • critical_age_days: type INT, default 14.

Outputs

  • status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.
  • backlog: the full classification as JSON, for example {"assignments":4,"waiting":15,"overdue":7,"oldest_days":5,"no_access":0,"worst":"BIO101 / Lab report 3 (4)","status":"WARNING"}.

Links

Pitfalls

  • Moodle web services take the token as the wstoken query parameter, so it is part of the request URL. Keep HTTP request logging off on any proxy in between, and use a dedicated account with the smallest role that can see submissions.
  • Moodle answers an invalid token, a missing permission or a disabled function with HTTP 200 and an exception body. The flow checks for that body explicitly, so these cases report UNREACHABLE instead of an empty, healthy-looking backlog.
  • Assignments the token's user cannot grade come back as warnings, not submissions. Some unreadable assignments raise WARNING; if none can be read, the check is UNREACHABLE.
  • mod_assign_get_submissions returns every submission for every listed assignment in one response. On a very large site, filter by course (see How to extend) to keep the response small.
  • The check only reads. It never grades, extends deadlines or messages students.
See How

New to Kestra?

Use blueprints to kickstart your first workflows.