Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Questionnaire: amend the 'Scale' question so that if you have 'Other'… #333

Open
wants to merge 1 commit into
base: MOODLE_310_STABLE
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions classes/question/rate.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,21 @@ 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';
if (($nbchoices > 1) && !$this->no_duplicate_choices() && !$blankquestionnaire) {
Expand Down Expand Up @@ -488,6 +501,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 @@ -45,20 +45,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 @@ -80,6 +85,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']);
}
}
94 changes: 85 additions & 9 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 @@ -415,9 +478,11 @@ 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 @@ -635,6 +700,9 @@ private function mkresavg($sort, $stravgvalue='') {
$content = $contents->text;
}
}
if (\mod_questionnaire\question\choice\choice::content_other_choice_display($content)) {
$content = \mod_questionnaire\question\choice\choice::content_other_choice_display($content);
}
if ($osgood) {
$choicecol1 = new \stdClass();
$choicecol1->width = $header1->width;
Expand Down Expand Up @@ -892,6 +960,10 @@ 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\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])];
} else {
Expand All @@ -900,6 +972,10 @@ private function mkrescount($rids, $rows, $sort) {
if ($contents->modname) {
$content = $contents->text;
}
$responsetxt = \mod_questionnaire\question\choice\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])];
Expand Down
1 change: 1 addition & 0 deletions templates/question_rate.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,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 can be used to uniquely rank options
In order to create a ranking question
As a teacher
I need to enter a rate question with with "no duplicate choices" selected

@javascript
Scenario: Add a rank the top three choices question
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | [email protected] |
| student1 | Student | 1 | [email protected] |
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 "Choice 1 for row Star Wars" "radio"
And I click on "Choice 2 for row Airplane" "radio"
And I click on "Choice 3 for row Casablanca" "radio"
And I click on "Choice 3 for row Other:" "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 click on "View All Responses" "text" in the ".allresponses" "css_element"
Then I should see "Once Upon a Time(3)"