MDL-61905 workshop: Implement privacy API in grading strategies

On low level, standard grading strategies subplugins do not store
personal themselves.  They make use of the grades storage provided by
the workshop itself.  What they do contain though is the information
about how the assessment forms were defined. And they are also
responsible for correctly interpreting the values in the central grades
table.

Grading strategies fulfil the contract with the parent workshop module
by implementing the workshopform_provider interface. That gives them a
chance to export data about the assessment form to each of exported
assessment.
This commit is contained in:
David Mudrák 2018-04-27 11:07:41 +02:00
parent 84a57322c2
commit fe745a0a84
12 changed files with 980 additions and 2 deletions

View File

@ -0,0 +1,105 @@
<?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/>.
/**
* Provides the class {@link workshopform_accumulative\privacy\provider}
*
* @package workshopform_accumulative
* @category privacy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace workshopform_accumulative\privacy;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy API implementation for the Accumulative grading strategy.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider, \mod_workshop\privacy\workshopform_provider {
/**
* Explain that this plugin stores no personal data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Return details of the filled assessment form.
*
* @param stdClass $user User we are exporting data for
* @param context $context The workshop activity context
* @param array $subcontext Subcontext within the context to export to
* @param int $assessmentid ID of the assessment
*/
public static function export_assessment_form(\stdClass $user, \context $context, array $subcontext, int $assessmentid) {
global $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
throw new \coding_exception('Unexpected context provided');
}
$sql = "SELECT dim.id, dim.description, dim.descriptionformat, dim.grade AS dimgrade, dim.weight,
wg.grade, wg.peercomment, wg.peercommentformat
FROM {course_modules} cm
JOIN {context} ctx ON ctx.contextlevel = :contextlevel AND ctx.instanceid = cm.id
JOIN {workshop} w ON cm.instance = w.id
JOIN {workshopform_accumulative} dim ON dim.workshopid = w.id
LEFT JOIN {workshop_grades} wg ON wg.strategy = :strategy
AND wg.dimensionid = dim.id AND wg.assessmentid = :assessmentid
WHERE ctx.id = :contextid
ORDER BY dim.sort";
$params = [
'strategy' => 'accumulative',
'contextlevel' => CONTEXT_MODULE,
'contextid' => $context->id,
'assessmentid' => $assessmentid,
];
$writer = \core_privacy\local\request\writer::with_context($context);
$data = [];
$hasdata = false;
$dimensionids = [];
foreach ($DB->get_records_sql($sql, $params) as $record) {
if ($record->grade !== null) {
$hasdata = true;
}
$record->description = $writer->rewrite_pluginfile_urls($subcontext, 'workshopform_accumulative',
'description', $record->id, $record->description);
$dimensionids[] = $record->id;
unset($record->id);
$data[] = $record;
}
if ($hasdata) {
$writer->export_data($subcontext, (object) ['aspects' => $data]);
foreach ($dimensionids as $dimensionid) {
$writer->export_area_files($subcontext, 'workshopform_accumulative', 'description', $dimensionid);
}
}
}
}

View File

@ -39,6 +39,7 @@ $string['mustchoosegrade'] = 'You have to select a grade for this aspect';
$string['pluginname'] = 'Accumulative grading';
$string['poor'] = 'Poor';
$string['present'] = 'Present';
$string['privacy:metadata'] = 'The Accumulative grading plugin only stores the details of the assessment form. Actual personal data of how the form has been filled are stored by the Workshop module itself and are attached to exported assessments.';
$string['scalename0'] = 'Yes/No (2 point)';
$string['scalename1'] = 'Present/Absent (2 point)';
$string['scalename2'] = 'Correct/Incorrect (2 point)';

View File

