From 9028d9b513fa2e6f673ab875bb53a8056778d21b Mon Sep 17 00:00:00 2001 From: Paul Charsley Date: Mon, 5 Aug 2013 14:07:23 +1200 Subject: [PATCH] MDL-31890 added core_grading_get_gradingform_instances web service function --- grade/externallib.php | 181 +++++++++++++++++++++++++++++- grade/grading/form/guide/lib.php | 25 +++++ grade/grading/form/lib.php | 39 +++++++ grade/grading/form/rubric/lib.php | 23 ++++ grade/tests/externallib_test.php | 149 ++++++++++++++++++++++-- lib/db/services.php | 20 +++- 6 files changed, 419 insertions(+), 18 deletions(-) diff --git a/grade/externallib.php b/grade/externallib.php index a39acebf4ca..30449e6197e 100644 --- a/grade/externallib.php +++ b/grade/externallib.php @@ -15,9 +15,9 @@ // along with Moodle. If not, see . /** - * External assign API + * External grading API * - * @package core_grade + * @package core_grading * @since Moodle 2.5 * @copyright 2013 Paul Charsley * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -29,9 +29,9 @@ require_once("$CFG->libdir/externallib.php"); require_once("$CFG->dirroot/grade/grading/lib.php"); /** - * core grade functions + * core grading functions */ -class core_grade_external extends external_api { +class core_grading_external extends external_api { /** * Describes the parameters for get_definitions @@ -290,4 +290,177 @@ class core_grade_external extends external_api { return $methods; } + /** + * Describes the parameters for get_gradingform_instances + * + * @return external_function_parameters + * @since Moodle 2.6 + */ + public static function get_gradingform_instances_parameters () { + return new external_function_parameters( + array( + 'definitionid' => new external_value(PARAM_INT, 'definition id'), + 'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0) + ) + ); + } + + /** + * Returns the instances and fillings for the requested definition id + * + * @param int $definitionid + * @param int $since only return instances with timemodified >= since + * @return array of grading instances with fillings for the definition id + * @since Moodle 2.6 + */ + public static function get_gradingform_instances ($definitionid, $since = 0) { + global $DB, $CFG; + require_once("$CFG->dirroot/grade/grading/form/lib.php"); + $params = self::validate_parameters(self::get_gradingform_instances_parameters(), + array('definitionid' => $definitionid, + 'since' => $since)); + $instances = array(); + $warnings = array(); + + $definition = $DB->get_record('grading_definitions', + array('id' => $params['definitionid']), + 'areaid,method', MUST_EXIST); + $area = $DB->get_record('grading_areas', + array('id' => $definition->areaid), + 'contextid,component', MUST_EXIST); + + $context = context::instance_by_id($area->contextid); + require_capability('moodle/grade:managegradingforms', $context); + + $gradingmanager = get_grading_manager($definition->areaid); + $controller = $gradingmanager->get_controller($definition->method); + $activeinstances = $controller->get_all_active_instances ($params['since']); + $details = $controller->get_external_instance_filling_details(); + if ($details == null) { + $warnings[] = array( + 'item' => 'definition', + 'itemid' => $params['definitionid'], + 'message' => 'Fillings unavailable because get_external_instance_filling_details is not defined', + 'warningcode' => '1' + ); + } + $getfilling = null; + if (method_exists('gradingform_'.$definition->method.'_instance', 'get_'.$definition->method.'_filling')) { + $getfilling = 'get_'.$definition->method.'_filling'; + } else { + $warnings[] = array( + 'item' => 'definition', + 'itemid' => $params['definitionid'], + 'message' => 'Fillings unavailable because get_'.$definition->method.'_filling is not defined', + 'warningcode' => '1' + ); + } + foreach ($activeinstances as $activeinstance) { + $instance = array(); + $instance['id'] = $activeinstance->get_id(); + $instance['raterid'] = $activeinstance->get_data('raterid'); + $instance['itemid'] = $activeinstance->get_data('itemid'); + $instance['rawgrade'] = $activeinstance->get_data('rawgrade'); + $instance['status'] = $activeinstance->get_data('status'); + $instance['feedback'] = $activeinstance->get_data('feedback'); + $instance['feedbackformat'] = $activeinstance->get_data('feedbackformat'); + // Format the feedback text field. + $formattedtext = external_format_text($activeinstance->get_data('feedback'), + $activeinstance->get_data('feedbackformat'), + $context->id, + $area->component, + 'feedback', + $params['definitionid']); + $instance['feedback'] = $formattedtext[0]; + $instance['feedbackformat'] = $formattedtext[1]; + $instance['timemodified'] = $activeinstance->get_data('timemodified'); + + if ($details != null && $getfilling != null) { + $fillingdata = $activeinstance->$getfilling(); + $filling = array(); + foreach ($details as $key => $value) { + $filling[$key] = self::format_text($fillingdata[$key], + $context->id, + $area->component, + $params['definitionid']); + } + $instance[$definition->method] = $filling; + } + $instances[] = $instance; + } + $result = array( + 'instances' => $instances, + 'warnings' => $warnings + ); + return $result; + } + + /** + * Creates a grading instance + * + * @return external_single_structure + * @since Moodle 2.6 + */ + private static function grading_instance() { + global $CFG; + $instance = array(); + $instance['id'] = new external_value(PARAM_INT, 'instance id'); + $instance['raterid'] = new external_value(PARAM_INT, 'rater id'); + $instance['itemid'] = new external_value(PARAM_INT, 'item id'); + $instance['rawgrade'] = new external_value(PARAM_TEXT, 'raw grade', VALUE_OPTIONAL); + $instance['status'] = new external_value(PARAM_INT, 'status'); + $instance['feedback'] = new external_value(PARAM_RAW, 'feedback', VALUE_OPTIONAL); + $instance['feedbackformat'] = new external_format_value('feedback', VALUE_OPTIONAL); + $instance['timemodified'] = new external_value(PARAM_INT, 'modified time'); + foreach (self::get_grading_methods() as $method) { + require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php'); + $details = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details'); + if ($details != null) { + $items = array(); + foreach ($details as $key => $value) { + $details[$key]->required = VALUE_OPTIONAL; + $items[$key] = $value; + } + $instance[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL); + } + } + return new external_single_structure($instance); + } + + /** + * Describes the get_gradingform_instances return value + * + * @return external_single_structure + * @since Moodle 2.6 + */ + public static function get_gradingform_instances_returns () { + return new external_single_structure( + array( + 'instances' => new external_multiple_structure(self::grading_instance(), 'list of grading instances'), + 'warnings' => new external_warnings() + ) + ); + } + +} + +/** + * @since Moodle 2.6 + * @deprecated See MDL-30085. Please do not use this class any more. + * @see core_grading_external + */ +class core_grade_external extends external_api { + + public static function get_definitions_parameters () { + return core_grading_external::get_definitions_parameters(); + } + + public static function get_definitions ($cmids, $areaname, $activeonly = false) { + return core_grading_external::get_definitions($cmids, $areaname, $activeonly = false); + } + + public static function get_definitions_returns() { + return core_grading_external::get_definitions_returns(); + } + } diff --git a/grade/grading/form/guide/lib.php b/grade/grading/form/guide/lib.php index 942b19b37fb..5c351fa320f 100644 --- a/grade/grading/form/guide/lib.php +++ b/grade/grading/form/guide/lib.php @@ -683,6 +683,31 @@ class gradingform_guide_controller extends gradingform_controller { ); return array('guide_criteria' => $guide_criteria, 'guide_comment' => $guide_comment); } + + /** + * Returns an array that defines the structure of the guide's filling. This function is used by + * the web service function core_grading_external::get_gradingform_instances(). + * + * @return An array containing a single key/value pair with the 'criteria' external_multiple_structure + * @see gradingform_controller::get_external_instance_filling_details() + * @since Moodle 2.6 + */ + public static function get_external_instance_filling_details() { + $criteria = new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'filling id'), + 'criterionid' => new external_value(PARAM_INT, 'criterion id'), + 'levelid' => new external_value(PARAM_INT, 'level id', VALUE_OPTIONAL), + 'remark' => new external_value(PARAM_RAW, 'remark', VALUE_OPTIONAL), + 'remarkformat' => new external_format_value('remark', VALUE_OPTIONAL), + 'score' => new external_value(PARAM_FLOAT, 'maximum score') + ) + ), 'filling', VALUE_OPTIONAL + ); + return array ('criteria' => $criteria); + } + } /** diff --git a/grade/grading/form/lib.php b/grade/grading/form/lib.php index 666d0d9ae06..bcf6e0ffd00 100644 --- a/grade/grading/form/lib.php +++ b/grade/grading/form/lib.php @@ -431,6 +431,27 @@ abstract class gradingform_controller { return $rv; } + /** + * Returns an array of all active instances for this definition. + * (intentionally does not return instances with status NEEDUPDATE) + * + * @param int since only return instances with timemodified >= since + * @return array of gradingform_instance objects + */ + public function get_all_active_instances($since = 0) { + global $DB; + $conditions = array ($this->definition->id, + gradingform_instance::INSTANCE_STATUS_ACTIVE, + $since); + $where = "definitionid = ? AND status = ? AND timemodified >= ?"; + $records = $DB->get_records_select('grading_instances', $where, $conditions); + $rv = array(); + foreach ($records as $record) { + $rv[] = $this->get_instance($record); + } + return $rv; + } + /** * Returns true if there are already people who has been graded on this definition. * In this case plugins may restrict changes of the grading definition @@ -680,6 +701,24 @@ abstract class gradingform_controller { public static function get_external_definition_details() { return null; } + + /** + * Overridden by sub classes that wish to make instance filling details available to web services. + * When not overridden, only instance filling data common to all grading methods is made available. + * When overriding, the return value should be an array containing one or more key/value pairs. + * These key/value pairs should match the filling data returned by the get__filling() function + * in the gradingform_instance subclass. + * For examples, look at: + * $gradingform_rubric_controller->get_external_instance_filling_details() + * $gradingform_guide_controller->get_external_instance_filling_details() + * + * @return array An array of one or more key/value pairs containing the external_multiple_structure/s + * corresponding to the definition returned by $gradingform__instance->get__filling() + * @since Moodle 2.6 + */ + public static function get_external_instance_filling_details() { + return null; + } } /** diff --git a/grade/grading/form/rubric/lib.php b/grade/grading/form/rubric/lib.php index 39e6adcbe15..1cf9b444173 100644 --- a/grade/grading/form/rubric/lib.php +++ b/grade/grading/form/rubric/lib.php @@ -688,6 +688,29 @@ class gradingform_rubric_controller extends gradingform_controller { return array('rubric_criteria' => $rubric_criteria); } + /** + * Returns an array that defines the structure of the rubric's filling. This function is used by + * the web service function core_grading_external::get_gradingform_instances(). + * + * @return An array containing a single key/value pair with the 'criteria' external_multiple_structure + * @see gradingform_controller::get_external_instance_filling_details() + * @since Moodle 2.6 + */ + public static function get_external_instance_filling_details() { + $criteria = new external_multiple_structure( + new external_single_structure( + array( + 'id' => new external_value(PARAM_INT, 'filling id'), + 'criterionid' => new external_value(PARAM_INT, 'criterion id'), + 'levelid' => new external_value(PARAM_INT, 'level id', VALUE_OPTIONAL), + 'remark' => new external_value(PARAM_RAW, 'remark', VALUE_OPTIONAL), + 'remarkformat' => new external_format_value('remark', VALUE_OPTIONAL) + ) + ), 'filling', VALUE_OPTIONAL + ); + return array ('criteria' => $criteria); + } + } /** diff --git a/grade/tests/externallib_test.php b/grade/tests/externallib_test.php index defbe491347..c431e5fbed4 100644 --- a/grade/tests/externallib_test.php +++ b/grade/tests/externallib_test.php @@ -21,14 +21,14 @@ global $CFG; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); /** - * External core grade functions unit tests + * Unit tests for the grading API at /grade/externallib.php * - * @package core_grade + * @package core_grading * @category external * @copyright 2013 Paul Charsley * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class core_grade_externallib_testcase extends externallib_advanced_testcase { +class core_grading_externallib_testcase extends externallib_advanced_testcase { /** * Tests set up @@ -58,10 +58,10 @@ class core_grade_externallib_testcase extends externallib_advanced_testcase { $cm = self::getDataGenerator()->create_module('assign', $assigndata); // Create manual enrolment record. - $manual_enrol_data['enrol'] = 'manual'; - $manual_enrol_data['status'] = 0; - $manual_enrol_data['courseid'] = $course->id; - $enrolid = $DB->insert_record('enrol', $manual_enrol_data); + $manualenroldata['enrol'] = 'manual'; + $manualenroldata['status'] = 0; + $manualenroldata['courseid'] = $course->id; + $enrolid = $DB->insert_record('enrol', $manualenroldata); // Create a teacher and give them capabilities. $coursecontext = context_course::instance($course->id); @@ -70,10 +70,10 @@ class core_grade_externallib_testcase extends externallib_advanced_testcase { $this->assignUserCapability('mod/assign:grade', $modulecontext->id, $roleid); // Create the teacher's enrolment record. - $user_enrolment_data['status'] = 0; - $user_enrolment_data['enrolid'] = $enrolid; - $user_enrolment_data['userid'] = $USER->id; - $DB->insert_record('user_enrolments', $user_enrolment_data); + $userenrolmentdata['status'] = 0; + $userenrolmentdata['enrolid'] = $enrolid; + $userenrolmentdata['userid'] = $USER->id; + $DB->insert_record('user_enrolments', $userenrolmentdata); // Create a grading area. $gradingarea = array( @@ -148,7 +148,7 @@ class core_grade_externallib_testcase extends externallib_advanced_testcase { // Call the external function. $cmids = array ($cm->id); $areaname = 'submissions'; - $result = core_grade_external::get_definitions($cmids, $areaname); + $result = core_grading_external::get_definitions($cmids, $areaname); $this->assertEquals(1, count($result['areas'])); $this->assertEquals(1, count($result['areas'][0]['definitions'])); @@ -181,4 +181,129 @@ class core_grade_externallib_testcase extends externallib_advanced_testcase { $this->assertTrue($found); } + /** + * Test get_gradingform_instances + */ + public function test_get_gradingform_instances () { + global $DB, $CFG, $USER; + + $this->resetAfterTest(true); + // Create a course and assignment. + $coursedata['idnumber'] = 'idnumbercourse'; + $coursedata['fullname'] = 'Lightwork Course'; + $coursedata['summary'] = 'Lightwork Course description'; + $coursedata['summaryformat'] = FORMAT_MOODLE; + $course = self::getDataGenerator()->create_course($coursedata); + + $assigndata['course'] = $course->id; + $assigndata['name'] = 'lightwork assignment'; + + $assign = self::getDataGenerator()->create_module('assign', $assigndata); + + // Create manual enrolment record. + $manualenroldata['enrol'] = 'manual'; + $manualenroldata['status'] = 0; + $manualenroldata['courseid'] = $course->id; + $enrolid = $DB->insert_record('enrol', $manualenroldata); + + // Create a teacher and give them capabilities. + $coursecontext = context_course::instance($course->id); + $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $coursecontext->id, 3); + $modulecontext = context_module::instance($assign->id); + $this->assignUserCapability('mod/assign:grade', $modulecontext->id, $roleid); + + // Create the teacher's enrolment record. + $userenrolmentdata['status'] = 0; + $userenrolmentdata['enrolid'] = $enrolid; + $userenrolmentdata['userid'] = $USER->id; + $DB->insert_record('user_enrolments', $userenrolmentdata); + + // Create a student with an assignment grade. + $student = self::getDataGenerator()->create_user(); + $assigngrade = new stdClass(); + $assigngrade->assignment = $assign->id; + $assigngrade->userid = $student->id; + $assigngrade->timecreated = time(); + $assigngrade->timemodified = $assigngrade->timecreated; + $assigngrade->grader = $USER->id; + $assigngrade->grade = 50; + $assigngrade->attemptnumber = 0; + $gid = $DB->insert_record('assign_grades', $assigngrade); + + // Create a grading area. + $gradingarea = array( + 'contextid' => $modulecontext->id, + 'component' => 'mod_assign', + 'areaname' => 'submissions', + 'activemethod' => 'rubric' + ); + $areaid = $DB->insert_record('grading_areas', $gradingarea); + + // Create a rubric grading definition. + $rubricdefinition = array ( + 'areaid' => $areaid, + 'method' => 'rubric', + 'name' => 'test', + 'status' => 20, + 'copiedfromid' => 1, + 'timecreated' => 1, + 'usercreated' => $USER->id, + 'timemodified' => 1, + 'usermodified' => $USER->id, + 'timecopied' => 0 + ); + $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition); + + // Create a criterion with a level. + $rubriccriteria = array ( + 'definitionid' => $definitionid, + 'sortorder' => 1, + 'description' => 'Demonstrate an understanding of disease control', + 'descriptionformat' => 0 + ); + $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria); + $rubriclevel = array ( + 'criterionid' => $criterionid, + 'score' => 50, + 'definition' => 'pass', + 'definitionformat' => 0 + ); + $levelid = $DB->insert_record('gradingform_rubric_levels', $rubriclevel); + + // Create a grading instance. + $instance = array ( + 'definitionid' => $definitionid, + 'raterid' => $USER->id, + 'itemid' => $gid, + 'status' => 1, + 'feedbackformat' => 0, + 'timemodified' => 1 + ); + $instanceid = $DB->insert_record('grading_instances', $instance); + + // Create a filling. + $filling = array ( + 'instanceid' => $instanceid, + 'criterionid' => $criterionid, + 'levelid' => $levelid, + 'remark' => 'excellent work', + 'remarkformat' => 0 + ); + $DB->insert_record('gradingform_rubric_fillings', $filling); + + // Call the external function. + $result = core_grading_external::get_gradingform_instances($definitionid, 0); + + $this->assertEquals(1, count($result['instances'])); + $this->assertEquals($USER->id, $result['instances'][0]['raterid']); + $this->assertEquals($gid, $result['instances'][0]['itemid']); + $this->assertEquals(1, $result['instances'][0]['status']); + $this->assertEquals(1, $result['instances'][0]['timemodified']); + $this->assertEquals(1, count($result['instances'][0]['rubric'])); + $this->assertEquals(1, count($result['instances'][0]['rubric']['criteria'])); + $criteria = $result['instances'][0]['rubric']['criteria']; + $this->assertEquals($criterionid, $criteria[$criterionid]['criterionid']); + $this->assertEquals($levelid, $criteria[$criterionid]['levelid']); + $this->assertEquals('excellent work', $criteria[$criterionid]['remark']); + } } diff --git a/lib/db/services.php b/lib/db/services.php index 311c0dd7caa..9781298b7d7 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -782,13 +782,29 @@ $functions = array( 'capabilities'=> 'moodle/notes:manage', ), - // === grade related functions === + // === grading related functions === + + 'core_grading_get_definitions' => array( + 'classname' => 'core_grading_external', + 'methodname' => 'get_definitions', + 'classpath' => 'grade/externallib.php', + 'description' => 'Get grading definitions', + 'type' => 'read' + ), 'core_grade_get_definitions' => array( 'classname' => 'core_grade_external', 'methodname' => 'get_definitions', 'classpath' => 'grade/externallib.php', - 'description' => 'Get grading definitions', + 'description' => 'DEPRECATED: this deprecated function will be removed in a future version. This function has been renamed as core_grading_get_definitions()', + 'type' => 'read' + ), + + 'core_grading_get_gradingform_instances' => array( + 'classname' => 'core_grading_external', + 'methodname' => 'get_gradingform_instances', + 'classpath' => 'grade/externallib.php', + 'description' => 'Get grading form instances', 'type' => 'read' ),