MDL-66700 gradingform_guide: Support new grading panel

Part of MDL-66074
This commit is contained in:
Andrew Nicols 2019-10-01 15:28:24 +08:00 committed by Mathew May
parent 57732a1c8e
commit 38f3bd93af
22 changed files with 1253 additions and 4 deletions

View File

@ -0,0 +1,2 @@
define ("gradingform_guide/grades/grader/gradingpanel",["exports","core/ajax","jquery"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.storeCurrentGrade=a.fetchCurrentGrade=void 0;c=function(a){return a&&a.__esModule?a:{default:a}}(c);a.fetchCurrentGrade=function fetchCurrentGrade(a,c,d,e){return(0,b.call)([{methodname:"gradingform_guide_grader_gradingpanel_fetch",args:{component:a,contextid:c,itemname:d,gradeduserid:e}}])[0]};var d=function(a,d,e,f,g){var h=g.querySelector("form");return(0,b.call)([{methodname:"gradingform_guide_grader_gradingpanel_store",args:{component:a,contextid:d,itemname:e,gradeduserid:f,formdata:(0,c.default)(h).serialize()}}])[0]};a.storeCurrentGrade=d});
//# sourceMappingURL=gradingpanel.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../../../src/grades/grader/gradingpanel.js"],"names":["fetchCurrentGrade","component","contextid","itemname","gradeduserid","methodname","args","storeCurrentGrade","rootNode","form","querySelector","formdata","serialize"],"mappings":"qNA2BA,uD,oBAEiC,QAApBA,CAAAA,iBAAoB,CAACC,CAAD,CAAYC,CAAZ,CAAuBC,CAAvB,CAAiCC,CAAjC,CAAkD,CAC/E,MAAO,WAAU,CAAC,CACdC,UAAU,8CADI,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,CAAG,SAACN,CAAD,CAAYC,CAAZ,CAAuBC,CAAvB,CAAiCC,CAAjC,CAA+CI,CAA/C,CAA4D,CACzF,GAAMC,CAAAA,CAAI,CAAGD,CAAQ,CAACE,aAAT,CAAuB,MAAvB,CAAb,CAEA,MAAO,WAAU,CAAC,CACdL,UAAU,8CADI,CAEdC,IAAI,CAAE,CACFL,SAAS,CAATA,CADE,CAEFC,SAAS,CAATA,CAFE,CAGFC,QAAQ,CAARA,CAHE,CAIFC,YAAY,CAAZA,CAJE,CAKFO,QAAQ,CAAE,cAAOF,CAAP,EAAaG,SAAb,EALR,CAFQ,CAAD,CAAV,EASH,CATG,CAUV,CAbM,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_guide.\n *\n * @module gradingform_guide/grades/grader/gradingpanel\n * @package gradingform_guide\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\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_guide_grader_gradingpanel_fetch`,\n args: {\n component,\n contextid,\n itemname,\n gradeduserid,\n },\n }])[0];\n};\n\n\nexport const storeCurrentGrade = (component, contextid, itemname, gradeduserid, rootNode) => {\n const form = rootNode.querySelector('form');\n\n return fetchMany([{\n methodname: `gradingform_guide_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