@ -0,0 +1,122 @@
<?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/>.
/**
* Provides the {@link workshopform_accumulative_privacy_provider_testcase} class.
*
* @package workshopform_accumulative
* @category test
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\local\request\writer;
/**
* Unit tests for the privacy API implementation.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class workshopform_accumulative_privacy_provider_testcase extends advanced_testcase {
/**
* Test {@link workshopform_accumulative\privacy\provider::export_assessment_form()} implementation.
*/
public function test_export_assessment_form() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$this->generator = $this->getDataGenerator();
$this->workshopgenerator = $this->generator->get_plugin_generator('mod_workshop');
$this->course1 = $this->generator->create_course();
$this->workshop11 = $this->generator->create_module('workshop', [
'course' => $this->course1,
'name' => 'Workshop11',
]);
$DB->set_field('workshop', 'phase', 50, ['id' => $this->workshop11->id]);
$this->dim1 = $DB->insert_record('workshopform_accumulative', [
'workshopid' => $this->workshop11->id,
'sort' => 1,
'description' => 'Aspect 1 description',
'descriptionformat' => FORMAT_MARKDOWN,
'grade' => 6,
'weight' => 1,
]);
$this->dim2 = $DB->insert_record('workshopform_accumulative', [
'workshopid' => $this->workshop11->id,
'sort' => 2,
'description' => 'Aspect 2 description',
'descriptionformat' => FORMAT_MARKDOWN,
'grade' => 4,
'weight' => 1,
]);
$this->student1 = $this->generator->create_user();
$this->student2 = $this->generator->create_user();
$this->submission111 = $this->workshopgenerator->create_submission($this->workshop11->id, $this->student1->id);
$this->assessment1112 = $this->workshopgenerator->create_assessment($this->submission111, $this->student2->id, [
'grade' => 92,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'accumulative',
'dimensionid' => $this->dim1,
'grade' => 3,
'peercomment' => 'Not awesome',
'peercommentformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'accumulative',
'dimensionid' => $this->dim2,
'grade' => 4,
'peercomment' => 'All good',
'peercommentformat' => FORMAT_PLAIN,
]);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_workshop', [
\context_module::instance($this->workshop11->cmid)->id,
]);
\mod_workshop\privacy\provider::export_user_data($contextlist);
$writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
$form = $writer->get_data([
get_string('myassessments', 'mod_workshop'),
$this->assessment1112,
get_string('assessmentform', 'mod_workshop'),
get_string('pluginname', 'workshopform_accumulative'),
]);
$this->assertEquals('Aspect 1 description', $form->aspects[0]->description);
$this->assertEquals(4, $form->aspects[1]->grade);
}
}

View File

@ -0,0 +1,104 @@
<?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/>.
/**
* Provides the class {@link workshopform_comments\privacy\provider}
*
* @package workshopform_comments
* @category privacy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace workshopform_comments\privacy;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy API implementation for the Comments grading strategy.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider, \mod_workshop\privacy\workshopform_provider {
/**
* Explain that this plugin stores no personal data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Return details of the filled assessment form.
*
* @param stdClass $user User we are exporting data for
* @param context $context The workshop activity context
* @param array $subcontext Subcontext within the context to export to
* @param int $assessmentid ID of the assessment
*/
public static function export_assessment_form(\stdClass $user, \context $context, array $subcontext, int $assessmentid) {
global $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
throw new \coding_exception('Unexpected context provided');
}
$sql = "SELECT dim.id, dim.description, dim.descriptionformat, wg.peercomment, wg.peercommentformat
FROM {course_modules} cm
JOIN {context} ctx ON ctx.contextlevel = :contextlevel AND ctx.instanceid = cm.id
JOIN {workshop} w ON cm.instance = w.id
JOIN {workshopform_comments} dim ON dim.workshopid = w.id
LEFT JOIN {workshop_grades} wg ON wg.strategy = :strategy
AND wg.dimensionid = dim.id AND wg.assessmentid = :assessmentid
WHERE ctx.id = :contextid
ORDER BY dim.sort";
$params = [
'strategy' => 'comments',
'contextlevel' => CONTEXT_MODULE,
'contextid' => $context->id,
'assessmentid' => $assessmentid,
];
$writer = \core_privacy\local\request\writer::with_context($context);
$data = [];
$hasdata = false;
$dimensionids = [];
foreach ($DB->get_records_sql($sql, $params) as $record) {
if ($record->peercomment !== null) {
$hasdata = true;
}
$record->description = $writer->rewrite_pluginfile_urls($subcontext, 'workshopform_comments',
'description', $record->id, $record->description);
$dimensionids[] = $record->id;
unset($record->id);
$data[] = $record;
}
if ($hasdata) {
$writer->export_data($subcontext, (object) ['aspects' => $data]);
foreach ($dimensionids as $dimensionid) {
$writer->export_area_files($subcontext, 'workshopform_comments', 'description', $dimensionid);
}
}
}
}

