From 46f5e9a0ef16260150942148285c8e972df29d57 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 27 Jun 2017 13:18:24 +0100 Subject: [PATCH] MDL-59248 mod_workshop: New WS get_assessment_form_definition --- mod/workshop/classes/external.php | 133 ++++++++++++++++++++++++++ mod/workshop/db/services.php | 7 ++ mod/workshop/form/assessment_form.php | 10 ++ mod/workshop/locallib.php | 27 ++++++ mod/workshop/tests/external_test.php | 84 +++++++++++++++- mod/workshop/version.php | 2 +- 6 files changed, 260 insertions(+), 3 deletions(-) diff --git a/mod/workshop/classes/external.php b/mod/workshop/classes/external.php index 78955bdf1af..6a94cf10dc6 100644 --- a/mod/workshop/classes/external.php +++ b/mod/workshop/classes/external.php @@ -1187,4 +1187,137 @@ class mod_workshop_external extends external_api { ) ); } + + /** + * Returns the description of the external function parameters. + * + * @return external_function_parameters + * @since Moodle 3.4 + */ + public static function get_assessment_form_definition_parameters() { + return new external_function_parameters( + array( + 'assessmentid' => new external_value(PARAM_INT, 'Assessment id'), + 'mode' => new external_value(PARAM_ALPHA, 'The form mode (assessment or preview)', VALUE_DEFAULT, 'assessment'), + ) + ); + } + + + /** + * Retrieves the assessment form definition (data required to be able to display the assessment form). + * + * @param int $assessmentid the assessment id + * @param string $mode the form mode (assessment or preview) + * @return array containing the assessment and warnings. + * @since Moodle 3.4 + * @throws moodle_exception + */ + public static function get_assessment_form_definition($assessmentid, $mode = 'assessment') { + global $DB, $USER; + + $params = self::validate_parameters( + self::get_assessment_form_definition_parameters(), array('assessmentid' => $assessmentid, 'mode' => $mode) + ); + $warnings = $pending = array(); + + if ($params['mode'] != 'assessment' && $params['mode'] != 'preview') { + throw new invalid_parameter_exception('Invalid value for mode parameter (value: ' . $params['mode'] . ')'); + } + + // Get and validate the assessment, submission and workshop. + $assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST); + $submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST); + list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid); + + // Check we can edit the assessment (so we can get the form data). + $workshop->check_edit_assessment($assessment, $submission); + + $cansetassessmentweight = has_capability('mod/workshop:allocate', $context); + $pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id); + + // Retrieve the data from the strategy plugin. + $strategy = $workshop->grading_strategy_instance(); + $strategyname = str_replace('_strategy', '', get_class($strategy)); // Get strategy name. + $mform = $strategy->get_assessment_form(null, $params['mode'], $assessment, true, + array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending))); + $formdata = $mform->get_customdata(); + + $result = array( + 'dimenssionscount' => $formdata['nodims'], + 'descriptionfiles' => external_util::get_area_files($context->id, $strategyname, 'description'), + 'warnings' => $warnings + ); + // Include missing dimension fields. + for ($i = 0; $i < $formdata['nodims']; $i++) { + $formdata['fields']->{'gradeid__idx_' . $i} = 0; + $formdata['fields']->{'peercomment__idx_' . $i} = ''; + } + + // Convert all the form data for external. + foreach (array('options', 'fields', 'current') as $typeofdata) { + $result[$typeofdata] = array(); + + if (!empty($formdata[$typeofdata])) { + $alldata = (array) $formdata[$typeofdata]; + foreach ($alldata as $key => $val) { + if (strpos($key, 'peercomment__idx_') === 0) { + // Format reviewer comment. + list($val, $format) = external_format_text($val, FORMAT_MOODLE, $context->id); + } else if (strpos($key, 'description__idx_')) { + // Format dimension description. + $id = str_replace('description__idx_', '', $key); + list($val, $format) = external_format_text($val, $alldata['dimensionid__idx_' . $id . 'format'], + $context->id, $strategyname, 'description', $alldata['dimensionid__idx_' . $id]); + } + $result[$typeofdata][] = array( + 'name' => $key, + 'value' => $val + ); + } + } + } + + return $result; + } + + /** + * Returns description of method result value + * + * @return external_description + * @since Moodle 3.4 + */ + public static function get_assessment_form_definition_returns() { + return new external_single_structure( + array( + 'dimenssionscount' => new external_value(PARAM_INT, 'The number of dimenssions used by the form.'), + 'descriptionfiles' => new external_files('Files in the description text'), + 'options' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name.'), + 'value' => new external_value(PARAM_NOTAGS, 'Option value.') + ) + ), 'The form options.' + ), + 'fields' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'), + 'value' => new external_value(PARAM_RAW, 'Field default value.') + ) + ), 'The form fields.' + ), + 'current' => new external_multiple_structure( + new external_single_structure( + array( + 'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'), + 'value' => new external_value(PARAM_RAW, 'Current field value.') + ) + ), 'The current field values.' + ), + 'warnings' => new external_warnings() + ) + ); + } } diff --git a/mod/workshop/db/services.php b/mod/workshop/db/services.php index 341e017b662..94b4c19a68d 100644 --- a/mod/workshop/db/services.php +++ b/mod/workshop/db/services.php @@ -113,4 +113,11 @@ $functions = array( 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), + 'mod_workshop_get_assessment_form_definition' => array( + 'classname' => 'mod_workshop_external', + 'methodname' => 'get_assessment_form_definition', + 'description' => 'Retrieves the assessment form definition.', + 'type' => 'read', + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), ); diff --git a/mod/workshop/form/assessment_form.php b/mod/workshop/form/assessment_form.php index 56128dcf5b3..4f7ba99b1da 100644 --- a/mod/workshop/form/assessment_form.php +++ b/mod/workshop/form/assessment_form.php @@ -120,4 +120,14 @@ class workshop_assessment_form extends moodleform { public function is_editable() { return !$this->_form->isFrozen(); } + + /** + * Return the form custom data. + * + * @return array an array containing the custom data + * @since Moodle 3.4 + */ + public function get_customdata() { + return $this->_customdata; + } } diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 383ce6104ed..ac243f238bb 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -2998,6 +2998,33 @@ class workshop { } } + /** + * Helper method for validating if the current user can edit the given assessment. + * + * @param stdClass $assessment assessment object + * @param stdClass $submission submission object + * @return void + * @throws moodle_exception + * @since Moodle 3.4 + */ + public function check_edit_assessment($assessment, $submission) { + global $USER; + + $this->check_view_assessment($assessment, $submission); + // Further checks. + $isreviewer = ($USER->id == $assessment->reviewerid); + + $assessmenteditable = $isreviewer && $this->assessing_allowed($USER->id); + if (!$assessmenteditable) { + throw new moodle_exception('nopermissions', 'error', '', 'edit assessments'); + } + + list($assessed, $notice) = $this->check_examples_assessed_before_assessment($assessment->reviewerid); + if (!$assessed) { + throw new moodle_exception($notice, 'mod_workshop'); + } + } + //////////////////////////////////////////////////////////////////////////////// // Internal methods (implementation details) // //////////////////////////////////////////////////////////////////////////////// diff --git a/mod/workshop/tests/external_test.php b/mod/workshop/tests/external_test.php index 5992fe530a1..c319b0a8841 100644 --- a/mod/workshop/tests/external_test.php +++ b/mod/workshop/tests/external_test.php @@ -79,6 +79,20 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase { $this->context = context_module::instance($this->workshop->cmid); $this->cm = get_coursemodule_from_instance('workshop', $this->workshop->id); + // Add grading strategy data (accumulative is the default). + $workshop = new workshop($this->workshop, $this->cm, $this->course); + $strategy = $workshop->grading_strategy_instance(); + $data = array(); + for ($i = 0; $i < 4; $i++) { + $data['dimensionid__idx_'.$i] = 0; + $data['description__idx_'.$i.'_editor'] = array('text' => "Content $i", 'format' => FORMAT_MOODLE); + $data['grade__idx_'.$i] = 25; + $data['weight__idx_'.$i] = 25; + } + $data['workshopid'] = $workshop->id; + $data['norepeats'] = 4; + $strategy->save_edit_strategy_form((object) $data); + // Create users. $this->student = self::getDataGenerator()->create_user(); $this->anotherstudentg1 = self::getDataGenerator()->create_user(); @@ -343,7 +357,7 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase { $this->assertCount(4, $result['userplan']['phases'][0]['tasks']); // For new empty workshops, always 4 tasks. foreach ($result['userplan']['phases'][0]['tasks'] as $task) { - if ($task['code'] == 'intro' || $task['code'] == 'instructauthors') { + if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform') { $this->assertEquals(1, $task['completed']); } else { $this->assertEmpty($task['completed']); @@ -357,7 +371,8 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase { $result = mod_workshop_external::get_user_plan($this->workshop->id); $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result); foreach ($result['userplan']['phases'][0]['tasks'] as $task) { - if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'switchtonextphase') { + if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform' || + $task['code'] == 'switchtonextphase') { $this->assertEquals(1, $task['completed']); } else { $this->assertEmpty($task['completed']); @@ -1266,4 +1281,69 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase { $this->setExpectedException('moodle_exception'); mod_workshop_external::get_assessment($assessmentid); } + + /** + * Test get_assessment_form_definition_reviewer_new_assessment. + */ + public function test_get_assessment_form_definition_reviewer_new_assessment() { + global $DB; + + // Create the submission. + $submissionid = $this->create_test_submission($this->anotherstudentg1); + + $workshop = new workshop($this->workshop, $this->cm, $this->course); + $submission = $workshop->get_submission_by_id($submissionid); + $assessmentid = $workshop->add_allocation($submission, $this->student->id); + + // Switch to assessment phase. + $DB->set_field('workshop', 'phase', workshop::PHASE_ASSESSMENT, array('id' => $this->workshop->id)); + $this->setUser($this->student); + $result = mod_workshop_external::get_assessment_form_definition($assessmentid); + $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result); + $this->assertEquals(4, $result['dimenssionscount']); // We receive the expected 4 dimensions. + $this->assertEmpty($result['current']); // Assessment not yet done. + foreach ($result['fields'] as $field) { + if (strpos($field['name'], 'grade__idx_') === 0) { + $this->assertEquals(25, $field['value']); // Check one of the dimension fields attributes. + } + } + } + + /** + * Test get_assessment_form_definition_teacher_new_assessment. + */ + public function test_get_assessment_form_definition_teacher_new_assessment() { + global $DB; + + // Create the submission. + $submissionid = $this->create_test_submission($this->anotherstudentg1); + + $workshop = new workshop($this->workshop, $this->cm, $this->course); + $submission = $workshop->get_submission_by_id($submissionid); + $assessmentid = $workshop->add_allocation($submission, $this->student->id); + + // Switch to assessment phase. + $DB->set_field('workshop', 'phase', workshop::PHASE_ASSESSMENT, array('id' => $this->workshop->id)); + $this->setUser($this->teacher); + $this->setExpectedException('moodle_exception'); + mod_workshop_external::get_assessment_form_definition($assessmentid); // Teachers can't add/edit assessments. + } + + /** + * Test get_assessment_form_definition_invalid_phase. + */ + public function test_get_assessment_form_definition_invalid_phase() { + global $DB; + + // Create the submission. + $submissionid = $this->create_test_submission($this->anotherstudentg1); + + $workshop = new workshop($this->workshop, $this->cm, $this->course); + $submission = $workshop->get_submission_by_id($submissionid); + $assessmentid = $workshop->add_allocation($submission, $this->student->id); + + $this->setUser($this->student); + $this->setExpectedException('moodle_exception'); + mod_workshop_external::get_assessment_form_definition($assessmentid); + } } diff --git a/mod/workshop/version.php b/mod/workshop/version.php index 487445b0a54..6bb09d7d69d 100644 --- a/mod/workshop/version.php +++ b/mod/workshop/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017051511; // The current module version (YYYYMMDDXX) +$plugin->version = 2017051512; // The current module version (YYYYMMDDXX) $plugin->requires = 2017050500; // Requires this Moodle version. $plugin->component = 'mod_workshop'; $plugin->cron = 60; // Give as a chance every minute. -- 2.43.0