@ -0,0 +1,2 @@
define ("gradingform_guide/grades/grader/gradingpanel/comments",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;a.init=function init(a){var b=document.querySelector("#".concat(a));b.addEventListener("click",function(a){if(!a.target.matches("[data-gradingform_guide-role=\"frequent-comment\"]")){return}a.preventDefault();var b=a.target.closest("[data-gradingform_guide-role=\"frequent-comment\"]"),c=b.closest("[data-gradingform-guide-role=\"criterion\"]"),d=c.querySelector("[data-gradingform-guide-role=\"remark\"]");if(!d){return}if(d.value.trim()){d.value+="\n".concat(b.innerHTML)}else{d.value+=b.innerHTML}})}});
//# sourceMappingURL=comments.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/grades/grader/gradingpanel/comments.js"],"names":["init","rootId","rootNode","document","querySelector","addEventListener","e","target","matches","preventDefault","clicked","closest","criterion","remark","value","trim","innerHTML"],"mappings":"2KAwBoB,QAAPA,CAAAA,IAAO,CAACC,CAAD,CAAY,CAC5B,GAAMC,CAAAA,CAAQ,CAAGC,QAAQ,CAACC,aAAT,YAA2BH,CAA3B,EAAjB,CAEAC,CAAQ,CAACG,gBAAT,CAA0B,OAA1B,CAAmC,SAACC,CAAD,CAAO,CACtC,GAAI,CAACA,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB,oDAAjB,CAAL,CAA2E,CACvE,MACH,CAEDF,CAAC,CAACG,cAAF,GALsC,GAOhCC,CAAAA,CAAO,CAAGJ,CAAC,CAACC,MAAF,CAASI,OAAT,CAAiB,oDAAjB,CAPsB,CAQhCC,CAAS,CAAGF,CAAO,CAACC,OAAR,CAAgB,6CAAhB,CARoB,CAShCE,CAAM,CAAGD,CAAS,CAACR,aAAV,CAAwB,0CAAxB,CATuB,CAWtC,GAAI,CAACS,CAAL,CAAa,CACT,MACH,CAED,GAAIA,CAAM,CAACC,KAAP,CAAaC,IAAb,EAAJ,CAAyB,CACrBF,CAAM,CAACC,KAAP,cAAqBJ,CAAO,CAACM,SAA7B,CACH,CAFD,IAEO,CACHH,CAAM,CAACC,KAAP,EAAgBJ,CAAO,CAACM,SAC3B,CACJ,CApBD,CAqBH,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 frequently used comments selector.\n *\n * @module gradingform_guide/grades/grader/gradingpanel/comments\n * @package gradingform_guide\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const init = (rootId) => {\n const rootNode = document.querySelector(`#${rootId}`);\n\n rootNode.addEventListener('click', (e) => {\n if (!e.target.matches('[data-gradingform_guide-role=\"frequent-comment\"]')) {\n return;\n }\n\n e.preventDefault();\n\n const clicked = e.target.closest('[data-gradingform_guide-role=\"frequent-comment\"]');\n const criterion = clicked.closest('[data-gradingform-guide-role=\"criterion\"]');\n const remark = criterion.querySelector('[data-gradingform-guide-role=\"remark\"]');\n\n if (!remark) {\n return;\n }\n\n if (remark.value.trim()) {\n remark.value += `\\n${clicked.innerHTML}`;\n } else {\n remark.value += clicked.innerHTML;\n }\n });\n};\n"],"file":"comments.min.js"}

View File

@ -0,0 +1,56 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Grading panel for gradingform_guide.
*
* @module gradingform_guide/grades/grader/gradingpanel
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {call as fetchMany} from 'core/ajax';
// Note: We use jQuery.serializer here until we can rewrite Ajax to use XHR.send()
import jQuery from 'jquery';
export const fetchCurrentGrade = (component, contextid, itemname, gradeduserid) => {
return fetchMany([{
methodname: `gradingform_guide_grader_gradingpanel_fetch`,
args: {
component,
contextid,
itemname,
gradeduserid,
},
}])[0];
};
export const storeCurrentGrade = (component, contextid, itemname, gradeduserid, rootNode) => {
const form = rootNode.querySelector('form');
return fetchMany([{
methodname: `gradingform_guide_grader_gradingpanel_store`,
args: {
component,
contextid,
itemname,
gradeduserid,
formdata: jQuery(form).serialize(),
},
}])[0];
};

View File

@ -0,0 +1,49 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Grading panel frequently used comments selector.
*
* @module gradingform_guide/grades/grader/gradingpanel/comments
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export const init = (rootId) => {
const rootNode = document.querySelector(`#${rootId}`);
rootNode.addEventListener('click', (e) => {
if (!e.target.matches('[data-gradingform_guide-role="frequent-comment"]')) {
return;
}
e.preventDefault();
const clicked = e.target.closest('[data-gradingform_guide-role="frequent-comment"]');
const criterion = clicked.closest('[data-gradingform-guide-role="criterion"]');
const remark = criterion.querySelector('[data-gradingform-guide-role="remark"]');
if (!remark) {
return;
}
if (remark.value.trim()) {
remark.value += `\n${clicked.innerHTML}`;
} else {
remark.value += clicked.innerHTML;
}
});
};

View File

