Skip to content

Commit

Permalink
Questionnaire: option to auto-delete responses after X time
Browse files Browse the repository at this point in the history
  • Loading branch information
toanlam committed Jun 5, 2024
1 parent b06947c commit a3b9ace
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 3 deletions.
2 changes: 1 addition & 1 deletion backup/moodle2/backup_questionnaire_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function define_structure() {
$questionnaire = new backup_nested_element('questionnaire', array('id'), array(
'course', 'name', 'intro', 'introformat', 'qtype',
'respondenttype', 'resp_eligible', 'resp_view', 'notifications', 'opendate',
'closedate', 'resume', 'navigate', 'grade', 'sid', 'timemodified', 'completionsubmit', 'autonum'));
'closedate', 'resume', 'navigate', 'grade', 'sid', 'timemodified', 'completionsubmit', 'autonum', 'removeafter'));

$surveys = new backup_nested_element('surveys');

Expand Down
1 change: 1 addition & 0 deletions classes/task/cleanup.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ public function execute() {
require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');

questionnaire_cleanup();
questionnaire_delete_old_responses();
}
}
1 change: 1 addition & 0 deletions db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<FIELD NAME="completionsubmit" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Questionnaire marked as 'complete' when submitted."/>
<FIELD NAME="autonum" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="3" SEQUENCE="false" COMMENT="option for auto numbering questions and pages (both selected by default)"/>
<FIELD NAME="progressbar" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Display a progress bar at top of questionnaire."/>
<FIELD NAME="removeafter" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Remove old responses after certain period. 1 for one month, 2 for two months...12 for one year, 13 for two years and 14 for three years."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand Down
14 changes: 14 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,20 @@ function xmldb_questionnaire_upgrade($oldversion=0) {
upgrade_mod_savepoint(true, 2022121600.02, 'questionnaire');
}

if ($oldversion < 2024060400.00) {
// Add removeafter fields.
$table = new xmldb_table('questionnaire');
$field = new xmldb_field('removeafter', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'progressbar');

// Conditionally launch add field.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Questionnaire savepoint reached.
upgrade_mod_savepoint(true, 2024060400.00, 'questionnaire');
}

return true;
}

Expand Down
6 changes: 6 additions & 0 deletions lang/en/questionnaire.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
$string['createcontent_help'] = 'Select one of the radio button options. \'Create new\' is the default.';
$string['createcontent_link'] = 'mod/questionnaire/mod#Content_Options';
$string['createnew'] = 'Create new';
$string['configremoveoldresponses'] = 'Setting which will be used as default on all new questionares.';
$string['centerlabel'] = 'Centre label';
$string['date'] = 'Date';
$string['date_help'] = 'Use this question type if you expect the response to be a correctly formatted date.';
Expand Down Expand Up @@ -403,6 +404,7 @@
$string['overviewnumrespvw'] = 'responses';
$string['overviewnumrespvw1'] = 'response';
$string['owner'] = 'Owner';
$string['onemonth'] = '1 month';
$string['page'] = 'Page';
$string['pageof'] = 'Page {$a->page} of {$a->totpages}';
$string['parent'] = 'Parent';
Expand Down Expand Up @@ -566,6 +568,10 @@
$string['resume_link'] = 'mod/questionnaire/mod#Save/Resume_answers';
$string['resumesurvey'] = 'Resume questionnaire';
$string['return'] = 'Return';
$string['removeoldresponsesdefault'] = 'Never remove';
$string['removeoldresponses'] = 'Manage old responses';
$string['removeoldresponsesafter'] = 'Manage old responses after';
$string['removeoldresponses_help'] = 'The system can automatically remove responses after a certain length of time.';
$string['rightlabel'] = 'Right label';
$string['rightpart'] = ' and {$a->max} is {$a->rightlabel}';
$string['rightpartdefault'] = ' and {$a->max} is maximum slider range';
Expand Down
60 changes: 60 additions & 0 deletions locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -949,3 +949,63 @@ function questionnaire_get_standard_page_items($id = null, $a = null) {

return (array($cm, $course, $questionnaire));
}


