Skip to content

Commit

Permalink
Questionnaire: amend the 'Scale' question so that if you have 'Other'…
Browse files Browse the repository at this point in the history
…, there's a text field underneath
  • Loading branch information
toanlam committed Sep 10, 2024
1 parent f2ffa46 commit dc3fc97
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 16 deletions.
24 changes: 22 additions & 2 deletions classes/question/rate.php
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,22 @@ protected function question_survey_display($response, $descendantsdata, $blankqu
if ($this->osgood_rate_scale()) {
list($content, $contentright) = array_merge(preg_split('/[|]/', $content), array(' '));
}
$cols[] = ['colstyle' => 'text-align: '.$textalign.';',
'coltext' => format_text($content, FORMAT_HTML, ['noclean' => true]).' '];
if ($choice->is_other_choice()) {
$othertext = $choice->other_choice_display();
$oname = $cid . '_qother';
$oid = $cid . '-other';
$odata = isset($response->answers[$this->id][$cid]) ? $response->answers[$this->id][$cid]->value : '';
if (isset($odata)) {
$ovalue = stripslashes($odata);
}
$content = $othertext;
$cols[] = ['oname' => $oname, 'oid' => $oid, 'ovalue' => $ovalue,
'colstyle' => 'text-align: ' . $textalign . ';',
'coltext' => format_text($content, FORMAT_HTML, ['noclean' => true]) . ' '];
} else {
$cols[] = ['colstyle' => 'text-align: '.$textalign.';',
'coltext' => format_text($content, FORMAT_HTML, ['noclean' => true]) . ' '];
}

$bg = 'c0 raterow';
$hasnotansweredchoice = false;
Expand Down Expand Up @@ -512,6 +526,12 @@ protected function response_survey_display($response) {
if ($this->osgood_rate_scale()) {
list($content, $contentright) = array_merge(preg_split('/[|]/', $content), array(' '));
}
if ($choice->is_other_choice()) {
$content = $choice->other_choice_display();
if (isset($response->answers[$this->id][$cid]->otheresponse)) {
$rowobj->othercontent = $response->answers[$this->id][$cid]->otheresponse;
}
}
$rowobj->content = format_text($content, FORMAT_HTML, ['noclean' => true]).' ';
$bg = 'c0';
$cols = [];
Expand Down
9 changes: 7 additions & 2 deletions classes/responsetype/answer/answer.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,25 @@ class answer {
/** @var string $value The value of this response (if applicable). */
public $value;

/** @var string $value The other value of this response (if applicable). */
public $otheresponse;

/**
* Answer constructor.
* @param null $id
* @param null $responseid
* @param null $questionid
* @param null $choiceid
* @param null $otheresponse
* @param null $value
*/
public function __construct($id = null, $responseid = null, $questionid = null, $choiceid = null, $value = null) {
public function __construct($id = null, $responseid = null, $questionid = null, $choiceid = null, $value = null, $otheresponse = null) {
$this->id = $id;
$this->responseid = $responseid;
$this->questionid = $questionid;
$this->choiceid = $choiceid;
$this->value = $value;
$this->otheresponse = $otheresponse;
}

/**
Expand All @@ -78,6 +83,6 @@ public static function create_from_data($answerdata) {
}

return new answer($answerdata['id'], $answerdata['responseid'], $answerdata['questionid'], $answerdata['choiceid'],
$answerdata['value']);
$answerdata['value'], $answerdata['otheresponse']);
}
}
104 changes: 93 additions & 11 deletions classes/responsetype/rank.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ public function insert_response($responsedata) {
$record->choice_id = $answer->choiceid;
$record->rankvalue = $answer->value;
$resid = $DB->insert_record(static::response_table(), $record);
if (isset($responsedata->{$answer->choiceid . '_qother'})) {
$otherrecord = new \stdClass();
$otherrecord->response_id = $response->id;
$otherrecord->question_id = $this->question->id;
$otherrecord->choice_id = $answer->choiceid;
$otherrecord->response = $responsedata->{$answer->choiceid . '_qother'};
$DB->insert_record('questionnaire_response_other', $otherrecord);
}
}
}
return $resid;
Expand All @@ -153,7 +161,7 @@ public function get_results($rids=false, $anonymous=false) {
$rsql = ' AND response_id ' . $rsql;
}

$select = 'question_id=' . $this->question->id . ' AND content NOT LIKE \'!other%\' ORDER BY id ASC';
$select = 'question_id=' . $this->question->id . ' ORDER BY id ASC';
if ($rows = $DB->get_records_select('questionnaire_quest_choice', $select)) {
foreach ($rows as $row) {
$this->counts[$row->content] = new \stdClass();
Expand Down Expand Up @@ -201,16 +209,25 @@ public function get_results($rids=false, $anonymous=false) {
GROUP BY c2.id) a ON a.id = c.id
order by c.id";
$results = $DB->get_records_sql($sql, array_merge(array($this->question->id, $this->question->id), $params));
if (!empty ($rankvalue)) {
foreach ($results as $key => $result) {
if (isset($value[$key])) {
$result->averagevalue = $value[$key] / $result->num;
if (!empty($results)) {
$choiceids = array_keys($results);
$otherresultcontent = self::get_other_choice($choiceids);
if (!empty ($rankvalue)) {
foreach ($results as $key => $result) {
if (isset($value[$key])) {
$result->averagevalue = $value[$key] / $result->num;
}
}
}
}
// Reindex by 'content'. Can't do this from the query as it won't work with MS-SQL.
foreach ($results as $key => $result) {
$results[$result->content] = $result;
if ($result->id) {
if (isset($otherresultcontent[$result->id])) {
$results[$result->content]->content = $otherresultcontent[$result->id];
}
}
unset($results[$key]);
}
return $results;
Expand All @@ -224,17 +241,63 @@ public function get_results($rids=false, $anonymous=false) {
WHERE c2.question_id = ? AND a2.question_id = ? AND a2.choice_id = c2.id AND a2.rankvalue >= 0{$rsql}
GROUP BY c2.id) a ON a.id = c.id";
$results = $DB->get_records_sql($sql, array_merge(array($this->question->id, $this->question->id), $params));
if (!empty($results)) {
$choiceids = array_keys($results);
$otherresultcontent = self::get_other_choice($choiceids);
}

// Formula to calculate the best ranking order.
$nbresponses = count($rids);
foreach ($results as $key => $result) {
$result->average = ($result->sum + ($nbresponses - $result->num) * ($this->length + 1)) / $nbresponses;
if (isset($this->length)) {
$result->average = ($result->sum + ($nbresponses - $result->num) * ($this->length + 1)) / $nbresponses;
} else {
$result->average = ($result->sum + ($nbresponses - $result->num) * 1 ) / $nbresponses;
}
$results[$result->content] = $result;
if (isset($otherresultcontent[$result->id])) {
$results[$result->content]->content = $otherresultcontent[$result->id];
}
unset($results[$key]);
}
return $results;
}
}

/**
* @param $choiceids
* @return array
*/
public function get_other_choice($choiceids) {
global $DB;
list($othersql, $params) = $DB->get_in_or_equal($choiceids);
$osql = "SELECT ro.*,rr.rankvalue FROM {questionnaire_response_other} ro
INNER JOIN {".static::response_table()."} rr ON ro.choice_id = rr.choice_id
AND ro.question_id = rr.question_id AND rr.response_id = ro.response_id
WHERE ro.choice_id $othersql
";
$otheresults = $DB->get_records_sql($osql, $params);
$otherresultcontent = [];
if (!empty($otheresults)) {
foreach ($otheresults as $key => $oresult) {
if (array_key_last($otheresults) == $key) {
if (isset($otherresultcontent[$oresult->choice_id])) {
$otherresultcontent[$oresult->choice_id] .= $oresult->response . '(' . $oresult->rankvalue . ')';
} else {
$otherresultcontent[$oresult->choice_id] = $oresult->response . '(' . $oresult->rankvalue . ')';
}
} else {
if (isset($otherresultcontent[$oresult->choice_id])) {
$otherresultcontent[$oresult->choice_id] .= $oresult->response . '(' . $oresult->rankvalue . '), ';
} else {
$otherresultcontent[$oresult->choice_id] = $oresult->response . '(' . $oresult->rankvalue . '), ';
}
}
}
}
return $otherresultcontent;
}

/**
* Provide the feedback scores for all requested response id's. This should be provided only by questions that provide feedback.
* @param array $rids
Expand Down Expand Up @@ -417,9 +480,17 @@ public static function response_answers_by_question($rid) {
global $DB;

$answers = [];
$sql = 'SELECT id, response_id as responseid, question_id as questionid, choice_id as choiceid, rankvalue as value ' .
'FROM {' . static::response_table() .'} ' .
'WHERE response_id = ? ';
$sql = 'SELECT r.id,
r.response_id AS responseid,
r.question_id AS questionid,
r.choice_id AS choiceid,
r.rankvalue AS value,
rt.response AS otheresponse
FROM {' . static::response_table() . '} r
LEFT JOIN {questionnaire_response_other} rt ON rt.choice_id = r.choice_id
AND r.question_id = rt.question_id
AND r.response_id = rt.response_id
WHERE r.response_id = ?';
$records = $DB->get_records_sql($sql, [$rid]);
foreach ($records as $record) {
$answers[$record->questionid][$record->choiceid] = answer\answer::create_from_data($record);
Expand Down Expand Up @@ -637,6 +708,9 @@ private function mkresavg($sort, $stravgvalue='') {
$content = $contents->text;
}
}
if (\mod_questionnaire\question\choice::content_other_choice_display($content)) {
$content = \mod_questionnaire\question\choice::content_other_choice_display($content);
}
if ($osgood) {
$choicecol1 = new \stdClass();
$choicecol1->width = $header1->width;
Expand Down Expand Up @@ -893,17 +967,25 @@ private function mkrescount($rids, $rows, $sort) {
// Ensure there are two bits of content.
list($content, $contentright) = array_merge(preg_split('/[|]/', $content), array(' '));
$header = reset($pagetags->totals->headers);
$responsetxt = \mod_questionnaire\question\choice::content_other_choice_display($content);
if (isset($rows[$content]) && $responsetxt) {
$content = $responsetxt . $rows[$content]->content;
}
$totalcols[] = (object)['align' => $header->align,
'text' => format_text($content, FORMAT_HTML, ['noclean' => true])];
'text' => format_text($content, FORMAT_HTML, ['noclean' => true, 'filter' => false])];
} else {
// Eliminate potentially short-named choices.
$contents = questionnaire_choice_values($content);
if ($contents->modname) {
$content = $contents->text;
}
$responsetxt = \mod_questionnaire\question\choice::content_other_choice_display($content);
if (isset($rows[$content]) && $responsetxt) {
$content = $responsetxt . $rows[$content]->content;
}
$header = reset($pagetags->totals->headers);
$totalcols[] = (object)['align' => $header->align,
'text' => format_text($content, FORMAT_HTML, ['noclean' => true])];
'text' => format_text($content, FORMAT_HTML, ['noclean' => true, 'filter' => false])];
}
// Display ranks/rates numbers.
$maxrank = max($rank);
Expand Down
1 change: 1 addition & 0 deletions templates/question_rate.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
<td {{#colstyle}}style="{{.}}"{{/colstyle}}{{#colclass}} class="{{.}}"{{/colclass}}{{#coltitle}} title="{{.}}"{{/coltitle}}
{{#colinput}} onclick="if('{{id}}'.length>0) document.getElementById('{{id}}').click()"{{/colinput}}>
{{#coltext}}{{{.}}}{{/coltext}}
{{#oname}}<input size="25" name="{{oname}}" id="{{oid}}" value="" type="text" />{{/oname}}
{{#colhiddentext}}<span class="accesshide">{{{.}}}</span>{{/colhiddentext}}
{{#colinput}}<input type="radio" name="{{name}}" id="{{id}}" value="{{value}}"{{#checked}} checked="checked"{{/checked}}{{#disabled}} disabled="disabled"{{/disabled}}{{#onclick}} onclick="{{.}}"{{/onclick}}>
{{#label}}<label for="{{id}}" class="accesshide">{{{.}}}</label>{{/label}}{{/colinput}}
Expand Down
2 changes: 1 addition & 1 deletion templates/response_rate.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
</tr>
{{#rows}}
<tr>
<td style="text-align:{{textalign}}">{{{content}}}</td>
<td style="text-align:{{textalign}}">{{{content}}} {{#othercontent}} <span class="response text">{{.}}</span>{{/othercontent}}</td>
{{#cols}}
{{#checked}}
<td style="text-align:center;" class="selected">
Expand Down
58 changes: 58 additions & 0 deletions tests/behat/rate_question_other.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
@mod @mod_questionnaire
Feature: Rate scale questions have Other option
In order to display an Other choice in rate question
As a teacher
the 'Other' option should display with textbox next to it in the question view

@javascript
Scenario: Create a rate question type with an 'Other' choice and verify the response exists
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | description | course | idnumber |
| questionnaire | Test questionnaire | Test questionnaire description | C1 | questionnaire0 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test questionnaire"
And I navigate to "Questions" in current page administration
And I add a "Rate (scale 1..5)" question and I fill the form with:
| Question Name | Q1 |
| Yes | y |
| Nb of scale items | 3 |
| Type of rate scale | No duplicate choices |
| Question Text | What are your top three movies? |
| Possible answers | Star Wars,Casablanca,Airplane,Citizen Kane,Anchorman,!other |
Then I should see "position 1"
And I should see "[Rate (scale 1..5)] (Q1)"
And I should see "What are your top three movies?"
And I log out

And I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Test questionnaire"
And I navigate to "Answer the questions..." in current page administration
Then I should see "Test questionnaire"
And I should see "What are your top three movies?"
And I click on "Row 2, Star Wars: Column 2, 1." "radio"
And I click on "Row 4, Airplane: Column 3, 2." "radio"
And I click on "Row 3, Casablanca: Column 4, 3." "radio"
And I click on "Row 7, !other: Column 4, 3." "radio"
And I set the field with xpath "//input[contains(@name,'qother')]" to "Once Upon a Time"
And I press "Submit questionnaire"
And I press "Continue"
Then I should see "Once Upon a Time"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test questionnaire"
And I navigate to "View all responses" in current page administration
Then I should see "Once Upon a Time(3)"

0 comments on commit dc3fc97

Please sign in to comment.