MDL-66074 gradingform_rubric: Documentation and selectors

This commit is contained in:
Mathew May 2019-10-28 12:56:19 +08:00
parent 9c2c79f5fb
commit 47919bbe78
10 changed files with 104 additions and 24 deletions

View File

@ -1 +1 @@
{"version":3,"sources":["../../../src/grades/grader/gradingpanel.js"],"names":["fetchCurrentGrade","component","contextid","itemname","gradeduserid","methodname","args","storeCurrentGrade","rootNode","form","querySelector","normaliseResult","formdata","serialize"],"mappings":"2QA6BA,uD,mVAEiC,QAApBA,CAAAA,iBAAoB,CAACC,CAAD,CAAYC,CAAZ,CAAuBC,CAAvB,CAAiCC,CAAjC,CAAkD,CAC/E,MAAO,WAAU,CAAC,CACdC,UAAU,+CADI,CAEdC,IAAI,CAAE,CACFL,SAAS,CAATA,CADE,CAEFC,SAAS,CAATA,CAFE,CAGFC,QAAQ,CAARA,CAHE,CAIFC,YAAY,CAAZA,CAJE,CAFQ,CAAD,CAAV,EAQH,CARG,CASV,C,CAGM,GAAMG,CAAAA,CAAiB,4CAAG,WAAMN,CAAN,CAAiBC,CAAjB,CAA4BC,CAA5B,CAAsCC,CAAtC,CAAoDI,CAApD,yFACvBC,CADuB,CAChBD,CAAQ,CAACE,aAAT,CAAuB,MAAvB,CADgB,MAGtBC,iBAHsB,gBAGA,WAAU,CAAC,CACpCN,UAAU,+CAD0B,CAEpCC,IAAI,CAAE,CACFL,SAAS,CAATA,CADE,CAEFC,SAAS,CAATA,CAFE,CAGFC,QAAQ,CAARA,CAHE,CAIFC,YAAY,CAAZA,CAJE,CAKFQ,QAAQ,CAAE,cAAOH,CAAP,EAAaI,SAAb,EALR,CAF8B,CAAD,CAAV,EASzB,CATyB,CAHA,qGAAH,uDAAvB,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Grading panel for gradingform_rubric.\n *\n * @module gradingform_rubric/grades/grader/gradingpanel\n * @package gradingform_rubric\n * @copyright 2019 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport {normaliseResult} from 'core_grades/grades/grader/gradingpanel/normalise';\n\n\n// Note: We use jQuery.serializer here until we can rewrite Ajax to use XHR.send()\nimport jQuery from 'jquery';\n\nexport const fetchCurrentGrade = (component, contextid, itemname, gradeduserid) => {\n return fetchMany([{\n methodname: `gradingform_rubric_grader_gradingpanel_fetch`,\n args: {\n component,\n contextid,\n itemname,\n gradeduserid,\n },\n }])[0];\n};\n\n\nexport const storeCurrentGrade = async(component, contextid, itemname, gradeduserid, rootNode) => {\n const form = rootNode.querySelector('form');\n\n return normaliseResult(await fetchMany([{\n methodname: `gradingform_rubric_grader_gradingpanel_store`,\n args: {\n component,\n contextid,\n itemname,\n gradeduserid,\n formdata: jQuery(form).serialize(),\n },\n }])[0]);\n};\n"],"file":"gradingpanel.min.js"}
{"version":3,"sources":["../../../src/grades/grader/gradingpanel.js"],"names":["fetchCurrentGrade","component","contextid","itemname","gradeduserid","methodname","args","storeCurrentGrade","rootNode","form","querySelector","normaliseResult","formdata","serialize"],"mappings":"2QA6BA,uD,mVAYiC,QAApBA,CAAAA,iBAAoB,CAACC,CAAD,CAAYC,CAAZ,CAAuBC,CAAvB,CAAiCC,CAAjC,CAAkD,CAC/E,MAAO,WAAU,CAAC,CACdC,UAAU,+CADI,CAEdC,IAAI,CAAE,CACFL,SAAS,CAATA,CADE,CAEFC,SAAS,CAATA,CAFE,CAGFC,QAAQ,CAARA,CAHE,CAIFC,YAAY,CAAZA,CAJE,CAFQ,CAAD,CAAV,EAQH,CARG,CASV,C,CAaM,GAAMG,CAAAA,CAAiB,4CAAG,WAAMN,CAAN,CAAiBC,CAAjB,CAA4BC,CAA5B,CAAsCC,CAAtC,CAAoDI,CAApD,yFACvBC,CADuB,CAChBD,CAAQ,CAACE,aAAT,CAAuB,MAAvB,CADgB,MAGtBC,iBAHsB,gBAGA,WAAU,CAAC,CACpCN,UAAU,+CAD0B,CAEpCC,IAAI,CAAE,CACFL,SAAS,CAATA,CADE,CAEFC,SAAS,CAATA,CAFE,CAGFC,QAAQ,CAARA,CAHE,CAIFC,YAAY,CAAZA,CAJE,CAKFQ,QAAQ,CAAE,cAAOH,CAAP,EAAaI,SAAb,EALR,CAF8B,CAAD,CAAV,EASzB,CATyB,CAHA,qGAAH,uDAAvB,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Grading panel for gradingform_rubric.\n *\n * @module gradingform_rubric/grades/grader/gradingpanel\n * @package gradingform_rubric\n * @copyright 2019 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport {normaliseResult} from 'core_grades/grades/grader/gradingpanel/normalise';\n\n\n// Note: We use jQuery.serializer here until we can rewrite Ajax to use XHR.send()\nimport jQuery from 'jquery';\n\n/**\n * For a given component, contextid, itemname & gradeduserid we can fetch the currently assigned grade.\n *\n * @param {String} component\n * @param {Number} contextid\n * @param {String} itemname\n * @param {Number} gradeduserid\n *\n * @returns {Promise}\n */\nexport const fetchCurrentGrade = (component, contextid, itemname, gradeduserid) => {\n return fetchMany([{\n methodname: `gradingform_rubric_grader_gradingpanel_fetch`,\n args: {\n component,\n contextid,\n itemname,\n gradeduserid,\n },\n }])[0];\n};\n\n/**\n * For a given component, contextid, itemname & gradeduserid we can store the currently assigned grade in a given form.\n *\n * @param {String} component\n * @param {Number} contextid\n * @param {String} itemname\n * @param {Number} gradeduserid\n * @param {HTMLElement} rootNode\n *\n * @returns {Promise}\n */\nexport const storeCurrentGrade = async(component, contextid, itemname, gradeduserid, rootNode) => {\n const form = rootNode.querySelector('form');\n\n return normaliseResult(await fetchMany([{\n methodname: `gradingform_rubric_grader_gradingpanel_store`,\n args: {\n component,\n contextid,\n itemname,\n gradeduserid,\n formdata: jQuery(form).serialize(),\n },\n }])[0]);\n};\n"],"file":"gradingpanel.min.js"}