View File

@ -28,4 +28,4 @@ $string['dimensioncommentfor'] = 'Comment for {$a}';
$string['dimensiondescription'] = 'Description';
$string['dimensionnumber'] = 'Aspect {$a}';
$string['pluginname'] = 'Comments';
$string['privacy:metadata'] = 'The Comments grading plugin only stores the details of the assessment form. Actual personal data of how the form has been filled are stored by the Workshop module itself and are attached to exported assessments.';

View File

@ -0,0 +1,118 @@
<?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/>.
/**
* Provides the {@link workshopform_comments_privacy_provider_testcase} class.
*
* @package workshopform_comments
* @category test
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\local\request\writer;
/**
* Unit tests for the privacy API implementation.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class workshopform_comments_privacy_provider_testcase extends advanced_testcase {
/**
* Test {@link workshopform_comments\privacy\provider::export_assessment_form()} implementation.
*/
public function test_export_assessment_form() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$this->generator = $this->getDataGenerator();
$this->workshopgenerator = $this->generator->get_plugin_generator('mod_workshop');
$this->course1 = $this->generator->create_course();
$this->workshop11 = $this->generator->create_module('workshop', [
'course' => $this->course1,
'name' => 'Workshop11',
]);
$DB->set_field('workshop', 'phase', 100, ['id' => $this->workshop11->id]);
$this->dim1 = $DB->insert_record('workshopform_comments', [
'workshopid' => $this->workshop11->id,
'sort' => 1,
'description' => 'Aspect 1 description',
'descriptionformat' => FORMAT_MARKDOWN,
]);
$this->dim2 = $DB->insert_record('workshopform_comments', [
'workshopid' => $this->workshop11->id,
'sort' => 2,
'description' => 'Aspect 2 description',
'descriptionformat' => FORMAT_MARKDOWN,
]);
$this->student1 = $this->generator->create_user();
$this->student2 = $this->generator->create_user();
$this->submission111 = $this->workshopgenerator->create_submission($this->workshop11->id, $this->student1->id);
$this->assessment1112 = $this->workshopgenerator->create_assessment($this->submission111, $this->student2->id, [
'grade' => 92,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'comments',
'dimensionid' => $this->dim1,
'grade' => 100,
'peercomment' => 'Not awesome',
'peercommentformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'comments',
'dimensionid' => $this->dim2,
'grade' => 100,
'peercomment' => 'All good',
'peercommentformat' => FORMAT_PLAIN,
]);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_workshop', [
\context_module::instance($this->workshop11->cmid)->id,
]);
\mod_workshop\privacy\provider::export_user_data($contextlist);
$writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
$form = $writer->get_data([
get_string('myassessments', 'mod_workshop'),
$this->assessment1112,
get_string('assessmentform', 'mod_workshop'),
get_string('pluginname', 'workshopform_comments'),
]);
$this->assertEquals('Aspect 1 description', $form->aspects[0]->description);
$this->assertEquals('All good', $form->aspects[1]->peercomment);
}
}

View File