/**
* Create options for remove old responses in the questionare.
*
* @return array
*/
function questionnaire_create_remove_options() {
$options = [];
$options[0] = get_string('removeoldresponsesdefault', 'questionnaire');
for ($i = 1; $i <= 36; $i++) {
$options[$i * 2592000] = $i > 1 ? get_string('nummonths', 'moodle', $i) : get_string('onemonth', 'questionnaire');
}
return $options;
}

/**
* Delete all the old responses when we have setting the questionnaire.
*
* @throws coding_exception
* @throws dml_exception
*/
function questionnaire_delete_old_responses() {
global $DB;
$currenttime = time();

$sql = "SELECT qr.id
FROM {questionnaire} q
JOIN {questionnaire_response} qr ON qr.questionnaireid = q.id AND qr.complete = 'y'
WHERE q.removeafter <> 0 AND (q.removeafter < :currettime - qr.submitted)";
// Get all old response from questionnaires.
$oldresponsesid = $DB->get_records_sql($sql, ['currettime' => $currenttime]);
if (!empty($oldresponsesid)) {
try {
$oldresponsesid = array_keys($oldresponsesid);
$count = count($oldresponsesid);
if (!PHPUNIT_TEST) {
mtrace("\nBeginning deleting $count old responses requests");
}
// Delete all of the response data for a response.
$responsetables = [
'questionnaire_response_bool', 'questionnaire_response_date', 'questionnaire_resp_multiple',
'questionnaire_response_other', 'questionnaire_response_rank', 'questionnaire_resp_single',
'questionnaire_response_text'];
list ($sqlparam, $params) = $DB->get_in_or_equal($oldresponsesid, SQL_PARAMS_QM);
foreach ($responsetables as $tablename) {
$sql = "DELETE FROM {{$tablename}} WHERE response_id $sqlparam";
$DB->execute($sql, $params);
}
// Delete the response from the main table.
$sql = "DELETE FROM {questionnaire_response} WHERE id $sqlparam";
$DB->execute($sql, $params);
if (!PHPUNIT_TEST) {
mtrace("\nCompleted deleting $count old responses requests");
}
} catch (\dml_exception $ex) {
debugging('Error: ' . $ex->getMessage(), DEBUG_DEVELOPER);
}
}
}
27 changes: 26 additions & 1 deletion mod_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class mod_questionnaire_mod_form extends moodleform_mod {
* Form definition.
*/
protected function definition() {
global $COURSE;
global $COURSE, $CFG;
global $questionnairetypes, $questionnairerespondents, $questionnaireresponseviewers, $autonumbering;

$questionnaire = new questionnaire($COURSE, $this->_cm, $this->_instance, null);
Expand Down Expand Up @@ -142,6 +142,17 @@ protected function definition() {
$mform->setDefault('create', 'new-0');
}

// Remove old responses.
$options = questionnaire_create_remove_options();
$mform->addElement('header', 'responsehdr', get_string('removeoldresponses', 'questionnaire'));
$mform->addElement('select', 'removeafter',
get_string('removeoldresponsesafter', 'questionnaire'), $options);
$mform->addHelpButton('removeafter', 'removeoldresponses', 'questionnaire');
// Just set default value when creating a new questionare.
if (empty($questionnaire->sid)) {
$defaultconfig = get_config('questionnaire', 'removeoldresponses');
$mform->setDefault('removeafter', $defaultconfig);
}
$this->standard_coursemodule_elements();

// Buttons.
Expand Down Expand Up @@ -223,4 +234,18 @@ public function completion_rule_enabled($data) {
return !empty($data['completionsubmit']);
}

/**
* Create options for remove old responses in the questionare.
*
* @return array
*/
public function create_remove_options() {
$options = [];
$options[0] = get_string('removeoldresponsesdefault', 'questionnaire');
for ($i = 1; $i <= 36; $i++) {
$options[$i * 2592000] = $i > 1 ? get_string('nummonths', 'moodle', $i) : get_string('onemonth', 'questionnaire');
}
return $options;
}

}
7 changes: 7 additions & 0 deletions settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/

defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');

if ($ADMIN->fulltree) {
$options = array(0 => get_string('no'), 1 => get_string('yes'));
Expand Down Expand Up @@ -52,4 +53,10 @@

$settings->add(new admin_setting_configcheckbox('questionnaire/allowemailreporting',
get_string('configemailreporting', 'questionnaire'), get_string('configemailreportinglong', 'questionnaire'), 0));

// Manage old responses after. The default value is 24 months.
$options = questionnaire_create_remove_options();
$settings->add(new admin_setting_configselect('questionnaire/removeoldresponses',
get_string('removeoldresponsesafter', 'questionnaire'),
get_string('configremoveoldresponses', 'questionnaire'), 0, $options));
}
51 changes: 51 additions & 0 deletions tests/responsetypes_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -419,4 +419,55 @@ private function response_tests($questionnaireid, $responseid, $userid,
$this->assertArrayHasKey($responseid, $responses);
$this->assertEquals($responseid, $responses[$responseid]->id);
}

public function test_create_old_response_boolean() {
global $DB;

$this->resetAfterTest();

// Some common variables used below.
$userid = 1;

// Set up a questinnaire with one boolean response question.
$course = $this->getDataGenerator()->create_course();
$generator = $this->getDataGenerator()->get_plugin_generator('mod_questionnaire');
// Add a questionnaire that will delete old responses after one month.
$questionnaire1 = $generator->create_test_questionnaire($course, QUESYESNO, ['content' => 'Enter yes or no']);
$question1 = reset($questionnaire1->questions);
$response1 = $generator->create_question_response($questionnaire1, $question1, 'y', $userid);

$questionnaire2 = $generator->create_test_questionnaire($course, QUESYESNO, ['content' => 'Enter yes or no']);
$question2 = reset($questionnaire2->questions);
$response2 = $generator->create_question_response($questionnaire2, $question2, 'y', $userid);

$this->response_tests($questionnaire1->id, $response1->id, $userid);
$this->response_tests($questionnaire2->id, $response2->id, $userid);

// Set the removeafterfield for questionnaires.
$newquestionairre1 = new \stdClass();
$newquestionairre1->id = $questionnaire1->id;
$newquestionairre1->removeafter = 2592000;
$newquestionairre2 = new \stdClass();
$newquestionairre2->id = $questionnaire2->id;
$newquestionairre2->removeafter = 2592000;
$DB->update_record('questionnaire', $newquestionairre1);
$DB->update_record('questionnaire', $newquestionairre2);
// Retrieve the specific boolean response.
$booleanresponses1 = $DB->get_record('questionnaire_response', ['id' => $response1->id]);
$booleanresponses2 = $DB->get_record('questionnaire_response', ['id' => $response2->id]);
// Set the submitted time to 31 day in the past.
$booleanresponses1->submitted = $booleanresponses1->submitted - 2592000 - 86400;
$booleanresponses2->submitted = $booleanresponses2->submitted - 2592000 - 86400;
$DB->update_record('questionnaire_response', $booleanresponses1);
$DB->update_record('questionnaire_response', $booleanresponses2);
questionnaire_delete_old_responses();
$responseresult1 = $DB->record_exists('questionnaire_response', ['id' => $response1->id]);
$responseresult2 = $DB->record_exists('questionnaire_response', ['id' => $response2->id]);
$this->assertEmpty($responseresult1);
$this->assertEmpty($responseresult2);
$boolresponseresult1 = $DB->record_exists('questionnaire_response_bool', ['response_id' => $response1->id]);
$boolresponseresult2 = $DB->record_exists('questionnaire_response_bool', ['response_id' => $response2->id]);
$this->assertEmpty($boolresponseresult1);
$this->assertEmpty($boolresponseresult2);
}
}
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2022121600.02; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2024060400.00; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2022112800.00; // Moodle version (4.1.0).

$plugin->component = 'mod_questionnaire';
Expand Down

0 comments on commit a3b9ace

Please sign in to comment.