Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Gradebook API

From MoodleDocs

This page requires updating. Please do so and remove this template when finished.

Warning: This page uses obsolete plugin mod_assignment for some examples, this plugin was removed in Moodle 4.2 (MDL-72350).


Overview

This document explains how custom Moodle activity modules may use the Gradebook API to read and write student grades.

The Gradebook API allows you to read and write from the gradebook and to provide an interface for detailed grading information.

File Locations

The main file for the gradebook API is /lib/gradelib.php

The grade item classes like grade_item, grade_category and grade_grade (a single student's grade) are in /lib/grade/.

Functions

grade_update() is used to submit new or updated grades.

grade_get_grades() is used to retrieve grade information about a given activity. Optionally you can retrieve student grades for that activity.

grade_update_outcomes() is used to submit new or updated outcome based grades. Here is a document describing how Moodle outcomes work from a user perspective.

Things to Implement

Gradebook redirection page

This should be at /mod/$modname/grade.php. For example the assignment activity module's gradebook redirection page is at /mod/assignment/grade.php.

A teacher or student clicking on the activity name in the gradebook is sent to grade.php within the activity. It should redirect the user to the appropriate page. For example, it could send students to the activity itself while sending teachers to a list of participating students.

grade.php is supplied with the following parameters.

$id = required_param('id', PARAM_INT);          // Course module ID
$itemnumber = optional_param('itemnumber', 0, PARAM_INT); // Item number, may be != 0 for activities that allow more than one grade per user
$userid = optional_param('userid', 0, PARAM_INT); // Graded user ID (optional)

Typically you will use has_capability() to determine where to send the user then call redirect().

Callbacks

Gradebook callbacks should be available in /mod/$modname/lib.php For example the assignment activity module gradebook callbacks are in /mod/assignment/lib.php

There are three required callbacks.

{$modname}_supports($feature)

You activity module needs to define what features it supports. It may well contain a lot of other features but needs to include FEATURE_GRADE_HAS_GRADE if you want to have a grade for your activity. Optionally you can also support FEATURE_GRADE_OUTCOMES if you want to use outcomes.

function assignment_supports($feature) {
    switch($feature) {
        case FEATURE_GRADE_HAS_GRADE:         return true;
        case FEATURE_GRADE_OUTCOMES:          return true;

        default: return null;
    }
}

{$modname}_grade_item_update($modinstance, $grades=NULL)

For example, this callback for the assignment module is assignment_grade_item_update().

This callback should create or update the grade item for a given activity module instance by calling grade_update(). It can update both the activity grade item information and grade's for users if they are supplied via the $grades parameter.

Typically the $grades parameter also accepts the string 'reset' which means that the grades in the gradebook should be reset. This assists with course reset functionality which is described below.

{$modname}_update_grades($modinstance, $userid=0, $nullifnone=true)

For example, this callback for the assignment module is assignment_update_grades().

This callback should update the grade(s) for the supplied user. This may be as simple as retrieving the grades for the user from the activity module's own tables then calling {$modname}_grade_item_update().

Its parameters are:

  1. stdClass $modinstance the activity module settings.
  2. int $userid A user ID or 0 for all users.
  3. bool $nullifnone If a single user is specified, $nullifnone is true and the user has no grade then a grade item with a null rawgrade should be inserted

Reset Course Support

Course reset allows a teacher or administrator to remove all user data from a course while retaining settings and activities. Here is a general introduction to resetting a course. Here is a guide to Implementing_Reset_course_functionality_in_a_module

Resetting grades isn't specifically covered in the reset course implementation guide. The quiz activity module provides a simple example of how it is done. quiz_reset_userdata() contains this code.

if (empty($data->reset_gradebook_grades)) {
    quiz_reset_gradebook($data->courseid);
}

quiz_reset_gradebook() find all quiz instances then calls quiz_grade_item_update() with the special 'reset' parameter.

function quiz_reset_gradebook($courseid, $type='') {
    global $CFG, $DB;

    $quizzes = $DB->get_records_sql("
            SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
            FROM {modules} m
            JOIN {course_modules} cm ON m.id = cm.module
            JOIN {quiz} q ON cm.instance = q.id
            WHERE m.name = 'quiz' AND cm.course = ?", array($courseid));

    foreach ($quizzes as $quiz) {
        quiz_grade_item_update($quiz, 'reset');
    }
}

Outcomes Support

You can choose to support outcomes. There are examples of how to implement outcomes support below. If you want to use outcomes make sure that you include outcomes in your implementation of {$modname}_supports().

Examples

Inserting or Updating Grades

{$modname}_grade_item_update($modinstance, $grades=NULL)

This is the forum activity module's implementation of this callback taken from /mod/forum/lib.php. Note that Moodle scales are stored as a positive integer if they are numeric, as a negative integer if they are a custom scale and 0 means the forum is ungraded (this system may be changed by MDL-17258).

Note the special 'reset' handling.

function forum_grade_item_update($forum, $grades=NULL) {
    global $CFG;
    if (!function_exists('grade_update')) { //workaround for buggy PHP versions
        require_once($CFG->libdir.'/gradelib.php');
    }

    $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);

    if (!$forum->assessed or $forum->scale == 0) {
        $params['gradetype'] = GRADE_TYPE_NONE;

    } else if ($forum->scale > 0) {
        $params['gradetype'] = GRADE_TYPE_VALUE;
        $params['grademax']  = $forum->scale;
        $params['grademin']  = 0;

    } else if ($forum->scale < 0) {
        $params['gradetype'] = GRADE_TYPE_SCALE;
        $params['scaleid']   = -$forum->scale;
    }

    if ($grades  === 'reset') {
        $params['reset'] = true;
        $grades = NULL;
    }

    return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
}

{$modname}_update_grades($modinstance, $userid=0, $nullifnone=true)

This is the forum activity module's implementation of this callback taken from /mod/forum/lib.php.

function forum_update_grades($forum, $userid=0, $nullifnone=true) {
    global $CFG, $DB;
    require_once($CFG->libdir.'/gradelib.php');

    if (!$forum->assessed) {
        forum_grade_item_update($forum);

    } else if ($grades = forum_get_user_grades($forum, $userid)) {
        forum_grade_item_update($forum, $grades);

    } else if ($userid and $nullifnone) {
        $grade = new stdClass();
        $grade->userid   = $userid;
        $grade->rawgrade = NULL;
        forum_grade_item_update($forum, $grade);

    } else {
        forum_grade_item_update($forum);
    }
}

It checks if the forum is assessed (is gradeable). If it is not the forum object is passed to forum_grade_item_update() so settings like the grading scale being used can be saved.

If the forum is assessed it attempts to load user grades. forum_get_user_grades() does this by accessing the Rating_API however your activity module may instead access its own tables. You should handle a user ID of 0 being supplied.

If the user has no grade (or a user ID of 0 was supplied) and if $nullifnone is true then a grade with rawgrade == NULL is inserted.

Retrieving Grades

This is an example of how to retrieve user grades.

//$users == array of users
$grading_info = grade_get_grades($courseid, 'mod', $modname, $modinstance->id, array_keys($users));

$grade_item_grademax = $grading_info->items[0]->grademax;
foreach ($users as $user) {
    $user_final_grade = $grading_info->items[0]->grades[$user->id];
}

Inserting or Updating Outcome Grade Items

Working with outcome grade items is slightly more complex than "regular" grade items.

Make sure to observe the value of $CFG->enableoutcomes as outcomes may be enabled or disabled at site level.

Inserting Outcome Grade Items

The process to insert an outcome grade item is a little long but its not complicated. First, create a new grade_object. An ID of 0 means new.

$grade_item = new grade_item(array('id'=>0, 'courseid'=>$courseid));
grade_item::set_properties($grade_item, $data);

$data is an instance of stdClass similar to this.

stdClass Object
(
    [itemname] => 'Reading comprehension' // the outcome name
    [iteminfo] => 
    [idnumber] =>    //blank for now
    [outcomeid] => 1 // the outcome ID
    [cmid] => 0
    [id] => 0        // 0 means new
    [courseid] => 2  // the course ID
    [aggregationcoef] => 0
)

Now give the outcome grade item a unique itemnumber. The activity's original grade item will have a itemnumber of 0. If you have multiple outcome grade items per activity you may need to query the database to check itemnumber is unique.

$grade_item->itemnumber = $uniqueitemnumber;

Set the outcome grade item to use the outcome's scale then insert the grade item into the database.

$outcome = grade_outcome::fetch(array('id'=>$outcomeid));
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->scaleid = $outcome->scaleid;

$grade_item->insert(); //or maybe $grade_item->update();

Finally load the activity's original grade item, put the outcome grade item in the same category and check that the sort order is correct.

if ($item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$grade_item->itemmodule,
        'iteminstance'=>$grade_item->iteminstance, 'itemnumber'=>0, 'courseid'=>$COURSE->id))) {
    $grade_item->set_parent($item->categoryid);
    $grade_item->move_after_sortorder($item->sortorder);
}

Inserting Student Grades For Outcome Grade Items

The "itemnumber" is the key to inserting outcome grades. All grade item's for an activity module will have the same itemtype, usually 'mod', the same itemmodule, something like 'assignment' or 'forum', and the same iteminstance, the integer ID of the module instance. The itemnumber is 0 for the activity's primary grade item but will be a number greater than 1 for outcome grade items attached to the activity.

if (empty($CFG->enableoutcomes)) {
    return;
}

require_once($CFG->libdir.'/gradelib.php');

$data = array();
$grading_info = grade_get_grades($courseid, 'mod', $modtype, $modinstance->id, $userid);

if (!empty($grading_info->outcomes)) {
    foreach($grading_info->outcomes as $n=>$old) {
         $data[$n] = 2; //new student grade for this outcome == 2, $n == "itemnumber"
    }
}
if (count($data) > 0) {
    grade_update_outcomes('mod/'.$modtype, $courseid, 'mod', $modtype, $modinstance->id, $userid, $data);
}