MDL-59246 mod_workshop: New WS get_submission_assessments

This commit is contained in:
Juan Leyva
2017-10-02 09:36:36 +02:00
parent 6e2f4866b0
commit 30b54b823a
5 changed files with 448 additions and 17 deletions
+184 -16
View File
@@ -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()
)
);
}
}
+179
View File
@@ -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,
];
}
}
+7
View File
@@ -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)
),
);
+77
View File
@@ -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']);
}
}
+1 -1
View File
@@ -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.