MDL-59246 mod_workshop: New WS get_submission_assessments
This commit is contained in:
@@ -31,6 +31,7 @@ require_once($CFG->dirroot . '/mod/workshop/locallib.php');
|
||||
|
||||
use mod_workshop\external\workshop_summary_exporter;
|
||||
use mod_workshop\external\submission_exporter;
|
||||
use mod_workshop\external\assessment_exporter;
|
||||
|
||||
/**
|
||||
* Workshop external functions
|
||||
@@ -874,6 +875,36 @@ class mod_workshop_external extends external_api {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for validating a submission.
|
||||
*
|
||||
* @param stdClass $submission submission object
|
||||
* @param workshop $workshop workshop instance
|
||||
* @return void
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
protected static function validate_submission($submission, workshop $workshop) {
|
||||
global $USER;
|
||||
|
||||
$workshopclosed = $workshop->phase == workshop::PHASE_CLOSED;
|
||||
$canviewpublished = has_capability('mod/workshop:viewpublishedsubmissions', $workshop->context);
|
||||
|
||||
$canview = $submission->authorid == $USER->id; // I did it.
|
||||
$canview = $canview || !empty($workshop->get_assessment_of_submission_by_user($submission->id, $USER->id)); // I reviewed.
|
||||
$canview = $canview || has_capability('mod/workshop:viewallsubmissions', $workshop->context); // I can view all.
|
||||
$canview = $canview || ($submission->published && $workshopclosed && $canviewpublished); // It has been published.
|
||||
|
||||
if ($canview) {
|
||||
// Here we should check if the user share group.
|
||||
if ($submission->authorid != $USER->id &&
|
||||
!groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
|
||||
throw new moodle_exception('notingroup');
|
||||
}
|
||||
} else {
|
||||
throw new moodle_exception('nopermissions', 'error', '', 'view submission');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the external function parameters.
|
||||
*
|
||||
@@ -907,22 +938,7 @@ class mod_workshop_external extends external_api {
|
||||
$submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
|
||||
list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
|
||||
|
||||
$workshopclosed = $workshop->phase == workshop::PHASE_CLOSED;
|
||||
$canviewpublished = has_capability('mod/workshop:viewpublishedsubmissions', $context);
|
||||
|
||||
$canview = $submission->authorid == $USER->id; // I did it.
|
||||
$canview = $canview || !empty($workshop->get_assessment_of_submission_by_user($submission->id, $USER->id)); // I reviewed.
|
||||
$canview = $canview || has_capability('mod/workshop:viewallsubmissions', $context); // I can view all.
|
||||
$canview = $canview || ($submission->published && $workshopclosed && $canviewpublished); // It has been published.
|
||||
|
||||
if ($canview) {
|
||||
// Here we should check if the user share group.
|
||||
if ($submission->authorid != $USER->id && !groups_user_groups_visible($course, $submission->authorid, $cm)) {
|
||||
throw new moodle_exception('notingroup');
|
||||
}
|
||||
} else {
|
||||
throw new moodle_exception('nopermissions', 'error', '', 'view submission');
|
||||
}
|
||||
self::validate_submission($submission, $workshop);
|
||||
|
||||
$submission = self::prepare_submission_for_external($submission, $workshop);
|
||||
|
||||
@@ -948,4 +964,156 @@ class mod_workshop_external extends external_api {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for validating if the current user can view the submission assessments.
|
||||
*
|
||||
* @param stdClass $submission submission object
|
||||
* @param workshop $workshop workshop instance
|
||||
* @return void
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
protected static function check_view_submission_assessments($submission, workshop $workshop) {
|
||||
global $USER;
|
||||
|
||||
$ownsubmission = $submission->authorid == $USER->id;
|
||||
$canview = has_capability('mod/workshop:viewallassessments', $workshop->context) ||
|
||||
($ownsubmission && $workshop->assessments_available());
|
||||
|
||||
if ($canview) {
|
||||
// Here we should check if the user share group.
|
||||
if ($submission->authorid != $USER->id &&
|
||||
!groups_user_groups_visible($workshop->course, $submission->authorid, $workshop->cm)) {
|
||||
throw new moodle_exception('notingroup');
|
||||
}
|
||||
} else {
|
||||
throw new moodle_exception('nopermissions', 'error', '', 'view assessment');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for returning the assessment data according the current user capabilities and current phase.
|
||||
*
|
||||
* @param stdClass $assessment the assessment data
|
||||
* @param workshop $workshop the workshop class
|
||||
* @return stdClass object with the assessment data filtered or null if is not viewable yet
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
protected static function prepare_assessment_for_external($assessment, workshop $workshop) {
|
||||
global $USER;
|
||||
static $canviewallassessments = null;
|
||||
static $canviewreviewers = null;
|
||||
static $canoverridegrades = null;
|
||||
|
||||
// Remove all the properties that does not belong to the assessment table.
|
||||
$properties = assessment_exporter::properties_definition();
|
||||
foreach ($assessment as $key => $value) {
|
||||
if (!isset($properties[$key])) {
|
||||
unset($assessment->{$key});
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($canviewallassessments)) {
|
||||
$canviewallassessments = has_capability('mod/workshop:viewallassessments', $workshop->context);
|
||||
}
|
||||
if (is_null($canviewreviewers)) {
|
||||
$canviewreviewers = has_capability('mod/workshop:viewreviewernames', $workshop->context);
|
||||
}
|
||||
if (is_null($canoverridegrades)) {
|
||||
$canoverridegrades = has_capability('mod/workshop:overridegrades', $workshop->context);
|
||||
}
|
||||
|
||||
$isreviewer = $assessment->reviewerid == $USER->id;
|
||||
|
||||
if (!$isreviewer && is_null($assessment->grade) && !$canviewallassessments) {
|
||||
// Students do not see peer-assessment that are not graded yet.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the feedback for the reviewer if the feedback is not closed or if we don't have enough permissions to see it.
|
||||
if (!$canoverridegrades && ($workshop->phase != workshop::PHASE_CLOSED || !$isreviewer)) {
|
||||
// Remove all the feedback information (all the optional fields).
|
||||
foreach ($properties as $attribute => $settings) {
|
||||
if (!empty($settings['optional'])) {
|
||||
unset($assessment->{$attribute});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isreviewer && !$canviewreviewers) {
|
||||
$assessment->reviewerid = 0;
|
||||
}
|
||||
|
||||
return $assessment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the external function parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function get_submission_assessments_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'submissionid' => new external_value(PARAM_INT, 'Submission id'),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the given submission assessments.
|
||||
*
|
||||
* @param int $submissionid the submission id
|
||||
* @return array containing the assessments and warnings.
|
||||
* @since Moodle 3.4
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_submission_assessments($submissionid) {
|
||||
global $USER, $DB, $PAGE;
|
||||
|
||||
$params = self::validate_parameters(self::get_submission_assessments_parameters(), array('submissionid' => $submissionid));
|
||||
$warnings = $assessments = array();
|
||||
|
||||
// Get and validate the submission and workshop.
|
||||
$submission = $DB->get_record('workshop_submissions', array('id' => $params['submissionid']), '*', MUST_EXIST);
|
||||
list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
|
||||
|
||||
// Check that we can get the assessments and get them.
|
||||
self::check_view_submission_assessments($submission, $workshop);
|
||||
$assessmentsrecords = $workshop->get_assessments_of_submission($submission->id);
|
||||
|
||||
$related = array('context' => $context);
|
||||
foreach ($assessmentsrecords as $assessment) {
|
||||
$assessment = self::prepare_assessment_for_external($assessment, $workshop);
|
||||
if (empty($assessment)) {
|
||||
continue;
|
||||
}
|
||||
$exporter = new assessment_exporter($assessment, $related);
|
||||
$assessments[] = $exporter->export($PAGE->get_renderer('core'));
|
||||
}
|
||||
|
||||
return array(
|
||||
'assessments' => $assessments,
|
||||
'warnings' => $warnings
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function get_submission_assessments_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'assessments' => new external_multiple_structure(
|
||||
assessment_exporter::get_read_structure()
|
||||
),
|
||||
'warnings' => new external_warnings()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Class for exporting assessment data.
|
||||
*
|
||||
* @package mod_workshop
|
||||
* @copyright 2017 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace mod_workshop\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\external\exporter;
|
||||
use renderer_base;
|
||||
use external_util;
|
||||
use external_files;
|
||||
|
||||
/**
|
||||
* Class for exporting assessment data.
|
||||
*
|
||||
* @copyright 2017 Juan Leyva <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class assessment_exporter extends exporter {
|
||||
|
||||
protected static function define_properties() {
|
||||
|
||||
return array(
|
||||
'id' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The primary key of the record.',
|
||||
),
|
||||
'submissionid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The id of the assessed submission',
|
||||
),
|
||||
'reviewerid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The id of the reviewer who makes this assessment',
|
||||
),
|
||||
'weight' => array(
|
||||
'type' => PARAM_INT,
|
||||
'default' => 1,
|
||||
'description' => 'The weight of the assessment for the purposes of aggregation',
|
||||
),
|
||||
'timecreated' => array(
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => 0,
|
||||
'description' => 'If 0 then the assessment was allocated but the reviewer has not assessed yet.
|
||||
If greater than 0 then the timestamp of when the reviewer assessed for the first time',
|
||||
),
|
||||
'timemodified' => array(
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => 0,
|
||||
'description' => 'If 0 then the assessment was allocated but the reviewer has not assessed yet.
|
||||
If greater than 0 then the timestamp of when the reviewer assessed for the last time',
|
||||
),
|
||||
'grade' => array(
|
||||
'type' => PARAM_FLOAT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'The aggregated grade for submission suggested by the reviewer.
|
||||
The grade 0..100 is computed from the values assigned to the assessment dimensions fields. If NULL then it has not been aggregated yet.',
|
||||
),
|
||||
'gradinggrade' => array(
|
||||
'type' => PARAM_FLOAT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'The computed grade 0..100 for this assessment. If NULL then it has not been computed yet.',
|
||||
),
|
||||
'gradinggradeover' => array(
|
||||
'type' => PARAM_FLOAT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'Grade for the assessment manually overridden by a teacher.
|
||||
Grade is always from interval 0..100. If NULL then the grade is not overriden.',
|
||||
),
|
||||
'gradinggradeoverby' => array(
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'The id of the user who has overridden the grade for submission.',
|
||||
),
|
||||
'feedbackauthor' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'The comment/feedback from the reviewer for the author.',
|
||||
),
|
||||
'feedbackauthorformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_MOODLE,
|
||||
'description' => 'Feedback text format.',
|
||||
),
|
||||
'feedbackauthorattachment' => array(
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => 0,
|
||||
'description' => 'Are there some files attached to the feedbackauthor field?
|
||||
Sets to 1 by file_postupdate_standard_filemanager().',
|
||||
),
|
||||
'feedbackreviewer' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'null' => NULL_ALLOWED,
|
||||
'description' => 'The comment/feedback from the teacher for the reviewer.
|
||||
For example the reason why the grade for assessment was overridden',
|
||||
'optional' => true,
|
||||
),
|
||||
'feedbackreviewerformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_MOODLE,
|
||||
'description' => 'Feedback text format.',
|
||||
),
|
||||
|
||||
'feedbackauthorformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_MOODLE,
|
||||
'description' => 'Feedback text format.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected static function define_related() {
|
||||
return array(
|
||||
'context' => 'context'
|
||||
);
|
||||
}
|
||||
|
||||
protected static function define_other_properties() {
|
||||
return array(
|
||||
'feedbackcontentfiles' => array(
|
||||
'type' => external_files::get_properties_for_exporter(),
|
||||
'multiple' => true,
|
||||
),
|
||||
'feedbackattachmentfiles' => array(
|
||||
'type' => external_files::get_properties_for_exporter(),
|
||||
'multiple' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$context = $this->related['context'];
|
||||
|
||||
$values['feedbackcontentfiles'] =
|
||||
external_util::get_area_files($context->id, 'mod_workshop', 'overallfeedback_content', $this->data->id);
|
||||
$values['feedbackattachmentfiles'] =
|
||||
external_util::get_area_files($context->id, 'mod_workshop', 'overallfeedback_attachment', $this->data->id);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatting parameters for the content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_format_parameters_for_feedbackauthor() {
|
||||
return [
|
||||
'component' => 'mod_workshop',
|
||||
'filearea' => 'overallfeedback_content',
|
||||
'itemid' => $this->data->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -99,4 +99,11 @@ $functions = array(
|
||||
'type' => 'read',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
'mod_workshop_get_submission_assessments' => array(
|
||||
'classname' => 'mod_workshop_external',
|
||||
'methodname' => 'get_submission_assessments',
|
||||
'description' => 'Retrieves all the assessments of the given submission.',
|
||||
'type' => 'read',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1065,4 +1065,81 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
|
||||
$result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
|
||||
$this->assertEquals($submissionid3, $result['submission']['id']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test get_submission_assessments_student.
|
||||
*/
|
||||
public function test_get_submission_assessments_student() {
|
||||
global $DB;
|
||||
|
||||
// Create the submission that will be deleted.
|
||||
$submissionid = $this->create_test_submission($this->student);
|
||||
|
||||
$workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
|
||||
$workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
|
||||
'weight' => 3,
|
||||
'grade' => 95,
|
||||
));
|
||||
$workshopgenerator->create_assessment($submissionid, $this->student->id, array(
|
||||
'weight' => 2,
|
||||
'grade' => 90,
|
||||
));
|
||||
|
||||
$DB->set_field('workshop', 'phase', workshop::PHASE_CLOSED, array('id' => $this->workshop->id));
|
||||
$this->setUser($this->student);
|
||||
$result = mod_workshop_external::get_submission_assessments($submissionid);
|
||||
$result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
|
||||
$this->assertCount(2, $result['assessments']); // I received my two assessments.
|
||||
foreach ($result['assessments'] as $assessment) {
|
||||
if ($assessment['grade'] == 90) {
|
||||
// My own assessment, I can see me.
|
||||
$this->assertEquals($this->student->id, $assessment['reviewerid']);
|
||||
} else {
|
||||
// Student's can't see who did the review.
|
||||
$this->assertEquals(0, $assessment['reviewerid']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_submission_assessments_invalid_phase.
|
||||
*/
|
||||
public function test_get_submission_assessments_invalid_phase() {
|
||||
global $DB;
|
||||
|
||||
// Create the submission that will be deleted.
|
||||
$submissionid = $this->create_test_submission($this->student);
|
||||
|
||||
$workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
|
||||
$workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
|
||||
'weight' => 3,
|
||||
'grade' => 95,
|
||||
));
|
||||
|
||||
$this->expectException('moodle_exception');
|
||||
mod_workshop_external::get_submission_assessments($submissionid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_submission_assessments_teacher.
|
||||
*/
|
||||
public function test_get_submission_assessments_teacher() {
|
||||
|
||||
// Create the submission that will be deleted.
|
||||
$submissionid = $this->create_test_submission($this->student);
|
||||
|
||||
$workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
|
||||
$assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
|
||||
'weight' => 1,
|
||||
'grade' => 50,
|
||||
));
|
||||
|
||||
$this->setUser($this->teacher);
|
||||
$result = mod_workshop_external::get_submission_assessments($submissionid);
|
||||
$result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
|
||||
$this->assertCount(1, $result['assessments']);
|
||||
$this->assertEquals(50, $result['assessments'][0]['grade']);
|
||||
$this->assertEquals($assessmentid, $result['assessments'][0]['id']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2017051509; // The current module version (YYYYMMDDXX)
|
||||
$plugin->version = 2017051510; // 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.
|
||||
|
||||
Reference in New Issue
Block a user