Gradebook API: Difference between revisions
Andrew Davis (talk | contribs) |
(Add Warning and Update templates as mod_assignment was removed in Moodle 4.2) |
||
(34 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{Update}} | |||
{{Warning|This page uses obsolete plugin mod_assignment for some examples, this plugin was removed in Moodle 4.2 (MDL-72350).}} | |||
==Overview== | ==Overview== | ||
This document explains how custom Moodle activity modules may use the Gradebook API to read and write student grades. | 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 | The Gradebook API allows you to read and write from the gradebook and to provide an interface for detailed grading information. | ||
==File Locations== | ==File Locations== | ||
The main file | 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== | ==Functions== | ||
Line 13: | Line 18: | ||
grade_update() is used to submit new or updated grades. | 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 [https://docs.moodle.org/en/Outcomes 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 | 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. | grade.php is supplied with the following parameters. | ||
< | <syntaxhighlight lang="php"> | ||
$id | $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 | $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) | $userid = optional_param('userid', 0, PARAM_INT); // Graded user ID (optional) | ||
</ | </syntaxhighlight> | ||
Typically you will use has_capability() to determine where to send the user then call redirect(). | Typically you will use has_capability() to determine where to send the user then call redirect(). | ||
Line 37: | Line 44: | ||
Gradebook callbacks should be available in /mod/$modname/lib.php For example the assignment activity module gradebook callbacks are in /mod/assignment/lib.php | 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 | 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. | |||
<syntaxhighlight lang="php"> | |||
function assignment_supports($feature) { | |||
switch($feature) { | |||
case FEATURE_GRADE_HAS_GRADE: return true; | |||
case FEATURE_GRADE_OUTCOMES: return true; | |||
default: return null; | |||
} | |||
} | |||
</syntaxhighlight> | |||
===={$modname}_grade_item_update($modinstance, $grades=NULL)==== | ===={$modname}_grade_item_update($modinstance, $grades=NULL)==== | ||
Line 44: | Line 66: | ||
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. | 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)==== | ===={$modname}_update_grades($modinstance, $userid=0, $nullifnone=true)==== | ||
Line 52: | Line 76: | ||
Its parameters are: | Its parameters are: | ||
# stdClass $ | # stdClass $modinstance the activity module settings. | ||
# int $userid | # int $userid A user ID or 0 for all users. | ||
# bool $nullifnone If a single user is specified | # 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 [https://docs.moodle.org/en/Reset_course 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. | |||
<syntaxhighlight lang="php"> | |||
if (empty($data->reset_gradebook_grades)) { | |||
quiz_reset_gradebook($data->courseid); | |||
} | |||
</syntaxhighlight> | |||
quiz_reset_gradebook() find all quiz instances then calls quiz_grade_item_update() with the special 'reset' parameter. | |||
<syntaxhighlight lang="php"> | |||
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'); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===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== | ==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. | |||
<syntaxhighlight lang="php"> | |||
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); | |||
} | |||
</syntaxhighlight> | |||
===={$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. | |||
<syntaxhighlight lang="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); | |||
} | |||
} | |||
</syntaxhighlight> | |||
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. | |||
<syntaxhighlight lang="php"> | |||
//$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]; | |||
} | |||
</syntaxhighlight> | |||
===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. | |||
<syntaxhighlight lang="php"> | |||
$grade_item = new grade_item(array('id'=>0, 'courseid'=>$courseid)); | |||
grade_item::set_properties($grade_item, $data); | |||
</syntaxhighlight> | |||
$data is an instance of stdClass similar to this. | |||
<syntaxhighlight lang="php"> | |||
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 | |||
) | |||
</syntaxhighlight> | |||
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. | |||
<syntaxhighlight lang="php"> | |||
$grade_item->itemnumber = $uniqueitemnumber; | |||
</syntaxhighlight> | |||
Set the outcome grade item to use the outcome's scale then insert the grade item into the database. | |||
<syntaxhighlight lang="php"> | |||
$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(); | |||
</syntaxhighlight> | |||
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. | |||
<syntaxhighlight lang="php"> | |||
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); | |||
} | |||
</syntaxhighlight> | |||
====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. | |||
<syntaxhighlight lang="php"> | |||
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); | |||
} | |||
</syntaxhighlight> |
Latest revision as of 10:50, 23 July 2023
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:
- stdClass $modinstance the activity module settings.
- int $userid A user ID or 0 for all users.
- 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);
}