@ -0,0 +1,283 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Web services relating to fetching of a marking guide for the grading panel.
*
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace gradingform_guide\grades\grader\gradingpanel\external;
use coding_exception;
use context;
use core_user;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use external_api;
use external_format_value;
use external_function_parameters;
use external_multiple_structure;
use external_single_structure;
use external_value;
use external_warnings;
use moodle_exception;
use stdClass;
/**
* Web services relating to fetching of a marking guide for the grading panel.
*
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fetch extends external_api {
/**
* Describes the parameters for fetching the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @return array
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
global $USER;
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
if ('guide' !== $gradeitem->get_advanced_grading_method()) {
throw new moodle_exception(
"The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a marking guide"
);
}
// Fetch the actual data.
$gradeduser = core_user::get_user($gradeduserid);
return self::get_fetch_data($gradeitem, $gradeduser);
}
/**
* Get the data to be fetched.
*
* @param component_gradeitem $gradeitem
* @return array
*/
public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser): array {
global $USER;
$grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
$instance = $gradeitem->get_advanced_grading_instance($USER, $grade);
$controller = $instance->get_controller();
$definition = $controller->get_definition();
$fillings = $instance->get_guide_filling();
$context = $controller->get_context();
$definitionid = (int) $definition->id;
$criterion = [];
if ($definition->guide_criteria) {
$criterion = array_map(function($criterion) use ($definitionid, $fillings, $context) {
$result = [
'id' => $criterion['id'],
'name' => $criterion['shortname'],
'maxscore' => $criterion['maxscore'],
'description' => self::get_formatted_text(
$context,
$definitionid,
'description',
$criterion['description'],
(int) $criterion['descriptionformat']
),
'descriptionmarkers' => self::get_formatted_text(
$context,
$definitionid,
'descriptionmarkers',
$criterion['descriptionmarkers'],
(int) $criterion['descriptionmarkersformat']
),
'score' => null,
'remark' => null,
];
if (array_key_exists($criterion['id'], $fillings['criteria'])) {
$filling = $fillings['criteria'][$criterion['id']];
$result['score'] = $filling['score'];
$result['remark'] = self::get_formatted_text(
$context,
$definitionid,
'remark',
$filling['remark'],
(int) $filling['remarkformat']
);
}
return $result;
}, $definition->guide_criteria);
}
$comments = [];
if ($definition->guide_comments) {
$comments = array_map(function($comment) use ($definitionid, $context) {
return [
'id' => $comment['id'],
'sortorder' => $comment['sortorder'],
'description' => self::get_formatted_text(
$context,
$definitionid,
'description',
$comment['description'],
(int) $comment['descriptionformat']
),
];
}, $definition->guide_comments);
}
return [
'templatename' => 'gradingform_guide/grades/grader/gradingpanel',
'grade' => [
'instanceid' => $instance->get_id(),
'criterion' => $criterion,
'hascomments' => !empty($comments),
'comments' => $comments,
'timecreated' => $grade->timecreated,
'timemodified' => $grade->timemodified,
],
'warnings' => [],
];
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'templatename' => new external_value(PARAM_SAFEPATH, 'The template to use when rendering this data'),
'grade' => new external_single_structure([
'instanceid' => new external_value(PARAM_INT, 'The id of the current grading instance'),
'criterion' => new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_INT, 'The id of the criterion'),
'name' => new external_value(PARAM_RAW, 'The name of the criterion'),
'maxscore' => new external_value(PARAM_FLOAT, 'The maximum score for this criterion'),
'description' => new external_value(PARAM_RAW, 'The description of the criterion'),
'descriptionmarkers' => new external_value(PARAM_RAW, 'The description of the criterion for markers'),
'score' => new external_value(PARAM_FLOAT, 'The current score for user being assessed', VALUE_OPTIONAL),
'remark' => new external_value(PARAM_RAW, 'Any remarks for this criterion for the user being assessed', VALUE_OPTIONAL),
]),
'The criterion by which this item will be graded'
),
'hascomments' => new external_value(PARAM_BOOL, 'Whether there are any frequently-used comments'),
'comments' => new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_INT, 'Comment id'),
'sortorder' => new external_value(PARAM_INT, 'The sortorder of this comment'),
'description' => new external_value(PARAM_RAW, 'The comment value'),
]),
'Frequently used comments'
),
'timecreated' => new external_value(PARAM_INT, 'The time that the grade was created'),
'timemodified' => new external_value(PARAM_INT, 'The time that the grade was last updated'),
]),
'warnings' => new external_warnings(),
]);
}
/**
* Get a formatted version of the remark/description/etc.
*
* @param context $context
* @param int $definitionid
* @param string $filearea The file area of the field
* @param string $text The text to be formatted
* @param int $format The input format of the string
* @return string
*/
protected static function get_formatted_text(context $context, int $definitionid, string $filearea, string $text, int $format): string {
$formatoptions = [
'noclean' => false,
'trusted' => false,
'filter' => true,
];
[$newtext, ] = external_format_text($text, $format, $context, 'grading', $filearea, $definitionid, $formatoptions);
return $newtext;
}
}

View File

