@@ -132,6 +132,14 @@ public function insert_response($responsedata) {
132132 $ record ->choice_id = $ answer ->choiceid ;
133133 $ record ->rankvalue = $ answer ->value ;
134134 $ resid = $ DB ->insert_record (static ::response_table (), $ record );
135+ if (isset ($ responsedata ->{$ answer ->choiceid . '_qother ' })) {
136+ $ otherrecord = new \stdClass ();
137+ $ otherrecord ->response_id = $ response ->id ;
138+ $ otherrecord ->question_id = $ this ->question ->id ;
139+ $ otherrecord ->choice_id = $ answer ->choiceid ;
140+ $ otherrecord ->response = $ responsedata ->{$ answer ->choiceid . '_qother ' };
141+ $ DB ->insert_record ('questionnaire_response_other ' , $ otherrecord );
142+ }
135143 }
136144 }
137145 return $ resid ;
@@ -148,18 +156,29 @@ public function get_results($rids=false, $anonymous=false) {
148156 global $ DB ;
149157
150158 $ rsql = '' ;
159+ $ params = [];
151160 if (!empty ($ rids )) {
152161 list ($ rsql , $ params ) = $ DB ->get_in_or_equal ($ rids );
153162 $ rsql = ' AND response_id ' . $ rsql ;
154163 }
164+ // Get other choices.
165+ $ otherrecs = $ this ->get_other_choice ($ rsql , $ params );
155166
156- $ select = 'question_id= ' . $ this ->question ->id . ' AND content NOT LIKE \' !other% \' ORDER BY id ASC ' ;
167+ $ select = 'question_id= ' . $ this ->question ->id . ' ORDER BY id ASC ' ;
157168 if ($ rows = $ DB ->get_records_select ('questionnaire_quest_choice ' , $ select )) {
158169 foreach ($ rows as $ row ) {
159- $ this ->counts [$ row ->content ] = new \stdClass ();
160170 $ nbna = $ DB ->count_records (static ::response_table (), array ('question_id ' => $ this ->question ->id ,
161- 'choice_id ' => $ row ->id , 'rankvalue ' => '-1 ' ));
162- $ this ->counts [$ row ->content ]->nbna = $ nbna ;
171+ 'choice_id ' => $ row ->id , 'rankvalue ' => '-1 ' ));
172+ if (\mod_questionnaire \question \choice::content_is_other_choice ($ row ->content ) && !empty ($ otherrecs )) {
173+ foreach (array_keys ($ otherrecs ) as $ key ) {
174+ $ this ->counts [$ key ] = new \stdClass ();
175+ $ this ->counts [$ key ]->nbna = $ nbna ;
176+ $ this ->counts [$ key ]->content = $ row ->content ;
177+ }
178+ } else {
179+ $ this ->counts [$ row ->content ] = new \stdClass ();
180+ $ this ->counts [$ row ->content ]->nbna = $ nbna ;
181+ }
163182 }
164183 }
165184
@@ -193,14 +212,30 @@ public function get_results($rids=false, $anonymous=false) {
193212 }
194213
195214 $ sql = "SELECT c.id, c.content, a.average, a.num
196- FROM {questionnaire_quest_choice} c
197- INNER JOIN
215+ FROM {questionnaire_quest_choice} c
216+ INNER JOIN
198217 (SELECT c2.id, AVG(a2.rankvalue) AS average, COUNT(a2.response_id) AS num
199- FROM {questionnaire_quest_choice} c2, { " .static ::response_table ()."} a2
200- WHERE c2.question_id = ? AND a2.question_id = ? AND a2.choice_id = c2.id AND a2.rankvalue >= 0 {$ rsql }
201- GROUP BY c2.id) a ON a.id = c.id
202- order by c.id " ;
218+ FROM {questionnaire_quest_choice} c2, { " .static ::response_table ()."} a2
219+ WHERE c2.question_id = ? AND a2.question_id = ? AND a2.choice_id = c2.id
220+ AND a2.rankvalue >= 0 AND c2.content NOT LIKE '!other%' {$ rsql }
221+ GROUP BY c2.id) a ON a.id = c.id
222+ ORDER BY c.id " ;
203223 $ results = $ DB ->get_records_sql ($ sql , array_merge (array ($ this ->question ->id , $ this ->question ->id ), $ params ));
224+
225+ // Handle 'other...'.
226+ if ($ otherrecs ) {
227+ $ i = 1 ;
228+ foreach ($ otherrecs as $ rec ) {
229+ $ results ['other ' .$ i ] = new \stdClass ();
230+ $ results ['other ' .$ i ]->id = $ rec ->cid ;
231+ $ results ['other ' .$ i ]->content = $ rec ->response ;
232+ $ results ['other ' .$ i ]->average = $ rec ->average ;
233+ $ results ['other ' .$ i ]->num = $ rec ->num ;
234+ $ results ['other ' .$ i ]->isother = true ;
235+ $ i ++;
236+ }
237+ }
238+
204239 if (!empty ($ rankvalue )) {
205240 foreach ($ results as $ key => $ result ) {
206241 if (isset ($ value [$ key ])) {
@@ -217,24 +252,68 @@ public function get_results($rids=false, $anonymous=false) {
217252 // Case where scaleitems is less than possible choices.
218253 } else {
219254 $ sql = "SELECT c.id, c.content, a.sum, a.num
220- FROM {questionnaire_quest_choice} c
221- INNER JOIN
255+ FROM {questionnaire_quest_choice} c
256+ INNER JOIN
222257 (SELECT c2.id, SUM(a2.rankvalue) AS sum, COUNT(a2.response_id) AS num
223- FROM {questionnaire_quest_choice} c2, { " .static ::response_table ()."} a2
224- WHERE c2.question_id = ? AND a2.question_id = ? AND a2.choice_id = c2.id AND a2.rankvalue >= 0 {$ rsql }
225- GROUP BY c2.id) a ON a.id = c.id " ;
258+ FROM {questionnaire_quest_choice} c2, { " .static ::response_table ()."} a2
259+ WHERE c2.question_id = ? AND a2.question_id = ? AND a2.choice_id = c2.id
260+ AND a2.rankvalue >= 0 AND c2.content NOT LIKE '!other%' {$ rsql }
261+ GROUP BY c2.id) a ON a.id = c.id " ;
262+
226263 $ results = $ DB ->get_records_sql ($ sql , array_merge (array ($ this ->question ->id , $ this ->question ->id ), $ params ));
264+
265+ if ($ otherrecs ) {
266+ $ i = 1 ;
267+ foreach ($ otherrecs as $ rec ) {
268+ $ results ['other ' .$ i ] = new \stdClass ();
269+ $ results ['other ' .$ i ]->id = $ rec ->cid ;
270+ $ results ['other ' .$ i ]->content = $ rec ->response ;
271+ $ results ['other ' .$ i ]->sum = $ rec ->average ;
272+ $ results ['other ' .$ i ]->num = $ rec ->num ;
273+ $ results ['other ' .$ i ]->isother = true ;
274+ $ i ++;
275+ }
276+ }
227277 // Formula to calculate the best ranking order.
228278 $ nbresponses = count ($ rids );
229279 foreach ($ results as $ key => $ result ) {
230- $ result ->average = ($ result ->sum + ($ nbresponses - $ result ->num ) * ($ this ->length + 1 )) / $ nbresponses ;
280+ if (isset ($ this ->length )) {
281+ $ result ->average = ($ result ->sum + ($ nbresponses - $ result ->num ) * ($ this ->length + 1 )) / $ nbresponses ;
282+ } else {
283+ $ result ->average = ($ result ->sum + ($ nbresponses - $ result ->num ) * 1 ) / $ nbresponses ;
284+ }
231285 $ results [$ result ->content ] = $ result ;
232286 unset($ results [$ key ]);
233287 }
234288 return $ results ;
235289 }
236290 }
237291
292+ /**
293+ * Get a list of other available choices.
294+ *
295+ * @param string $rsql
296+ * @param array $params
297+ * @return array
298+ */
299+ public function get_other_choice (string $ rsql , array $ params ): array {
300+ global $ DB ;
301+ $ osql = "SELECT ro.response, AVG(a.rankvalue) AS average, COUNT(a.response_id) AS num, c.id as cid
302+ FROM {questionnaire_quest_choice} c
303+ INNER JOIN { " . static ::response_table () . "} a ON a.choice_id = c.id
304+ AND a.rankvalue >= 0
305+ AND a.question_id = c.question_id {$ rsql }
306+ INNER JOIN {questionnaire_response_other} ro ON ro.choice_id = c.id
307+ AND ro.response_id = a.response_id
308+ AND ro.question_id = c.question_id
309+ WHERE c.question_id = ?
310+ AND c.content = '!other'
311+ AND ro.response <> ''
312+ GROUP BY ro.response, cid
313+ ORDER BY cid " ;
314+ return $ DB ->get_records_sql ($ osql , array_merge ($ params , [$ this ->question ->id ]));
315+ }
316+
238317 /**
239318 * Provide the feedback scores for all requested response id's. This should be provided only by questions that provide feedback.
240319 * @param array $rids
@@ -417,9 +496,17 @@ public static function response_answers_by_question($rid) {
417496 global $ DB ;
418497
419498 $ answers = [];
420- $ sql = 'SELECT id, response_id as responseid, question_id as questionid, choice_id as choiceid, rankvalue as value ' .
421- 'FROM { ' . static ::response_table () .'} ' .
422- 'WHERE response_id = ? ' ;
499+ $ sql = 'SELECT r.id,
500+ r.response_id AS responseid,
501+ r.question_id AS questionid,
502+ r.choice_id AS choiceid,
503+ r.rankvalue AS value,
504+ rt.response AS otheresponse
505+ FROM { ' . static ::response_table () . '} r
506+ LEFT JOIN {questionnaire_response_other} rt ON rt.choice_id = r.choice_id
507+ AND r.question_id = rt.question_id
508+ AND r.response_id = rt.response_id
509+ WHERE r.response_id = ? ' ;
423510 $ records = $ DB ->get_records_sql ($ sql , [$ rid ]);
424511 foreach ($ records as $ record ) {
425512 $ answers [$ record ->questionid ][$ record ->choiceid ] = answer \answer::create_from_data ($ record );
@@ -637,6 +724,11 @@ private function mkresavg($sort, $stravgvalue='') {
637724 $ content = $ contents ->text ;
638725 }
639726 }
727+ if (isset ($ contentobj ->content ) &&
728+ \mod_questionnaire \question \choice::content_other_choice_display ($ contentobj ->content )) {
729+ $ othertext = \mod_questionnaire \question \choice::content_other_choice_display ($ contentobj ->content );
730+ $ content = $ othertext . ' ' . clean_text ($ content );
731+ }
640732 if ($ osgood ) {
641733 $ choicecol1 = new \stdClass ();
642734 $ choicecol1 ->width = $ header1 ->width ;
@@ -761,15 +853,21 @@ private function mkrescount($rids, $rows, $sort) {
761853 $ rsql = ' AND response_id ' . $ rsql ;
762854 }
763855
764- array_unshift ($ params , $ this ->question ->id ); // This is question_id.
765- $ sql = 'SELECT r.id, c.content, r.rankvalue, c.id AS choiceid ' .
766- 'FROM {questionnaire_quest_choice} c , ' .
767- '{questionnaire_response_rank} r ' .
768- 'WHERE c.question_id = ? ' .
769- ' AND r.question_id = c.question_id ' .
770- ' AND r.choice_id = c.id ' .
771- $ rsql .
772- ' ORDER BY choiceid, rankvalue ASC ' ;
856+ // This is question_id.
857+ array_push ($ params , $ this ->question ->id );
858+ $ sql = "SELECT r.id,
859+ CASE
860+ WHEN c.content = '!other' THEN o.response
861+ ELSE c.content
862+ END as content, r.rankvalue, c.id AS choiceid
863+ FROM {questionnaire_quest_choice} c
864+ INNER JOIN { " . static ::response_table () . "} r ON r.question_id = c.question_id
865+ AND r.choice_id = c.id {$ rsql }
866+ LEFT JOIN {questionnaire_response_other} o ON o.choice_id = c.id
867+ AND o.response_id = r.response_id
868+ AND o.question_id = c.question_id
869+ WHERE c.question_id = ? AND (c.content != '!other' OR (o.response IS NOT NULL AND o.response <> ''))
870+ ORDER BY choiceid, rankvalue ASC " ;
773871 $ choices = $ DB ->get_records_sql ($ sql , $ params );
774872
775873 // Sort rows (results) by average value.
@@ -799,10 +897,10 @@ private function mkrescount($rids, $rows, $sort) {
799897 if (!empty ($ this ->question ->nameddegrees )) {
800898 $ rankvalue = array_flip (array_keys ($ this ->question ->nameddegrees ));
801899 }
802- foreach ($ rows as $ row ) {
900+ foreach ($ rows as $ key => $ row ) {
803901 $ choiceid = $ row ->id ;
804902 foreach ($ choices as $ choice ) {
805- if ($ choice ->choiceid == $ choiceid ) {
903+ if ($ choice ->choiceid == $ choiceid && $ choice -> content === $ key ) {
806904 $ n = 0 ;
807905 for ($ i = 1 ; $ i <= $ nbranks ; $ i ++) {
808906 if ((isset ($ rankvalue [$ choice ->rankvalue ]) && ($ rankvalue [$ choice ->rankvalue ] == ($ i - 1 ))) ||
@@ -893,17 +991,23 @@ private function mkrescount($rids, $rows, $sort) {
893991 // Ensure there are two bits of content.
894992 list ($ content , $ contentright ) = array_merge (preg_split ('/[|]/ ' , $ content ), array (' ' ));
895993 $ header = reset ($ pagetags ->totals ->headers );
994+ if (isset ($ rows [$ content ]) && isset ($ rows [$ content ]->isother ) && $ rows [$ content ]->isother ) {
995+ $ content = get_string ('other ' , 'questionnaire ' ) . ' ' . $ content ;
996+ }
896997 $ totalcols [] = (object )['align ' => $ header ->align ,
897- 'text ' => format_text ($ content , FORMAT_HTML , ['noclean ' => true ])];
998+ 'text ' => format_text ($ content , FORMAT_HTML , ['noclean ' => true , ' filter ' => false ])];
898999 } else {
8991000 // Eliminate potentially short-named choices.
9001001 $ contents = questionnaire_choice_values ($ content );
9011002 if ($ contents ->modname ) {
9021003 $ content = $ contents ->text ;
9031004 }
1005+ if (isset ($ rows [$ content ]) && isset ($ rows [$ content ]->isother ) && $ rows [$ content ]->isother ) {
1006+ $ content = get_string ('other ' , 'questionnaire ' ) . ' ' . $ content ;
1007+ }
9041008 $ header = reset ($ pagetags ->totals ->headers );
9051009 $ totalcols [] = (object )['align ' => $ header ->align ,
906- 'text ' => format_text ($ content , FORMAT_HTML , ['noclean ' => true ])];
1010+ 'text ' => format_text ($ content , FORMAT_HTML , ['noclean ' => true , ' filter ' => false ])];
9071011 }
9081012 // Display ranks/rates numbers.
9091013 $ maxrank = max ($ rank );
0 commit comments