@ -0,0 +1,117 @@
<?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/>.
/**
* Provides the class {@link workshopform_numerrors\privacy\provider}
*
* @package workshopform_numerrors
* @category privacy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace workshopform_numerrors\privacy;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy API implementation for the Number of errors strategy.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider, \mod_workshop\privacy\workshopform_provider {
/**
* Explain that this plugin stores no personal data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Return details of the filled assessment form.
*
* @param stdClass $user User we are exporting data for
* @param context $context The workshop activity context
* @param array $subcontext Subcontext within the context to export to
* @param int $assessmentid ID of the assessment
*/
public static function export_assessment_form(\stdClass $user, \context $context, array $subcontext, int $assessmentid) {
global $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
throw new \coding_exception('Unexpected context provided');
}
$sql = "SELECT dim.id, dim.workshopid, dim.description, dim.descriptionformat, dim.grade0, dim.grade1, dim.weight,
wg.grade, wg.peercomment, wg.peercommentformat
FROM {course_modules} cm
JOIN {context} ctx ON ctx.contextlevel = :contextlevel AND ctx.instanceid = cm.id
JOIN {workshop} w ON cm.instance = w.id
JOIN {workshopform_numerrors} dim ON dim.workshopid = w.id
LEFT JOIN {workshop_grades} wg ON wg.strategy = :strategy
AND wg.dimensionid = dim.id AND wg.assessmentid = :assessmentid
WHERE ctx.id = :contextid
ORDER BY dim.sort";
$params = [
'strategy' => 'numerrors',
'contextlevel' => CONTEXT_MODULE,
'contextid' => $context->id,
'assessmentid' => $assessmentid,
];
$writer = \core_privacy\local\request\writer::with_context($context);
$data = [];
$workshopid = null;
$hasdata = false;
$dimensionids = [];
foreach ($DB->get_records_sql($sql, $params) as $record) {
if ($record->grade !== null) {
$hasdata = true;
}
$record->description = $writer->rewrite_pluginfile_urls($subcontext, 'workshopform_numerrors',
'description', $record->id, $record->description);
$workshopid = $record->workshopid;
$dimensionids[] = $record->id;
unset($record->id);
unset($record->workshopid);
$data[] = $record;
}
if ($hasdata) {
$writer->export_data($subcontext, (object)['assertions' => $data]);
foreach ($dimensionids as $dimensionid) {
$writer->export_area_files($subcontext, 'workshopform_numerrors', 'description', $dimensionid);
}
foreach ($DB->get_records('workshopform_numerrors_map', ['workshopid' => $workshopid], 'nonegative',
'id, nonegative, grade') as $mapping) {
$writer->export_metadata($subcontext, 'map_'.$mapping->nonegative.'_errors', $mapping->grade,
get_string('privacy:export:metadata:map', 'workshopform_numerrors', [
'nonegative' => $mapping->nonegative,
'grade' => $mapping->grade
])
);
}
}
}
}

View File

@ -40,4 +40,5 @@ $string['maperror'] = 'Weighted number of errors is less than or equals';
$string['mapgrade'] = 'Grade for submission';
$string['percents'] = '{$a} %';
$string['pluginname'] = 'Number of errors';
$string['privacy:metadata'] = 'The Number of errors plugin only stores the details of the assessment form. Actual personal data of how the form has been filled are stored by the Workshop module itself and are attached to exported assessments.';
$string['privacy:export:metadata:map'] = 'If the weighted number of errors reaches {$a->nonegative} then the grade is {$a->grade} percents.';

View File

@ -0,0 +1,127 @@
<?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/>.
/**
* Provides the {@link workshopform_numerrors_privacy_provider_testcase} class.
*
* @package workshopform_numerrors
* @category test
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\local\request\writer;
/**
* Unit tests for the privacy API implementation.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class workshopform_numerrors_privacy_provider_testcase extends advanced_testcase {
/**
* Test {@link workshopform_numerrors\privacy\provider::export_assessment_form()} implementation.
*/
public function test_export_assessment_form() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$this->generator = $this->getDataGenerator();
$this->workshopgenerator = $this->generator->get_plugin_generator('mod_workshop');
$this->course1 = $this->generator->create_course();
$this->workshop11 = $this->generator->create_module('workshop', [
'course' => $this->course1,
'name' => 'Workshop11',
]);
$DB->set_field('workshop', 'phase', 50, ['id' => $this->workshop11->id]);
$this->dim1 = $DB->insert_record('workshopform_numerrors', [
'workshopid' => $this->workshop11->id,
'sort' => 1,
'description' => 'Assertion 1 description',
'descriptionformat' => FORMAT_MARKDOWN,
'descriptiontrust' => 0,
'grade0' => 'No',
'grade1' => 'Yes',
'weight' => 1,
]);
$this->dim2 = $DB->insert_record('workshopform_numerrors', [
'workshopid' => $this->workshop11->id,
'sort' => 2,
'description' => 'Assertion 2 description',
'descriptionformat' => FORMAT_MARKDOWN,
'descriptiontrust' => 0,
'grade0' => 'Missing',
'grade1' => 'Present',
'weight' => 1,
]);
$this->student1 = $this->generator->create_user();
$this->student2 = $this->generator->create_user();
$this->submission111 = $this->workshopgenerator->create_submission($this->workshop11->id, $this->student1->id);
$this->assessment1112 = $this->workshopgenerator->create_assessment($this->submission111, $this->student2->id, [
'grade' => 92,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'numerrors',
'dimensionid' => $this->dim1,
'grade' => 1,
'peercomment' => 'Awesome',
'peercommentformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'numerrors',
'dimensionid' => $this->dim2,
'grade' => 0,
'peercomment' => 'Missing',
'peercommentformat' => FORMAT_PLAIN,
]);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_workshop', [
\context_module::instance($this->workshop11->cmid)->id,
]);
\mod_workshop\privacy\provider::export_user_data($contextlist);
$writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
$form = $writer->get_data([
get_string('myassessments', 'mod_workshop'),
$this->assessment1112,
get_string('assessmentform', 'mod_workshop'),
get_string('pluginname', 'workshopform_numerrors'),
]);
$this->assertEquals('Assertion 1 description', $form->assertions[0]->description);
$this->assertEquals(0, $form->assertions[1]->grade);
$this->assertEquals('Missing', $form->assertions[1]->peercomment);
}
}