@ -0,0 +1,160 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Web services relating to fetching of a marking guide for the grading panel.
*
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace gradingform_guide\grades\grader\gradingpanel\external;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use core_user;
use external_api;
use external_function_parameters;
use external_single_structure;
use external_value;
use moodle_exception;
/**
* Web services relating to storing of a marking guide for the grading panel.
*
* @package gradingform_guide
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class store extends external_api {
/**
* Describes the parameters for storing the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
'formdata' => new external_value(
PARAM_RAW,
'The serialised form data representing the grade',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @return array
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid, string $formdata): array {
global $USER;
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'formdata' => $formdata,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'formdata' => $formdata,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
// Validate that this gradeitem is actually enabled.
if (!$gradeitem->is_grading_enabled()) {
throw new moodle_exception("Grading is not enabled for {$itemname} in this context");
}
// Fetch the record for the graded user.
$gradeduser = core_user::get_user($gradeduserid);
// Require that this user can save grades.
$gradeitem->require_user_can_grade($gradeduser, $USER);
if ('guide' !== $gradeitem->get_advanced_grading_method()) {
throw new moodle_exception(
"The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a marking guide"
);
}
// Parse the serialised string into an object.
$data = [];
parse_str($formdata, $data);
// Grade.
$gradeitem->store_grade_from_formdata($gradeduser, $USER, (object) $data);
return fetch::get_fetch_data($gradeitem, $gradeduser);
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return fetch::execute_returns();
}
}

View File

@ -0,0 +1,42 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* External functions and service definitions for the Marking Guide advanced grading form.
*
* @package mod_forum
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$functions = [
'gradingform_guide_grader_gradingpanel_fetch' => [
'classname' => 'gradingform_guide\\grades\\grader\\gradingpanel\\external\\fetch',
'methodname' => 'execute',
'description' => 'Fetch the data required to display the grader grading panel, ' .
'creating the grade item if required',
'type' => 'write',
'ajax' => true,
],
'gradingform_guide_grader_gradingpanel_store' => [
'classname' => 'gradingform_guide\\grades\\grader\\gradingpanel\\external\\store',
'methodname' => 'execute',
'description' => 'Store the grading data for a user from the grader grading panel.',
'type' => 'write',
'ajax' => true,
],
];

View File

@ -25,6 +25,7 @@
defined('MOODLE_INTERNAL') || die();
$string['addcomment'] = 'Add frequently used comment';
$string['additionalcomments'] = 'Additional comments';
$string['addcriterion'] = 'Add criterion';
$string['alwaysshowdefinition'] = 'Show guide definition to students';
$string['backtoediting'] = 'Back to editing';
@ -73,6 +74,7 @@ $string['insertcomment'] = 'Insert frequently used comment';
$string['maxscore'] = 'Maximum score';
$string['name'] = 'Name';
$string['needregrademessage'] = 'The marking guide definition was changed after this student had been graded. The student can not see this marking guide until you check the marking guide and update the grade.';
$string['outof'] = 'Out of {$a}';
$string['pluginname'] = 'Marking guide';
$string['previewmarkingguide'] = 'Preview marking guide';
$string['privacy:metadata:criterionid'] = 'An identifier to a criterion for advanced marking.';

View File

@ -997,3 +997,15 @@ class gradingform_guide_instance extends gradingform_instance {
return $html;
}
}
/**
* Get the icon mapping for font-awesome.
*
* @return array
*/
function gradingform_guide_get_fontawesome_icon_map(): array {
return [
'gradingform_guide:info' => 'fa-info-circle',
'gradingform_guide:plus' => 'fa-plus',
];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 14c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm1.2-2c0 .5-.5 1-1 1h-.4c-.5 0-1-.5-1-1V7.4c0-.5.5-1 1-1h.5c.5 0 1 .5 1 1V12zm0-7.8c0 .7-.6 1.2-1.2 1.2-.7 0-1.2-.6-1.2-1.2C6.8 3.5 7.3 3 8 3s1.2.5 1.2 1.2z" fill="#999"/></svg>

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

View File

@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M11 4.5H7.5V1c0-.5-.5-1-1-1h-1c-.5 0-1 .5-1 1v3.5H1c-.5 0-1 .5-1 1v1c0 .5.5 1 1 1h3.5V11c0 .5.5 1 1 1h1c.5 0 1-.5 1-1V7.5H11c.6 0 1-.5 1-1v-1c0-.5-.4-1-1-1z" fill="#999"/></svg>

After

Width:  |  Height:  |  Size: 481 B

View File

@ -241,3 +241,8 @@
max-height: 80vh;
overflow-y: auto;
}
.gradingform_guide-frequent-comments {
position: absolute;
top: 7px;
right: 0px;
}

