Skip to content

Commit 35c0bf7

Browse files
emanoylovEmanoil Manoylov
andauthored
GHI535 Support for user identity fields in Download Responses (#536)
Co-authored-by: Emanoil Manoylov <[email protected]>
1 parent 6818190 commit 35c0bf7

File tree

5 files changed

+198
-16
lines changed

5 files changed

+198
-16
lines changed

db/upgrade.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,11 @@ function xmldb_questionnaire_upgrade($oldversion=0) {
997997
upgrade_mod_savepoint(true, 2022092200, 'questionnaire');
998998
}
999999

1000+
if ($oldversion < 2022121600.02) {
1001+
// Upgrade for downloadoptions - useridentityfields setting.
1002+
upgrade_mod_savepoint(true, 2022121600.02, 'questionnaire');
1003+
}
1004+
10001005
return true;
10011006
}
10021007

questionnaire.class.php

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3066,29 +3066,23 @@ public function survey_is_public_master() {
30663066
* @param array $questionsbyposition
30673067
* @param int $nbinfocols
30683068
* @param int $numrespcols
3069-
* @param int $showincompletes
3069+
* @param array $options
3070+
* @param array $identityfields
30703071
* @return array
30713072
*/
30723073
protected function process_csv_row(array &$row,
30733074
stdClass $resprow,
30743075
$currentgroupid,
30753076
array &$questionsbyposition,
30763077
$nbinfocols,
3077-
$numrespcols, $showincompletes = 0) {
3078+
$numrespcols,
3079+
$options,
3080+
$identityfields) {
30783081
global $DB;
30793082

3080-
static $config = null;
30813083
// If using an anonymous response, map users to unique user numbers so that number of unique anonymous users can be seen.
30823084
static $anonumap = [];
30833085

3084-
if ($config === null) {
3085-
$config = get_config('questionnaire', 'downloadoptions');
3086-
}
3087-
$options = empty($config) ? array() : explode(',', $config);
3088-
if ($showincompletes == 1) {
3089-
$options[] = 'complete';
3090-
}
3091-
30923086
$positioned = [];
30933087
$user = new stdClass();
30943088
foreach ($this->user_fields() as $userfield) {
@@ -3184,6 +3178,9 @@ protected function process_csv_row(array &$row,
31843178
if (in_array('complete', $options)) {
31853179
array_push($positioned, $resprow->complete);
31863180
}
3181+
foreach ($identityfields as $field) {
3182+
array_push($positioned, $resprow->$field);
3183+
}
31873184

31883185
for ($c = $nbinfocols; $c < $numrespcols; $c++) {
31893186
if (isset($row[$c])) {
@@ -3234,11 +3231,18 @@ public function generate_csv($currentgroupid, $rid='', $userid='', $choicecodes=
32343231
if (in_array($option, array('response', 'submitted', 'id'))) {
32353232
$columns[] = get_string($option, 'questionnaire');
32363233
$types[] = 0;
3234+
} else if ($option == 'useridentityfields') {
3235+
// Ignore option.
3236+
continue;
32373237
} else {
32383238
$columns[] = get_string($option);
32393239
$types[] = 1;
32403240
}
32413241
}
3242+
$identityfields = $this->get_identity_fields($options);
3243+
foreach ($identityfields as $field) {
3244+
$columns[] = \core_user\fields::get_display_name($field);
3245+
}
32423246
$nbinfocols = count($columns);
32433247

32443248
$idtocsvmap = array(
@@ -3469,6 +3473,7 @@ public function generate_csv($currentgroupid, $rid='', $userid='', $choicecodes=
34693473
if ($rankaverages) {
34703474
$averagerow = [];
34713475
}
3476+
$useridentityfields = [];
34723477
foreach ($allresponsesrs as $responserow) {
34733478
$rid = $responserow->rid;
34743479
$qid = $responserow->question_id;
@@ -3478,6 +3483,21 @@ public function generate_csv($currentgroupid, $rid='', $userid='', $choicecodes=
34783483
continue;
34793484
}
34803485

3486+
if (!empty($identityfields)) {
3487+
// Get identity fields for user.
3488+
if (isset($useridentityfields[$responserow->userid])) {
3489+
$customfields = $useridentityfields[$responserow->userid];
3490+
} else {
3491+
$customfields = self::get_user_identity_fields($this->context, $responserow->userid);
3492+
$useridentityfields[$responserow->userid] = $customfields;
3493+
}
3494+
3495+
// Set profile fields for user in response row.
3496+
foreach ($identityfields as $field) {
3497+
$responserow->{$field} = $customfields->{$field};
3498+
}
3499+
}
3500+
34813501
$question = $this->questions[$qid];
34823502
$qtype = intval($question->type_id);
34833503
if ($rankaverages) {
@@ -3494,7 +3514,7 @@ public function generate_csv($currentgroupid, $rid='', $userid='', $choicecodes=
34943514

34953515
if ($prevresprow !== false && $prevresprow->rid !== $rid) {
34963516
$output[] = $this->process_csv_row($row, $prevresprow, $currentgroupid, $questionsbyposition,
3497-
$nbinfocols, $numrespcols, $showincompletes);
3517+
$nbinfocols, $numrespcols, $options, $identityfields);
34983518
$row = [];
34993519
}
35003520

@@ -3576,7 +3596,7 @@ public function generate_csv($currentgroupid, $rid='', $userid='', $choicecodes=
35763596
if ($prevresprow !== false) {
35773597
// Add final row to output. May not exist if no response data was ever present.
35783598
$output[] = $this->process_csv_row($row, $prevresprow, $currentgroupid, $questionsbyposition,
3579-
$nbinfocols, $numrespcols, $showincompletes);
3599+
$nbinfocols, $numrespcols, $options, $identityfields);
35803600
}
35813601

35823602
// Add averages row if appropriate.
@@ -4121,4 +4141,39 @@ public function get_all_file_areas() {
41214141

41224142
return $areas;
41234143
}
4144+
4145+
/**
4146+
* Gets the identity fields.
4147+
*
4148+
* @param array $options
4149+
* @return array
4150+
*/
4151+
protected function get_identity_fields($options) {
4152+
$fields = !in_array('useridentityfields', $options) || $this->respondenttype == 'anonymous' ? [] :
4153+
\core_user\fields::get_identity_fields($this->context);
4154+
return $fields;
4155+
}
4156+
4157+
/**
4158+
* Gets the identity fields values for a user.
4159+
*
4160+
* @param object $context
4161+
* @param int $userid
4162+
* @return array
4163+
*/
4164+
public static function get_user_identity_fields($context, $userid) {
4165+
global $DB;
4166+
4167+
$fields = \core_user\fields::for_identity($context);
4168+
[
4169+
'selects' => $selects,
4170+
'joins' => $joins,
4171+
'params' => $params
4172+
] = (array)$fields->get_sql('u', false, '', '', false);
4173+
$sql = "SELECT $selects
4174+
FROM {user} u $joins
4175+
WHERE u.id = ?";
4176+
$row = $DB->get_record_sql($sql, array_merge($params, [$userid]));
4177+
return $row;
4178+
}
41244179
}

settings.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
'group' => get_string('group'),
4444
'id' => get_string('id', 'questionnaire'),
4545
'fullname' => get_string('fullname'),
46-
'username' => get_string('username')
46+
'username' => get_string('username'),
47+
'useridentityfields' => get_string('showuseridentity', 'admin')
4748
);
4849

4950
$settings->add(new admin_setting_configmultiselect('questionnaire/downloadoptions',

tests/csvexport_test.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ private function get_csv_text(array $rows) {
5151
return $lines;
5252
}
5353

54+
/**
55+
* Tests the CSV export.
56+
*/
5457
public function test_csvexport() {
5558
$this->resetAfterTest();
5659
$dg = $this->getDataGenerator();
@@ -79,6 +82,107 @@ public function test_csvexport() {
7982
}
8083
}
8184

85+
/**
86+
* Tests the CSV export with identity fields and anonymous questionnaires.
87+
*/
88+
public function test_csvexport_identity_fields() {
89+
global $DB;
90+
$this->resetAfterTest();
91+
92+
$config = get_config('questionnaire', 'downloadoptions');
93+
if (strpos($config, 'useridentityfields') === false) {
94+
set_config('downloadoptions', "{$config},useridentityfields", 'questionnaire');
95+
}
96+
97+
$dg = $this->getDataGenerator();
98+
$qdg = $dg->get_plugin_generator('mod_questionnaire');
99+
$profilefields = ['specialid' => 'Special id', 'staffno' => 'Staff number'];
100+
$qdg->create_and_fully_populate(1, 2, 1, 1, $profilefields);
101+
102+
$user = $dg->create_user();
103+
$this->setUser($user);
104+
$roleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
105+
106+
$questionnaires = $qdg->questionnaires();
107+
foreach ($questionnaires as $item) {
108+
list($course, $cm) = get_course_and_cm_from_instance($item->id, 'questionnaire', $item->course);
109+
110+
$this->do_test_csvexport_identity_fields($course, $cm, $user, $roleid, $profilefields, $item, false);
111+
$this->do_test_csvexport_identity_fields($course, $cm, $user, $roleid, $profilefields, $item, true);
112+
}
113+
}
114+
115+
/**
116+
* Tests the CSV export with identity fields for a questionnaire.
117+
*
118+
* @param object $course
119+
* @param object $cm
120+
* @param object $user
121+
* @param int $roleid
122+
* @param array $profilefields
123+
* @param object $item
124+
* @param bool $anonymous
125+
* @throws coding_exception
126+
* @throws dml_exception
127+
* @throws moodle_exception
128+
*/
129+
private function do_test_csvexport_identity_fields($course, $cm, $user, $roleid, $profilefields, $item, $anonymous): void {
130+
global $DB;
131+
132+
if ($anonymous) {
133+
// Make questionnaire anonymous.
134+
$row = new \stdClass();
135+
$row->id = $item->id;
136+
$row->respondenttype = 'anonymous';
137+
$DB->update_record('questionnaire', $row);
138+
}
139+
140+
$context = \context_course::instance($course->id);
141+
role_assign($roleid, $user->id, $context);
142+
assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $roleid, $context);
143+
144+
// Generate CSV output.
145+
$questionnaire = new questionnaire($course, $cm, $item->id);
146+
$output = $questionnaire->generate_csv(0, '', '', 0, 0, 1);
147+
148+
$this->assertNotNull($output);
149+
$this->assertCount(3, $output);
150+
151+
// Check profile field columns.
152+
$errortext = $anonymous ? 'exists' : 'missing';
153+
$columns = $output[0];
154+
$columns1 = [];
155+
foreach ($profilefields as $field => $name) {
156+
$col = array_search($name, $columns);
157+
$this->assertEquals(!$anonymous, $col, "Profile field {$field} {$errortext}");
158+
if (!$anonymous) {
159+
$columns1[] = $col;
160+
}
161+
}
162+
163+
// Check profile field values.
164+
for ($i = 1; $i < count($output); $i++) {
165+
$columns2 = [];
166+
foreach ($profilefields as $field => $name) {
167+
$values = $output[$i];
168+
$id = $field . ($i - 1);
169+
$col = array_search($id, $values);
170+
$this->assertEquals(!$anonymous, $col, "Profile field {$field} {$errortext}");
171+
if (!$anonymous) {
172+
$columns2[] = $col;
173+
}
174+
}
175+
176+
if (!$anonymous) {
177+
// Check indexes of columns and values.
178+
$this->assertEquals(count($columns1), count($columns2), "Indexes of columns and values");
179+
for ($j = 0; $j < count($columns1); $j++) {
180+
$this->assertEquals($columns1[$j], $columns2[$j], "Indexes of columns and values");
181+
}
182+
}
183+
}
184+
}
185+
82186
/**
83187
* Return the expected output.
84188
* @return string[]

tests/generator/lib.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,9 +678,10 @@ public function generate_response($questionnaire, $questions, $userid, $complete
678678
* @param int $studentcount
679679
* @param int $questionnairecount
680680
* @param int $questionspertype
681+
* @param array $profilefields in format ['<shortname>' => '<name>']
681682
*/
682683
public function create_and_fully_populate($coursecount = 4, $studentcount = 20, $questionnairecount = 2,
683-
$questionspertype = 5) {
684+
$questionspertype = 5, $profilefields = []) {
684685
global $DB;
685686

686687
$dg = $this->datagenerator;
@@ -692,8 +693,24 @@ public function create_and_fully_populate($coursecount = 4, $studentcount = 20,
692693
$courses = [];
693694
$questionnaires = [];
694695

696+
if (!empty($profilefields)) {
697+
// Create profile fields and set them to show for user identity.
698+
$fields = [];
699+
foreach ($profilefields as $field => $name) {
700+
$dg->create_custom_profile_field(['datatype' => 'text',
701+
'shortname' => $field, 'name' => $name]);
702+
$fields[] = "profile_field_{$field}";
703+
}
704+
set_config('showuseridentity', implode(',', $fields));
705+
}
706+
695707
for ($u = 0; $u < $studentcount; $u++) {
696-
$students[] = $dg->create_user(['firstname' => 'Testy']);
708+
$user = ['firstname' => 'Testy'];
709+
// Set values for the profile fields.
710+
foreach ($profilefields as $field => $name) {
711+
$user["profile_field_{$field}"] = "{$field}{$u}";
712+
}
713+
$students[] = $dg->create_user($user);
697714
}
698715

699716
$manplugin = enrol_get_plugin('manual');

0 commit comments

Comments
 (0)