Merge branch 'wip-MDL-31890-master' of git://github.com/marinaglancy/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 3 Sep 2013 01:53:22 +0000 (09:53 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 3 Sep 2013 01:53:22 +0000 (09:53 +0800)
grade/externallib.php
grade/grading/form/guide/lib.php
grade/grading/form/lib.php
grade/grading/form/rubric/lib.php
grade/tests/externallib_test.php
lib/db/services.php

index a39aceb..6a5bcd0 100644 (file)
@@ -15,9 +15,9 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * 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,16 +29,16 @@ 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
      * @return external_function_parameters
      * @since Moodle 2.5
      */
-    public static function get_definitions_parameters () {
+    public static function get_definitions_parameters() {
         return new external_function_parameters(
             array(
                 'cmids' => new external_multiple_structure(
@@ -57,7 +57,7 @@ class core_grade_external extends external_api {
      * @return array of areas with definitions for each requested course module id
      * @since Moodle 2.5
      */
-    public static function get_definitions ($cmids, $areaname, $activeonly = false) {
+    public static function get_definitions($cmids, $areaname, $activeonly = false) {
         global $DB, $CFG;
         require_once("$CFG->dirroot/grade/grading/form/lib.php");
         $params = self::validate_parameters(self::get_definitions_parameters(),
@@ -290,4 +290,179 @@ 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()
+            )
+        );
+    }
+
+}
+
+/**
+ * core grading functions. Renamed to core_grading_external
+ *
+ * @since Moodle 2.5
+ * @deprecated since 2.6 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();
+    }
+
 }
index 942b19b..5c351fa 100644 (file)
@@ -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);
+    }
+
 }
 
 /**
index 666d0d9..bcf6e0f 100644 (file)
@@ -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_<method>_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_<method>_instance->get_<method>_filling()
+     * @since Moodle 2.6
+     */
+    public static function get_external_instance_filling_details() {
+        return null;
+    }
 }
 
 /**
index 39e6adc..1cf9b44 100644 (file)
@@ -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);
+    }
+
 }
 
 /**
index defbe49..3230236 100644 (file)
@@ -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
@@ -41,7 +41,7 @@ class core_grade_externallib_testcase extends externallib_advanced_testcase {
     /**
      * Test get_definitions
      */
-    public function test_get_definitions () {
+    public function test_get_definitions() {
         global $DB, $CFG, $USER;
 
         $this->resetAfterTest(true);
@@ -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, $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']);
+    }
 }
index 311c0dd..9781298 100644 (file)
@@ -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'
     ),