View File

@ -0,0 +1,127 @@
<?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/>.
/**
* Provides the class {@link workshopform_rubric\privacy\provider}
*
* @package workshopform_rubric
* @category privacy
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace workshopform_rubric\privacy;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy API implementation for the Rubric strategy.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider, \mod_workshop\privacy\workshopform_provider {
/**
* Explain that this plugin stores no personal data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
/**
* Return details of the filled assessment form.
*
* @param stdClass $user User we are exporting data for
* @param context $context The workshop activity context
* @param array $subcontext Subcontext within the context to export to
* @param int $assessmentid ID of the assessment
*/
public static function export_assessment_form(\stdClass $user, \context $context, array $subcontext, int $assessmentid) {
global $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
throw new \coding_exception('Unexpected context provided');
}
$sql = "SELECT r.id, r.workshopid, r.description, r.descriptionformat,
rl.id AS levelid, rl.grade AS levelgrade, rl.definition, rl.definitionformat,
wg.grade
FROM {course_modules} cm
JOIN {context} ctx ON ctx.contextlevel = :contextlevel AND ctx.instanceid = cm.id
JOIN {workshop} w ON cm.instance = w.id
JOIN {workshopform_rubric} r ON r.workshopid = w.id
JOIN {workshopform_rubric_levels} rl ON rl.dimensionid = r.id
LEFT JOIN {workshop_grades} wg ON wg.strategy = :strategy AND wg.dimensionid = r.id AND wg.assessmentid = :assessmentid
WHERE ctx.id = :contextid
ORDER BY r.sort, rl.grade DESC";
$params = [
'strategy' => 'rubric',
'contextlevel' => CONTEXT_MODULE,
'contextid' => $context->id,
'assessmentid' => $assessmentid,
];
$writer = \core_privacy\local\request\writer::with_context($context);
$criteria = [];
$workshopid = null;
$hasdata = false;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $record) {
if (empty($criteria[$record->id])) {
$criteria[$record->id] = (object) [
'description' => $writer->rewrite_pluginfile_urls($subcontext, 'workshopform_rubric', 'description',
$record->id, $record->description),
'descriptionformat' => $record->descriptionformat,
'grade' => $record->grade,
'levels' => [],
];
$workshopid = $record->workshopid;
}
$criteria[$record->id]->levels[] = (object) [
'grade' => $record->levelgrade,
'definition' => $record->definition,
'definitionformat' => $record->definitionformat,
];
if ($record->grade !== null) {
$hasdata = true;
}
}
$rs->close();
if ($hasdata) {
$data = (object) [
'criteria' => array_values($criteria),
];
$layout = $DB->get_field('workshopform_rubric_config', 'layout', ['workshopid' => $workshopid]);
foreach (array_keys($criteria) as $dimensionid) {
$writer->export_area_files($subcontext, 'workshopform_rubric', 'description', $dimensionid);
}
$writer->export_data($subcontext, $data);
$writer->export_metadata($subcontext, 'layout', $layout, get_string('layout', 'workshopform_rubric'));
}
}
}

