Completion is divided up into two subsystems:

  • Activity completion - marking an activity complete depending on various criteria
  • Course completion - aggregating the results of a list of criteria for determining if a user has completed a particular course

Activity completion was written (and maintained) by Sam Marshall of the Open University. Course completion was written and maintained by Aaron Barnes.

Activity and course completion interact through course completion's "Activity" criteria. This criteria checks a user's activity completion state in a course module.

Activity completion

This is set up via an individual course modules settings, and once set work on user's future activity within the module. There are some default criteria, and some modules have additional activity criteria defined. Once user's have completed the activity, the settings become locked and cannot be modified unless all existing completion data is deleted.

User data is stored in the table "course_modules_completion".

Recording time of activity completion

When activities are completed that are part of course completion criteria, Moodle and Totara both update a timecompleted field in the course_completion_criteria_compl table.

Regardless of whether activities are part of course completion, when an activity is completed, a row will be added to the 'course_modules_completion' table. Moodle does not use a 'timecompleted' field within this table, however Totara added this as part of an update to face-to-face. At this stage, that timecompleted field is only updated in limited cases.

Note that the rest of the discussion about timecompleted, in this section about activity completion, refers to the timecompleted field in the course_modules_completion table (and not course_completion_criteria_compl).

Functions involved

The function responsible for setting timemodified or timecompleted fields is normally the update_state() function in lib/completionlib.php, which sets those properties within the object $cm used in the function. A timecompleted value could be added to $cm prior to it being passed to the function as a parameter or it may be set during a call to internal_get_state(), which will in turn call {module}_get_completion_state (e.g. facetoface_get_completion_state).

The update_state() function will call internal_set_data() which updates the table itself.

Timemodified as completion time

Moodle uses the 'timemodified' field for the purposes of completion time in cases where it needs to retrieve the completion time for an activity from that table. Using timemodified as a completion time generally works as the code has been structured so that the record is only updated when a state changes from complete to incomplete, or vice versa.

Where timecompleted is used

Totara's 'timecompleted' field in 'course_modules_completion' is currently only updated when completion criteria for a face-to-face activity is to fully or partially attend a session. When this happens, the timecompleted will be set to a timestamp for the finish time of the attended session. A reason for this may be that the completion time for attending a session is different to the time that the attendance was updated (which would be reflected in the timemodified field).

When an activity is completed by being viewed, graded, manually completed or any other form of completion within Totara or Moodle code aside from attending a face-to-face session, only the 'timemodified' field will be updated (which will be with the current timestamp). The 'timecompleted' field will not be updated – ideally, it should never have been set, so should be null and then would remain that way.

timecompleted not unset

When the state of an activity completion record is changed from complete to incomplete, the timemodified field is updated with the current timestamp again. Timecompleted is generally not updated in this case, if a user had completed a face-to-face activity by attending, the timecompleted will be left there. The code only sets timecompleted to null when changing to an incomplete state if timecompleted was set in the $cm data passed to update_state, or it was set by the modules {module}_get_completion_state() function – in current code, there won't be if the state is being changed to incomplete.

Where timecompleted is used

When creating or updating a course completion criteria record that is based on completion of an activity, the review() function within completion/criteria/completion_criteria_activity.php will first check if the new course_modules_completion record has timecompleted set, if not, it will use timemodified. If timemodified was not set somehow, it will ultimately use the current timestamp (which is set when it runs the function mark_complete()).

Things to be aware of with the current code

If timecompleted has somehow been set in a record other than a face-to-face activity requiring attendance, the standard Totara code will not update it again. e.g. if a custom patch meant that when a quiz was completed, the timecompleted field would be set with the current time. If that patch is overwritten, any course_modules_completion records updated when that patch existed will now have a timecompleted that will not be updated, even when completion states change to incomplete and back again. And if that quiz is part of completion criteria, the code will use the out-of-date timecompleted value because it checks that first.

If a report based on the course_modules_completion table, the timecompleted fields would only be set for the specific face-to-face completions. It would also not be unset if the state was changed to incomplete. (At present, no Totara report sources use that table).

Other notes relevant to completion of activities

The completion time as recorded in completion criteria (in the course_completions_criteria_compl table) is only ever updated with a later time. This is intentional to prevent issues that arise in certifications where there may be unwanted follow-on effects.

When the window opens for a certification, relevant records in the course_modules_completion table (that were created/updated when the certification was last completed) remain, but their state changes to incomplete.

When completion criteria is updated with the chosen option of removing existing completion records, the relevant records in the course_modules_completion table are deleted.

When completion criteria is updated with the chosen option of keeping existing completion records, the relevant records in the course_modules_completion table remain and no changes are made to them.

Course completion

Course completion is decided by enabling completion criteria for a course. Criteria are checked by a cron job, and if required criteria are completed a user is marked as "complete" in a course.

User data is stored in the tables "course_completion_crit_compl" for individual criteria, and "course_completions" for the aggregate results for a course.


Course completion criteria:

  • Grade: This compares the user's course grade from the gradebook with the value set for the criteria. This value is initially loaded from the gradebook, and if changed will update the gradebook's grade required.