Skip to content

Quiz keeps blocking reattempts after a course/user reset (local_recompletion, native "Reset course") #16

Description

@rigao-alexandre

Problem

quizaccess_failgrade decides whether to block a new attempt using only two signals: the
number of previous attempts and the user's current grade in the gradebook. It has no notion
of "training cycles."

This came up in practice from a site using
local_recompletion to reset ~70
courses for annual compliance training. Depending on each course's recompletion settings
(whether "Quiz attempts" is set to delete/keep, and whether "Delete grade data" is enabled),
a user who already passed in a previous cycle can be permanently blocked from a new attempt
after their completion is reset, because their old attempt/grade is still there and the
plugin has no way to know a reset happened in between.

The most commonly suggested workaround (local_recompletion's "Delete grade data" option)
has a real downside: it removes the old grade from the normal gradebook view (recoverable
only via the seldom-used "Grade history" report), which isn't great for compliance use cases
where you actually want last cycle's pass visibly on record.

Proposed fix (in progress)

See branch recompletion-compat
(based on top of the Moodle 5.x support work in moodle5-support, not yet merged).

Rather than requiring any grade/attempt data to be deleted, the plugin now detects resets
directly and adjusts its own interpretation, with zero data loss:

  • A new quizaccess_failgrade_reset table (courseid, nullable userid, timereset).
  • Two event observers:
    • \core\event\course_reset_ended — Moodle's own native "Reset course" feature (course-wide,
      so userid is left null).
    • \local_recompletion\event\completion_reset — fired once per user by that plugin's
      scheduled task. Soft/optional dependency: registered by class name, never fires if
      local_recompletion isn't installed.
  • is_finished() now ignores any attempt/grade older than the most recent matching reset
    (course-wide or for that specific user), instead of blocking on it.
  • Full privacy provider added (the new table stores userid for the per-user path, so the
    plugin could no longer honestly declare itself a null_provider).

Status

Work in progress, no release planned yet. Still open:

  • Needs to actually run green in CI (not yet pushed as a PR).
  • The local_recompletion observer path has no automated test coverage, since that plugin
    isn't part of this repo's CI environment — it delegates to the same code path the
    course_reset_ended test does exercise, so risk is limited but not zero.
  • Only covers these two specific reset sources; any other custom/proprietary reset tooling
    that doesn't fire one of these two events remains unaddressed.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions