Skip to content
15 changes: 12 additions & 3 deletions classes/feedback_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,20 @@ public function definition() {

// Questionnaire Feedback Sections and Messages.
$mform->addElement('header', 'submithdr', get_string('feedbackoptions', 'questionnaire'));
// Do not display feedbacknone and feedbackglobal options if this is a questionnaire with keywords.
$questionnairehaskeywords = false;
foreach ($questionnaire->questions as $question) {
if ($question->has_keywords()) {
$questionnairehaskeywords = true;
break;
}
}
$feedbackoptions = [];
$feedbackoptions[0] = get_string('feedbacknone', 'questionnaire');
$feedbackoptions[1] = get_string('feedbackglobal', 'questionnaire');
if (!$questionnairehaskeywords) {
$feedbackoptions[0] = get_string('feedbacknone', 'questionnaire');
$feedbackoptions[1] = get_string('feedbackglobal', 'questionnaire');
}
$feedbackoptions[2] = get_string('feedbacksections', 'questionnaire');

$mform->addElement('select', 'feedbacksections', get_string('feedbackoptions', 'questionnaire'), $feedbackoptions);
$mform->setDefault('feedbacksections', $questionnaire->survey->feedbacksections);
$mform->addHelpButton('feedbacksections', 'feedbackoptions', 'questionnaire');
Expand Down
3 changes: 2 additions & 1 deletion classes/feedback_section_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public function definition() {
$feedbacksection = $this->_customdata->feedbacksection;
$validquestions = $this->_customdata->validquestions;
$survey = $this->_customdata->survey;
$canselectquestions = $this->_customdata->canselectquestions;
$feedbacksections = $questionnaire->survey->feedbacksections;
$this->_feedbacks = $feedbacksection->sectionfeedback;
$this->context = $questionnaire->context;
Expand Down Expand Up @@ -89,7 +90,7 @@ public function definition() {
$mform->setDefault('feedbacknotes', $questionnaire->survey->feedbacknotes);
$mform->addHelpButton('sectionheading', 'feedbackheading', 'questionnaire');

if ($questionnaire->survey->feedbacksections > 0) {
if ($questionnaire->survey->feedbacksections > 0 && $canselectquestions) {
// Sections.
if ($survey->feedbacksections > 1) {
$mform->addElement('header', 'fbsection_' . $feedbacksection->id,
Expand Down
39 changes: 31 additions & 8 deletions classes/question/question.php
Original file line number Diff line number Diff line change
Expand Up @@ -501,14 +501,34 @@ public function supports_feedback_scores() {
public function valid_feedback() {
if ($this->supports_feedback() && $this->has_choices() && $this->required() && !empty($this->name)) {
foreach ($this->choices as $choice) {
if ($choice->value != null) {
if ($choice->value != null && $choice->value !== '!other') {
return true;
}
}
}
return false;
}

/**
* True if the question supports feedback and has keywords instead of score value (for DISC personality test).
* Override if the default logic is not enough.
* @return bool
*/
public function has_keywords() {
if ($this->supports_feedback() && $this->has_choices() && $this->required() && !empty($this->name)) {
foreach ($this->choices as $choice) {
if ($choice->value !== null) {
// D param means no digits.
$r = preg_match_all("/(\D+)/", $choice->value, $matches);
if ($r && $matches[0][0] !== '!other') {
return true;
}
}
}
}
return false;
}

/**
* Provide the feedback scores for all requested response id's. This should be provided only by questions that provide feedback.
* @param array $rids
Expand All @@ -531,7 +551,7 @@ public function get_feedback_maxscore() {
if ($this->valid_feedback()) {
$maxscore = 0;
foreach ($this->choices as $choice) {
if (isset($choice->value) && ($choice->value != null)) {
if (isset($choice->value) && ($choice->value != null) && is_numeric($choice->value)) {
if ($choice->value > $maxscore) {
$maxscore = $choice->value;
}
Expand Down Expand Up @@ -1368,12 +1388,12 @@ public function form_update($formdata, $questionnaire) {
$choicerecord->id = $ekey;
$choicerecord->question_id = $this->qid;
$choicerecord->content = trim($newchoices[$nidx]);
$r = preg_match_all("/^(\d{1,2})(=.*)$/", $newchoices[$nidx], $matches);
// This choice has been attributed a "score value" OR this is a rate question type.
$r = preg_match_all("/^(\d{1,2}|\D.*)=(.*)$/", $newchoices[$nidx], $matches);
// This choice has been attributed a "score value" or a DISC keyword OR this is a rate question type.
if ($r) {
$newscore = $matches[1][0];
$choicerecord->value = $newscore;
} else { // No score value for this choice.
} else { // No score value for this choice.
$choicerecord->value = null;
}
$this->update_choice($choicerecord);
Expand All @@ -1389,10 +1409,13 @@ public function form_update($formdata, $questionnaire) {
$choicerecord = new \stdClass();
$choicerecord->question_id = $this->qid;
$choicerecord->content = trim($newchoices[$nidx]);
$r = preg_match_all("/^(\d{1,2})(=.*)$/", $choicerecord->content, $matches);
// This choice has been attributed a "score value" OR this is a rate question type.
$r = preg_match_all("/^(\d{1,2}|\D.*)=(.*)$/", $choicerecord->content, $matches);
// This choice has been attributed a "score value" or a DISC keyword OR this is a rate question type.
if ($r) {
$choicerecord->value = $matches[1][0];
$newscore = $matches[1][0];
$choicerecord->value = $newscore;
} else { // No score value for this choice.
$choicerecord->value = null;
}
$this->add_choice($choicerecord);
$nidx++;
Expand Down
11 changes: 11 additions & 0 deletions fbsections.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
}
}


// Add renderer and page objects to the questionnaire object for display use.
$questionnaire->add_renderer($PAGE->get_renderer('mod_questionnaire'));
$questionnaire->add_page(new \mod_questionnaire\output\feedbackpage());
Expand All @@ -111,9 +112,19 @@
}
}

// Do not display Feedback questions option if this is a DISC questionnaire.
$canselectquestions = true;
foreach ($questionnaire->questions as $question) {
if ($question->has_keywords()) {
$canselectquestions = false;
break;
}
}

$customdata = new stdClass();
$customdata->feedbacksection = $feedbacksection;
$customdata->validquestions = $validquestions;
$customdata->canselectquestions = $canselectquestions;
$customdata->survey = $questionnaire->survey;
$customdata->sectionselect = $DB->get_records_menu('questionnaire_fb_sections', ['surveyid' => $questionnaire->survey->id],
'section', 'id,sectionlabel');
Expand Down
2 changes: 1 addition & 1 deletion locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function questionnaire_choice_values($content) {
}

// Check for score value first (used e.g. by personality test feature).
$r = preg_match_all("/^(\d{1,2}=)(.*)$/", $content, $matches);
$r = preg_match_all("/^(\d{1,2}|\w*)=(.*)$/", $content, $matches);
if ($r) {
$content = $matches[2][0];
}
Expand Down
147 changes: 106 additions & 41 deletions questionnaire.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -3736,19 +3736,55 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
// Calculate max score per question in questionnaire.
$qmax = [];
$maxtotalscore = 0;
$nbquestionswithkeywords = 0;
// Calculate max score per questionnaire by adding nb of questions with keywords.
$thisquestionnairehaskeywords = false;
foreach ($this->questions as $question) {
$qid = $question->id;
if ($question->valid_feedback()) {
if ($question->has_keywords()) {
$thisquestionnairehaskeywords = true;
$maxtotalscore++;
$nbquestionswithkeywords++;
}
}
if (!$thisquestionnairehaskeywords) {
foreach ($this->questions as $question) {
$qid = $question->id;
$qmax[$qid] = $question->get_feedback_maxscore();
$maxtotalscore += $qmax[$qid];
// Get all the feedback scores for this question.
$responsescores[$qid] = $question->get_feedback_scores($rids);
}
} else {
foreach ($this->questions as $question) {
$qid = $question->id;
if ($question->has_keywords() ) {
// Get all the feedback scores (actually keywords) for this question.
$responsescores[$qid] = $question->get_feedback_scores($rids);
}
}
}
// Just in case no values have been entered in the various questions possible answers field.

/**
* Returns the number of responses containing the keyword in specified response.
* @param array $responsescores The array of response scores.
* @param string $keyword
* @param int $rid
* @return number
*/
function countscore($responsescores, $keyword, $rid) {
$count = 0;
foreach ($responsescores as $subarray) {
if (isset($subarray[$rid]) && $subarray[$rid]->score === $keyword) {
$count++;
}
}
return $count;
}

if ($maxtotalscore === 0) {
return '';
}

$feedbackmessages = [];

// Get individual scores for each question in this responses set.
Expand All @@ -3759,32 +3795,40 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
$nbparticipants = max(1, $nbparticipants - !$isgroupmember);
}
foreach ($responsescores as $qid => $responsescore) {
if (!empty($responsescore)) {
foreach ($responsescore as $rrid => $response) {
// If this is current user's response OR if current user is viewing another group's results.
if ($rrid == $rid || $allresponses) {
if (!isset($qscore[$qid])) {
$qscore[$qid] = 0;
}
$qscore[$qid] = $response->score;
foreach ($responsescore as $rrid => $response) {
// If this is current user's response OR if current user is viewing another group's results.
if ($rrid == $rid || $allresponses) {
if (!isset($qscore[$qid])) {
$qscore[$qid] = 0;
}
// Course score.
if (!isset($allqscore[$qid])) {
$allqscore[$qid] = 0;
if (!empty($responsescore)) {
if (!$thisquestionnairehaskeywords) {
$qscore[$qid] = $response->score;
}
}
// Only add current score if conditions below are met.
if ($groupmode == 0 || $isgroupmember || (!$isgroupmember && $rrid != $rid) || $allresponses) {
$allqscore[$qid] += $response->score;
}
// Course score.
if (!isset($allqscore[$qid])) {
$allqscore[$qid] = 0;
}

// Only add current score if conditions below are met.
if ($groupmode == 0 || $isgroupmember || (!$isgroupmember && $rrid != $rid) || $allresponses) {
if (!empty($responsescore)) {
if (!$thisquestionnairehaskeywords) {
$allqscore[$qid] += $response->score;
}
}
}
}
}

$totalscore = array_sum($qscore);
$scorepercent = round($totalscore / $maxtotalscore * 100);
$oppositescorepercent = 100 - $scorepercent;
$alltotalscore = array_sum($allqscore);
$allscorepercent = round($alltotalscore / $nbparticipants / $maxtotalscore * 100);

$allscorepercent = round($alltotalscore / $nbparticipants / $maxtotalscore * 100);
// No need to go further if feedback is global, i.e. only relying on total score.
if ($this->survey->feedbacksections == 1) {
$sectionid = $fbsectionsnb[0];
Expand Down Expand Up @@ -3872,7 +3916,6 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
$oppositescorepercent = array();
$alloppositescorepercent = array();
$chartlabels = array();
// Sections where all questions are unseen because of the $advdependencies.
$nanscores = array();

for ($i = 1; $i <= $numsections; $i++) {
Expand All @@ -3887,35 +3930,59 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
if (($filteredsections != null) && !in_array($section, $filteredsections)) {
continue;
}
foreach ($fbsections as $key => $fbsection) {
if ($fbsection->section == $section) {
$feedbacksectionid = $key;
$scorecalculation = section::decode_scorecalculation($fbsection->scorecalculation);
if (empty($scorecalculation) && !is_array($scorecalculation)) {
$scorecalculation = [];
if (!$thisquestionnairehaskeywords) {
foreach ($fbsections as $key => $fbsection) {
if ($fbsection->section == $section) {
$feedbacksectionid = $key;
$scorecalculation = section::decode_scorecalculation($fbsection->scorecalculation);
if (empty($scorecalculation) && !is_array($scorecalculation)) {
$scorecalculation = [];
}
$sectionheading = $fbsection->sectionheading;
$imageid = $fbsection->id;
$chartlabels[$section] = $fbsection->sectionlabel;
}
$sectionheading = $fbsection->sectionheading;
$imageid = $fbsection->id;
$chartlabels[$section] = $fbsection->sectionlabel;
}
foreach ($scorecalculation as $qid => $key) {
// Just in case a question pertaining to a section has been deleted or made not required
// after being included in scorecalculation.
if (isset($qscore[$qid])) {
$key = ($key == 0) ? 1 : $key;
$score[$section] += round($qscore[$qid] * $key);
$maxscore[$section] += round($qmax[$qid] * $key);
if ($compare || $allresponses) {
$allscore[$section] += round($allqscore[$qid] * $key);
}
}
}
} else {
foreach ($fbsections as $key => $fbsection) {
if ($fbsection->section == $section) {
$feedbacksectionid = $key;
$sectionheading = $fbsection->sectionheading;
$imageid = $fbsection->id;
$chartlabels[$section] = $fbsection->sectionlabel;
$score[$section] = countscore($responsescores, $fbsection->sectionlabel, $rid);
}
}
// Set maxscore for all sections to nb of questions with keywords.
$maxscore[$section] = $nbquestionswithkeywords;
}
foreach ($scorecalculation as $qid => $key) {
// Just in case a question pertaining to a section has been deleted or made not required
// after being included in scorecalculation.
if (isset($qscore[$qid])) {
$key = ($key == 0) ? 1 : $key;
$score[$section] += round($qscore[$qid] * $key);
$maxscore[$section] += round($qmax[$qid] * $key);
if ($compare || $allresponses) {
$allscore[$section] += round($allqscore[$qid] * $key);

if ($thisquestionnairehaskeywords && ($compare || $allresponses)) {
foreach ($rids as $key => $rid) {
foreach ($fbsections as $key => $fbsection) {
if ($fbsection->section == $section) {
$keyword = $fbsection->sectionlabel;
$allscore[$section] += countscore($responsescores, $keyword, $rid);
}
}
}
}

if ($maxscore[$section] == 0) {
array_push($nanscores, $section);
}

$scorepercent[$section] = ($maxscore[$section] > 0) ? (round($score[$section] / $maxscore[$section] * 100)) : 0;
$oppositescorepercent[$section] = 100 - $scorepercent[$section];

Expand All @@ -3924,7 +3991,6 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
$maxscore[$section] * 100)) : 0;
$alloppositescorepercent[$section] = 100 - $allscorepercent[$section];
}

if (!$allresponses) {
if (is_nan($scorepercent[$section])) {
// Info: all questions of $section are unseen
Expand Down Expand Up @@ -4021,8 +4087,7 @@ public function response_analysis($rid, $resps, $compare, $isgroupmember, $allre
$allresponses,
$this->survey->chart_type,
array_values($scorepercent),
array_values($allscorepercent),
$sectionlabel
array_values($allscorepercent)
)
);
}
Expand Down
Loading