View File

@ -0,0 +1,5 @@
.gradingform_guide-fac {
position: absolute;
right: -5px;
top: 5px;
}

View File

@ -0,0 +1,74 @@
<form id="gradingform_guide-{{uniqid}}">
<input type="hidden" name="instanceid" value="{{instanceid}}">
{{#criterion}}
<div data-gradingform-guide-role="criterion">
<h5>
{{name}}
<a
href="#gradingform_guide-{{uniqid}}-criteria-{{id}}-description"
aria-controls="gradingform_guide-{{uniqid}}-criteria-{{id}}-description"
aria-expanded="false"
data-toggle="collapse"
role="button"
>
{{# pix }} info, gradingform_guide {{/ pix }}
</a>
</h5>
<div class="collapse" id="gradingform_guide-{{uniqid}}-criteria-{{id}}-description">
<div class="border p-3 mb-3 bg-white rounded">
{{{description}}}
{{#descriptionmarkers}}
<hr>
{{{descriptionmarkers}}}
{{/descriptionmarkers}}
</div>
</div>
<div class="form-group">
<label for="gradingform_guide-{{uniqid}}-criteria-{{id}}-score">{{#str}}outof, gradingform_guide, {{maxscore}}{{/str}}</label>
<input class="form-control" type="number" name="advancedgrading[criteria][{{id}}][score]" value="{{score}}"
id="gradingform_guide-{{uniqid}}-criteria-{{id}}-score"
aria-describedby="gradingform_guide-{{uniqid}}-help-{{id}}-score">
<small id="gradingform_guide-{{uniqid}}-help-{{id}}-score" class="sr-only">{{#str}}grade_help, gradingform_guide{{/str}}</small>
</div>
<div class="form-group ">
<label for="gradingform_guide-{{uniqid}}-criteria-{{id}}-remark">{{#str}}additionalcomments, gradingform_guide{{/str}}</label>
<div class="input-group mb-3 form-inset form-inset-right">
<textarea class="form-control" type="text" name="advancedgrading[criteria][{{id}}][remark]"
id="gradingform_guide-{{uniqid}}-criteria-{{id}}-remark"
aria-describedby="gradingform_guide-{{uniqid}}-help-{{id}}-remark"
data-gradingform-guide-role="remark"
>{{remark}}</textarea>
{{#hascomments}}
<a
class="form-inset-item"
href="#gradingform_guide-{{uniqid}}-criteria-{{id}}-remark-frequent-comments"
aria-controls="gradingform_guide-{{uniqid}}-criteria-{{id}}-remark-frequent-comments"
aria-expanded="false"
data-toggle="collapse"
role="button"
>
{{#pix}}plus, gradingform_guide{{/pix}}
</a>
{{/hascomments}}
</div>
{{#hascomments}}
<div class="collapse" id="gradingform_guide-{{uniqid}}-criteria-{{id}}-remark-frequent-comments">
<div data-gradingform_guide-frequent-comments="gradingform_guide-{{uniqid}}-criteria-{{id}}-remark">
<div class="list-group">
{{#comments}}
<button type="button" class="list-group-item list-group-item-action" data-gradingform_guide-role="frequent-comment">{{description}}</button>
{{/comments}}
</div>
</div>
</div>
{{/hascomments}}
<small id="gradingform_guide-{{uniqid}}-help-{{id}}-remark" class="sr-only">{{#str}}grade_help, gradingform_guide{{/str}}</small>
</div>
</div>
{{/criterion}}
</form>
{{#js}}
require(['gradingform_guide/grades/grader/gradingpanel/comments'], function(Comments) {
Comments.init('gradingform_guide-{{uniqid}}');
});
{{/js}}

View File

@ -173,10 +173,14 @@ class gradingform_guide_generator extends component_generator_base {
* @param context_module $context
* @return gradingform_guide_controller
*/
public function get_test_guide(context_module $context): gradingform_guide_controller {
public function get_test_guide(
context_module $context,
string $component = 'mod_assign',
string $areaname = 'submission'
): gradingform_guide_controller {
$generator = \testing_util::get_data_generator();
$gradinggenerator = $generator->get_plugin_generator('core_grading');
$controller = $gradinggenerator->create_instance($context, 'mod_assign', 'submission', 'guide');
$controller = $gradinggenerator->create_instance($context, $component, $areaname, 'guide');
$generator = \testing_util::get_data_generator();
$guidegenerator = $generator->get_plugin_generator('gradingform_guide');

View File

@ -0,0 +1,296 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
declare(strict_types = 1);
namespace gradingform_guide\grades\grader\gradingpanel\external;
use advanced_testcase;
use coding_exception;
use core_grades\component_gradeitem;
use external_api;
use mod_forum\local\entities\forum as forum_entity;
use moodle_exception;
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fetch_test extends advanced_testcase {
public static function setupBeforeClass(): void {
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
}
/**
* Ensure that an execute with an invalid component is rejected.
*/
public function test_execute_invalid_component(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(coding_exception::class);
$this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_invalid' component");
fetch::execute('mod_invalid', 1, 'foo', 2);
}
/**
* Ensure that an execute with an invalid itemname on a valid component is rejected.
*/
public function test_execute_invalid_itemname(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(coding_exception::class);
$this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_forum' component");
fetch::execute('mod_forum', 1, 'foo', 2);
}
/**
* Ensure that an execute against a different grading method is rejected.
*/
public function test_execute_incorrect_type(): void {
$this->resetAfterTest();
$forum = $this->get_forum_instance([
// Negative numbers mean a scale.
'grade_forum' => 5,
]);
$course = $forum->get_course_record();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$this->expectException(moodle_exception::class);
$this->expectExceptionMessage("not configured for advanced grading with a marking guide");
fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id);
}
/**
* Ensure that an execute against the correct grading method returns the current state of the user.
*/
public function test_execute_fetch_empty(): void {
$this->resetAfterTest();
[
'forum' => $forum,
'controller' => $controller,
'definition' => $definition,
'student' => $student,
'teacher' => $teacher,
] = $this->get_test_data();
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$result = fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id);
$result = external_api::clean_returnvalue(fetch::execute_returns(), $result);
$this->assertIsArray($result);
$this->assertArrayHasKey('templatename', $result);
$this->assertEquals('gradingform_guide/grades/grader/gradingpanel', $result['templatename']);
$this->assertArrayHasKey('grade', $result);
$this->assertIsArray($result['grade']);
$this->assertIsInt($result['grade']['timecreated']);
$this->assertArrayHasKey('timemodified', $result['grade']);
$this->assertIsInt($result['grade']['timemodified']);
$this->assertArrayHasKey('warnings', $result);
$this->assertIsArray($result['warnings']);
$this->assertEmpty($result['warnings']);
$this->assertArrayHasKey('criterion', $result['grade']);
$criteria = $result['grade']['criterion'];
$this->assertCount(count($definition->guide_criteria), $criteria);
foreach ($criteria as $criterion) {
$this->assertArrayHasKey('id', $criterion);
$criterionid = $criterion['id'];
$sourcecriterion = $definition->guide_criteria[$criterionid];
$this->assertArrayHasKey('name', $criterion);
$this->assertEquals($sourcecriterion['shortname'], $criterion['name']);
$this->assertArrayHasKey('maxscore', $criterion);
$this->assertEquals($sourcecriterion['maxscore'], $criterion['maxscore']);
$this->assertArrayHasKey('description', $criterion);
$this->assertEquals($sourcecriterion['description'], $criterion['description']);
$this->assertArrayHasKey('descriptionmarkers', $criterion);
$this->assertEquals($sourcecriterion['descriptionmarkers'], $criterion['descriptionmarkers']);
$this->assertArrayHasKey('score', $criterion);
$this->assertEmpty($criterion['score']);
$this->assertArrayHasKey('remark', $criterion);
$this->assertEmpty($criterion['remark']);
}
}
/**
* Ensure that an execute against the correct grading method returns the current state of the user.
*/
public function test_execute_fetch_graded(): void {
$this->resetAfterTest();
$generator = \testing_util::get_data_generator();
$guidegenerator = $generator->get_plugin_generator('gradingform_guide');
[
'forum' => $forum,
'controller' => $controller,
'definition' => $definition,
'student' => $student,
'teacher' => $teacher,
] = $this->get_test_data();
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$grade = $gradeitem->get_grade_for_user($student, $teacher);
$instance = $gradeitem->get_advanced_grading_instance($teacher, $grade);
$submissiondata = $guidegenerator->get_test_form_data($controller, (int) $student->id,
10, 'Propper good speling',
0, 'ASCII art is not a picture'
);
$gradeitem->store_grade_from_formdata($student, $teacher, (object) [
'instanceid' => $instance->get_id(),
'advancedgrading' => $submissiondata,
]);
$result = fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id);
$result = external_api::clean_returnvalue(fetch::execute_returns(), $result);
$this->assertIsArray($result);
$this->assertArrayHasKey('templatename', $result);
$this->assertEquals('gradingform_guide/grades/grader/gradingpanel', $result['templatename']);
$this->assertArrayHasKey('grade', $result);
$this->assertIsArray($result['grade']);
$this->assertIsInt($result['grade']['timecreated']);
$this->assertArrayHasKey('timemodified', $result['grade']);
$this->assertIsInt($result['grade']['timemodified']);
$this->assertArrayHasKey('warnings', $result);
$this->assertIsArray($result['warnings']);
$this->assertEmpty($result['warnings']);
$this->assertArrayHasKey('criterion', $result['grade']);
$criteria = $result['grade']['criterion'];
$this->assertCount(count($definition->guide_criteria), $criteria);
foreach ($criteria as $criterion) {
$this->assertArrayHasKey('id', $criterion);
$criterionid = $criterion['id'];
$sourcecriterion = $definition->guide_criteria[$criterionid];
$this->assertArrayHasKey('name', $criterion);
$this->assertEquals($sourcecriterion['shortname'], $criterion['name']);
$this->assertArrayHasKey('maxscore', $criterion);
$this->assertEquals($sourcecriterion['maxscore'], $criterion['maxscore']);
$this->assertArrayHasKey('description', $criterion);
$this->assertEquals($sourcecriterion['description'], $criterion['description']);
$this->assertArrayHasKey('descriptionmarkers', $criterion);
$this->assertEquals($sourcecriterion['descriptionmarkers'], $criterion['descriptionmarkers']);
$this->assertArrayHasKey('score', $criterion);
$this->assertArrayHasKey('remark', $criterion);
}
$this->assertEquals(10, $criteria[0]['score']);
$this->assertEquals('Propper good speling', $criteria[0]['remark']);
$this->assertEquals(0, $criteria[1]['score']);
$this->assertEquals('ASCII art is not a picture', $criteria[1]['remark']);
}
/**
* Get a forum instance.
*
* @param array $config
* @return forum_entity
*/
protected function get_forum_instance(array $config = []): forum_entity {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id]));
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
return $vault->get_from_id((int) $forum->id);
}
/**
* Get test data for forums graded using a marking guide.
*
* @return array
*/
protected function get_test_data(): array {
global $DB;
$this->resetAfterTest();
$generator = \testing_util::get_data_generator();
$guidegenerator = $generator->get_plugin_generator('gradingform_guide');
$forum = $this->get_forum_instance();
$course = $forum->get_course_record();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($teacher);
$controller = $guidegenerator->get_test_guide($forum->get_context(), 'forum', 'forum');
$definition = $controller->get_definition();
$DB->set_field('forum', 'grade_forum', count($definition->guide_criteria), ['id' => $forum->get_id()]);
return [
'forum' => $forum,
'controller' => $controller,
'definition' => $definition,
'student' => $student,
'teacher' => $teacher,
];
}
}

View File

@ -0,0 +1,249 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
declare(strict_types = 1);
namespace gradingform_guide\grades\grader\gradingpanel\external;
use advanced_testcase;
use coding_exception;
use core_grades\component_gradeitem;
use external_api;
use mod_forum\local\entities\forum as forum_entity;
use moodle_exception;
/**
* Unit tests for core_grades\component_gradeitems;
*
* @package core_grades
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class store_test extends advanced_testcase {
public static function setupBeforeClass(): void {
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
}
/**
* Ensure that an execute with an invalid component is rejected.
*/
public function test_execute_invalid_component(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(coding_exception::class);
$this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_invalid' component");
store::execute('mod_invalid', 1, 'foo', 2, 'formdata');
}
/**
* Ensure that an execute with an invalid itemname on a valid component is rejected.
*/
public function test_execute_invalid_itemname(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(coding_exception::class);
$this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_forum' component");
store::execute('mod_forum', 1, 'foo', 2, 'formdata');
}
/**
* Ensure that an execute against a different grading method is rejected.
*/
public function test_execute_incorrect_type(): void {
$this->resetAfterTest();
$forum = $this->get_forum_instance([
'grade_forum' => 5,
]);
$course = $forum->get_course_record();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$this->expectException(moodle_exception::class);
$this->expectExceptionMessage("not configured for advanced grading with a marking guide");
store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, 'formdata');
}
/**
* Ensure that an execute against a different grading method is rejected.
*/
public function test_execute_disabled(): void {
$this->resetAfterTest();
$forum = $this->get_forum_instance();
$course = $forum->get_course_record();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$this->expectException(moodle_exception::class);
$this->expectExceptionMessage("Grading is not enabled");
store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, 'formdata');
}
/**
* Ensure that an execute against the correct grading method returns the current state of the user.
*/
public function test_execute_store_graded(): void {
$this->resetAfterTest();
$generator = \testing_util::get_data_generator();
$guidegenerator = $generator->get_plugin_generator('gradingform_guide');
[
'forum' => $forum,
'controller' => $controller,
'definition' => $definition,
'student' => $student,
'teacher' => $teacher,
] = $this->get_test_data();
$this->setUser($teacher);
$gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
$grade = $gradeitem->get_grade_for_user($student, $teacher);
$instance = $gradeitem->get_advanced_grading_instance($teacher, $grade);
$submissiondata = $guidegenerator->get_test_form_data($controller, (int) $student->id,
10, 'Propper good speling',
0, 'ASCII art is not a picture'
);
$formdata = http_build_query((object) [
'instanceid' => $instance->get_id(),
'advancedgrading' => $submissiondata,
], '', '&');
$result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, $formdata);
$result = external_api::clean_returnvalue(store::execute_returns(), $result);
$this->assertIsArray($result);
$this->assertArrayHasKey('templatename', $result);
$this->assertEquals('gradingform_guide/grades/grader/gradingpanel', $result['templatename']);
$this->assertArrayHasKey('grade', $result);
$this->assertIsArray($result['grade']);
$this->assertIsInt($result['grade']['timecreated']);
$this->assertArrayHasKey('timemodified', $result['grade']);
$this->assertIsInt($result['grade']['timemodified']);
$this->assertArrayHasKey('warnings', $result);
$this->assertIsArray($result['warnings']);
$this->assertEmpty($result['warnings']);
$this->assertArrayHasKey('criterion', $result['grade']);
$criteria = $result['grade']['criterion'];
$this->assertCount(count($definition->guide_criteria), $criteria);
foreach ($criteria as $criterion) {
$this->assertArrayHasKey('id', $criterion);
$criterionid = $criterion['id'];
$sourcecriterion = $definition->guide_criteria[$criterionid];
$this->assertArrayHasKey('name', $criterion);
$this->assertEquals($sourcecriterion['shortname'], $criterion['name']);
$this->assertArrayHasKey('maxscore', $criterion);
$this->assertEquals($sourcecriterion['maxscore'], $criterion['maxscore']);
$this->assertArrayHasKey('description', $criterion);
$this->assertEquals($sourcecriterion['description'], $criterion['description']);
$this->assertArrayHasKey('descriptionmarkers', $criterion);
$this->assertEquals($sourcecriterion['descriptionmarkers'], $criterion['descriptionmarkers']);
$this->assertArrayHasKey('score', $criterion);
$this->assertArrayHasKey('remark', $criterion);
}
$this->assertEquals(10, $criteria[0]['score']);
$this->assertEquals('Propper good speling', $criteria[0]['remark']);
$this->assertEquals(0, $criteria[1]['score']);
$this->assertEquals('ASCII art is not a picture', $criteria[1]['remark']);
}
/**
* Get a forum instance.
*
* @param array $config
* @return forum_entity
*/
protected function get_forum_instance(array $config = []): forum_entity {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
$forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id]));
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$vault = $vaultfactory->get_forum_vault();
return $vault->get_from_id((int) $forum->id);
}
/**
* Get test data for forums graded using a marking guide.
*
* @return array
*/
protected function get_test_data(): array {
global $DB;
$this->resetAfterTest();
$generator = \testing_util::get_data_generator();
$guidegenerator = $generator->get_plugin_generator('gradingform_guide');
$forum = $this->get_forum_instance();
$course = $forum->get_course_record();
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($teacher);
$controller = $guidegenerator->get_test_guide($forum->get_context(), 'forum', 'forum');
$definition = $controller->get_definition();
$DB->set_field('forum', 'grade_forum', count($definition->guide_criteria), ['id' => $forum->get_id()]);
return [
'forum' => $forum,
'controller' => $controller,
'definition' => $definition,
'student' => $student,
'teacher' => $teacher,
];
}
}

View File

@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'gradingform_guide';
$plugin->version = 2019052000;
$plugin->version = 2019100300;
$plugin->requires = 2019051100;
$plugin->maturity = MATURITY_STABLE;
$plugin->maturity = MATURITY_STABLE;