View File

@ -29,6 +29,16 @@ import {normaliseResult} from 'core_grades/grades/grader/gradingpanel/normalise'
// Note: We use jQuery.serializer here until we can rewrite Ajax to use XHR.send()
import jQuery from 'jquery';
/**
* For a given component, contextid, itemname & gradeduserid we can fetch the currently assigned grade.
*
* @param {String} component
* @param {Number} contextid
* @param {String} itemname
* @param {Number} gradeduserid
*
* @returns {Promise}
*/
export const fetchCurrentGrade = (component, contextid, itemname, gradeduserid) => {
return fetchMany([{
methodname: `gradingform_rubric_grader_gradingpanel_fetch`,
@ -41,7 +51,17 @@ export const fetchCurrentGrade = (component, contextid, itemname, gradeduserid)
}])[0];
};
/**
* For a given component, contextid, itemname & gradeduserid we can store the currently assigned grade in a given form.
*
* @param {String} component
* @param {Number} contextid
* @param {String} itemname
* @param {Number} gradeduserid
* @param {HTMLElement} rootNode
*
* @returns {Promise}
*/
export const storeCurrentGrade = async(component, contextid, itemname, gradeduserid, rootNode) => {
const form = rootNode.querySelector('form');

View File

@ -26,6 +26,8 @@ declare(strict_types = 1);
namespace gradingform_rubric\grades\grader\gradingpanel\external;
global $CFG;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
@ -38,6 +40,7 @@ use external_value;
use external_warnings;
use stdClass;
use moodle_exception;
require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php');
/**
* Web services relating to fetching of a rubric for the grading panel.
@ -90,8 +93,6 @@ class fetch extends external_api {
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
global $USER;
[
'component' => $component,
'contextid' => $contextid,
@ -116,7 +117,7 @@ class fetch extends external_api {
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
if ('rubric' !== $gradeitem->get_advanced_grading_method()) {
if (RUBRIC !== $gradeitem->get_advanced_grading_method()) {
throw new moodle_exception(
"The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a rubric"
);
@ -129,7 +130,7 @@ class fetch extends external_api {
}
/**
* Get the data to be fetched.
* Get the data to be fetched and create the structure ready for Mustache.
*
* @param gradeitem $gradeitem
* @param stdClass $gradeduser
@ -138,6 +139,7 @@ class fetch extends external_api {
public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser): array {
global $USER;
// Set up all the controllers etc that we'll be needing.
$grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
$instance = $gradeitem->get_advanced_grading_instance($USER, $grade);
$controller = $instance->get_controller();
@ -156,7 +158,9 @@ class fetch extends external_api {
$criterion = [];
if ($definition->rubric_criteria) {
// Iterate over the defined criterion in the rubric and map out what we need to render each item.
$criterion = array_map(function($criterion) use ($definitionid, $fillings, $context) {
// The general structure we'll be returning, we still need to get the remark (if any) and the levels associated.
$result = [
'id' => $criterion['id'],
'description' => self::get_formatted_text(
@ -168,6 +172,7 @@ class fetch extends external_api {
),
];
// Do we have an existing grade filling? if so lets get the remark associated to this criteria.
$filling = [];
if (array_key_exists($criterion['id'], $fillings['criteria'])) {
$filling = $fillings['criteria'][$criterion['id']];
@ -179,7 +184,9 @@ class fetch extends external_api {
);
}
// Lets build the levels within a criteria and figure out what needs to go where.
$result['levels'] = array_map(function($level) use ($criterion, $filling, $context, $definitionid) {
// The bulk of what'll be returned can be defined easily we'll add to this further down.
$result = [
'id' => $level['id'],
'criterionid' => $criterion['id'],
@ -194,6 +201,7 @@ class fetch extends external_api {
'checked' => null,
];
// Consult the grade filling to see if a level has been selected and if it is the current level.
if (array_key_exists('levelid', $filling) && $filling['levelid'] == $level['id']) {
$result['checked'] = true;
}
@ -201,6 +209,20 @@ class fetch extends external_api {
return $result;
}, $criterion['levels']);
$nulllevel = [
'id' => null,
'criterionid' => $criterion['id'],
'score' => '-',
'definition' => 'Not set',
'checked' => null,
];
// Consult the grade filling to see if a level has been selected and if it is the current level.
if (array_key_exists('levelid', $filling) && $filling['levelid'] == 0) {
$nulllevel['checked'] = true;
}
array_unshift($result['levels'], $nulllevel);
return $result;
}, $definition->rubric_criteria);
}
@ -241,7 +263,7 @@ class fetch extends external_api {
'levels' => new external_multiple_structure(new external_single_structure([
'id' => new external_value(PARAM_INT, 'ID of level'),
'criterionid' => new external_value(PARAM_INT, 'ID of the criterion this matches to'),
'score' => new external_value(PARAM_INT, 'What this level is worth'),
'score' => new external_value(PARAM_RAW, 'What this level is worth'),
'definition' => new external_value(PARAM_RAW, 'Definition of the level'),
'checked' => new external_value(PARAM_BOOL, 'Selected flag'),
])),

View File

@ -26,6 +26,8 @@ declare(strict_types = 1);
namespace gradingform_rubric\grades\grader\gradingpanel\external;
global $CFG;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
@ -35,6 +37,7 @@ use external_function_parameters;
use external_single_structure;
use external_value;
use moodle_exception;
require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php');
/**
* Web services relating to storing of a rubric for the grading panel.
@ -132,7 +135,7 @@ class store extends external_api {
// Require that this user can save grades.
$gradeitem->require_user_can_grade($gradeduser, $USER);
if ('rubric' !== $gradeitem->get_advanced_grading_method()) {
if (RUBRIC !== $gradeitem->get_advanced_grading_method()) {
throw new moodle_exception(
"The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a rubric"
);

View File

@ -22,6 +22,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$functions = [
'gradingform_rubric_grader_gradingpanel_fetch' => [
'classname' => 'gradingform_rubric\\grades\\grader\\gradingpanel\\external\\fetch',

View File

@ -27,6 +27,9 @@ defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/grade/grading/form/lib.php');
require_once($CFG->dirroot.'/lib/filelib.php');
/** rubric: Used to compare our gradeitem_type against. */
const RUBRIC = 'rubric';
/**
* This controller encapsulates the rubric grading logic
*

View File

@ -18,21 +18,43 @@
@template gradingform_rubric/grades/grader/gradingpanel
Classes required for JS:
* TODO
* none
Data attributes required for JS:
* TODO
* none
Context variables required for this template:
* TODO
* instanceid: Instance of the module this grading form belongs too
* criteria: A gradeable item in the Marking Guide
* id: The ID of the criteria
* description: Description of the criteria
* levels: The level that a criteria can be graded at
* criterionid: The ID of the criteria
* checked: Flag for if this is the currently selected level
* definition: Definition of the level
* remark: Text input for the teacher to relay to the student
Example context (json):
{
"instanceid": "42",
"criteria": [
{
"id": 13,
"description": "Show your motivation to rock climbing",
"levels": [
{
"criterionid": 13,
"checked": true,
"definition": "Great work!"
}
],
"remark": "That's great!"
}
]
}
}}
<form id="gradingform_rubric-{{uniqid}}">
<input type="hidden" name="instanceid" value="{{instanceid}}">
<div class="gradingform_rubric-description">{{{teacherdescription}}}</div>
<div id="rubric-advancedgrading-{{uniqid}}" class="criterion">
{{#criteria}}
<div class="d-block mb-2">

View File

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Coverage information for the gradingform_rubric plugin.
*
@ -25,6 +23,8 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Coverage information for the gradingform_rubric plugin.
*
@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
return new class extends phpunit_coverage_info {
/** @var array The list of folders relative to the plugin root to whitelist in coverage generation. */
// Array The list of folders relative to the plugin root to whitelist in coverage generation.
protected $whitelistfolders = [
'classes',
'tests/generator',

View File

@ -17,7 +17,7 @@
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @package gradingform_rubric
* @category test
* @copyright 2019 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
@ -37,7 +37,7 @@ use moodle_exception;
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @package gradingform_rubric
* @category test
* @copyright 2019 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@ -150,6 +150,9 @@ class fetch_test extends advanced_testcase {
$levels = $criterion['levels'];
foreach ($levels as $level) {
$levelid = $level['id'];
if (!isset($levelid)) {
continue;
}
$sourcelevel = $sourcecriterion['levels'][$levelid];
$this->assertArrayHasKey('criterionid', $level);
@ -235,6 +238,9 @@ class fetch_test extends advanced_testcase {
$levels = $criterion['levels'];
foreach ($levels as $level) {
$levelid = $level['id'];
if (!isset($levelid)) {
continue;
}
$sourcelevel = $sourcecriterion['levels'][$levelid];
$this->assertArrayHasKey('criterionid', $level);
@ -251,9 +257,9 @@ class fetch_test extends advanced_testcase {
}
$this->assertEquals(1, $criteria[0]['levels'][0]['checked']);
$this->assertEquals(1, $criteria[0]['levels'][1]['checked']);
$this->assertEquals('Too many mistakes. Please try again.', $criteria[0]['remark']);
$this->assertEquals(1, $criteria[1]['levels'][2]['checked']);
$this->assertEquals(1, $criteria[1]['levels'][3]['checked']);
$this->assertEquals('Great number of pictures. Well done.', $criteria[1]['remark']);
}

View File

@ -17,7 +17,7 @@
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @package gradingform_rubric
* @category test
* @copyright 2019 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
@ -37,7 +37,7 @@ use moodle_exception;
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @package gradingform_rubric
* @category test
* @copyright 2019 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@ -92,7 +92,6 @@ class store_test extends advanced_testcase {
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$this->expectException(moodle_exception::class);
//$this->expectExceptionMessage("not configured for advanced grading with a rubric");
store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, 'formdata');
}
@ -184,6 +183,9 @@ class store_test extends advanced_testcase {
$levels = $criterion['levels'];
foreach ($levels as $level) {
$levelid = $level['id'];
if (!isset($levelid)) {
continue;
}
$sourcelevel = $sourcecriterion['levels'][$levelid];
$this->assertArrayHasKey('criterionid', $level);
@ -200,9 +202,9 @@ class store_test extends advanced_testcase {
}
$this->assertEquals(1, $criteria[0]['levels'][0]['checked']);
$this->assertEquals(1, $criteria[0]['levels'][1]['checked']);
$this->assertEquals('Too many mistakes. Please try again.', $criteria[0]['remark']);
$this->assertEquals(1, $criteria[1]['levels'][2]['checked']);
$this->assertEquals(1, $criteria[1]['levels'][3]['checked']);
$this->assertEquals('Great number of pictures. Well done.', $criteria[1]['remark']);
}