View File

@ -37,3 +37,4 @@ $string['mustbeunique'] = 'Level grades must be unique within a criterion';
$string['mustdefinelevel'] = 'At least one level is required';
$string['mustchooseone'] = 'You have to select one of these items';
$string['pluginname'] = 'Rubric';
$string['privacy:metadata'] = 'The Rubric plugin only stores the details of the assessment form. Actual personal data of how the form has been filled are stored by the Workshop module itself and are attached to exported assessments.';

View File

@ -0,0 +1,155 @@
<?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/>.
/**
* Provides the {@link workshopform_rubric_privacy_provider_testcase} class.
*
* @package workshopform_rubric
* @category test
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\local\request\writer;
/**
* Unit tests for the privacy API implementation.
*
* @copyright 2018 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class workshopform_rubric_privacy_provider_testcase extends advanced_testcase {
/**
* Test {@link workshopform_rubric\privacy\provider::export_assessment_form()} implementation.
*/
public function test_export_assessment_form() {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$this->generator = $this->getDataGenerator();
$this->workshopgenerator = $this->generator->get_plugin_generator('mod_workshop');
$this->course1 = $this->generator->create_course();
$this->workshop11 = $this->generator->create_module('workshop', [
'course' => $this->course1,
'name' => 'Workshop11',
]);
$DB->set_field('workshop', 'phase', 50, ['id' => $this->workshop11->id]);
$this->dim1 = $DB->insert_record('workshopform_rubric', [
'workshopid' => $this->workshop11->id,
'sort' => 1,
'description' => 'Criterion 1 description',
'descriptionformat' => FORMAT_MARKDOWN,
]);
$DB->insert_record('workshopform_rubric_levels', [
'dimensionid' => $this->dim1,
'grade' => 0,
'definition' => 'Missing',
'definitionformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshopform_rubric_levels', [
'dimensionid' => $this->dim1,
'grade' => 1,
'definition' => 'Poor',
'definitionformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshopform_rubric_levels', [
'dimensionid' => $this->dim1,
'grade' => 2,
'definition' => 'Good',
'definitionformat' => FORMAT_PLAIN,
]);
$this->dim2 = $DB->insert_record('workshopform_rubric', [
'workshopid' => $this->workshop11->id,
'sort' => 2,
'description' => 'Criterion 2 description',
'descriptionformat' => FORMAT_MARKDOWN,
]);
$DB->insert_record('workshopform_rubric_levels', [
'dimensionid' => $this->dim2,
'grade' => 0,
'definition' => 'Missing',
'definitionformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshopform_rubric_levels', [
'dimensionid' => $this->dim2,
'grade' => 5,
'definition' => 'Great',
'definitionformat' => FORMAT_PLAIN,
]);
$this->student1 = $this->generator->create_user();
$this->student2 = $this->generator->create_user();
$this->submission111 = $this->workshopgenerator->create_submission($this->workshop11->id, $this->student1->id);
$this->assessment1112 = $this->workshopgenerator->create_assessment($this->submission111, $this->student2->id, [
'grade' => 92,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'rubric',
'dimensionid' => $this->dim1,
'grade' => 1,
'peercomment' => '',
'peercommentformat' => FORMAT_PLAIN,
]);
$DB->insert_record('workshop_grades', [
'assessmentid' => $this->assessment1112,
'strategy' => 'rubric',
'dimensionid' => $this->dim2,
'grade' => 5,
'peercomment' => '',
'peercommentformat' => FORMAT_PLAIN,
]);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_workshop', [
\context_module::instance($this->workshop11->cmid)->id,
]);
\mod_workshop\privacy\provider::export_user_data($contextlist);
$writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
$form = $writer->get_data([
get_string('myassessments', 'mod_workshop'),
$this->assessment1112,
get_string('assessmentform', 'mod_workshop'),
get_string('pluginname', 'workshopform_rubric'),
]);
$this->assertEquals('Criterion 1 description', $form->criteria[0]->description);
$this->assertEquals(3, count($form->criteria[0]->levels));
$this->assertEquals(2, count($form->criteria[1]->levels));
$this->assertEquals(5, $form->criteria[1]->grade);
}
}