2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 defined('MOODLE_INTERNAL') OR die('not allowed');
18 require_once($CFG->dirroot
.'/mod/feedback/item/feedback_item_class.php');
20 define('FEEDBACK_RADIORATED_ADJUST_SEP', '<<<<<');
22 define('FEEDBACK_MULTICHOICERATED_MAXCOUNT', 10); //count of possible items
23 define('FEEDBACK_MULTICHOICERATED_VALUE_SEP', '####');
24 define('FEEDBACK_MULTICHOICERATED_VALUE_SEP2', '/');
25 define('FEEDBACK_MULTICHOICERATED_TYPE_SEP', '>>>>>');
26 define('FEEDBACK_MULTICHOICERATED_LINE_SEP', '|');
27 define('FEEDBACK_MULTICHOICERATED_ADJUST_SEP', '<<<<<');
28 define('FEEDBACK_MULTICHOICERATED_IGNOREEMPTY', 'i');
29 define('FEEDBACK_MULTICHOICERATED_HIDENOSELECT', 'h');
31 class feedback_item_multichoicerated
extends feedback_item_base
{
32 protected $type = "multichoicerated";
34 public function build_editform($item, $feedback, $cm) {
36 require_once('multichoicerated_form.php');
38 //get the lastposition number of the feedback_items
39 $position = $item->position
;
40 $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedback->id
));
41 if ($position == -1) {
42 $i_formselect_last = $lastposition +
1;
43 $i_formselect_value = $lastposition +
1;
44 $item->position
= $lastposition +
1;
46 $i_formselect_last = $lastposition;
47 $i_formselect_value = $item->position
;
49 //the elements for position dropdownlist
50 $positionlist = array_slice(range(0, $i_formselect_last), 1, $i_formselect_last, true);
52 $item->presentation
= empty($item->presentation
) ?
'' : $item->presentation
;
53 $info = $this->get_info($item);
55 $item->ignoreempty
= $this->ignoreempty($item);
56 $item->hidenoselect
= $this->hidenoselect($item);
58 //all items for dependitem
59 $feedbackitems = feedback_get_depend_candidates_for_item($feedback, $item);
60 $commonparams = array('cmid'=>$cm->id
,
61 'id'=>isset($item->id
) ?
$item->id
: null,
63 'items'=>$feedbackitems,
64 'feedback'=>$feedback->id
);
67 $customdata = array('item' => $item,
68 'common' => $commonparams,
69 'positionlist' => $positionlist,
70 'position' => $position,
73 $this->item_form
= new feedback_multichoicerated_form('edit_item.php', $customdata);
76 public function save_item() {
79 if (!$this->get_data()) {
84 if (isset($item->clone_item
) AND $item->clone_item
) {
85 $item->id
= ''; //to clone this item
89 $this->set_ignoreempty($item, $item->ignoreempty
);
90 $this->set_hidenoselect($item, $item->hidenoselect
);
92 $item->hasvalue
= $this->get_hasvalue();
94 $item->id
= $DB->insert_record('feedback_item', $item);
96 $DB->update_record('feedback_item', $item);
99 return $DB->get_record('feedback_item', array('id'=>$item->id
));
104 * Helper function for collected data, both for analysis page and export to excel
106 * @param stdClass $item the db-object from feedback_item
107 * @param int $groupid
108 * @param int $courseid
111 protected function get_analysed($item, $groupid = false, $courseid = false) {
112 $analysed_item = array();
113 $analysed_item[] = $item->typ
;
114 $analysed_item[] = $item->name
;
116 //die moeglichen Antworten extrahieren
117 $info = $this->get_info($item);
119 $lines = explode (FEEDBACK_MULTICHOICERATED_LINE_SEP
, $info->presentation
);
120 if (!is_array($lines)) {
125 $values = feedback_get_group_values($item, $groupid, $courseid, $this->ignoreempty($item));
129 //schleife ueber den Werten und ueber die Antwortmoeglichkeiten
131 $analysed_answer = array();
132 $sizeoflines = count($lines);
133 for ($i = 1; $i <= $sizeoflines; $i++
) {
134 $item_values = explode(FEEDBACK_MULTICHOICERATED_VALUE_SEP
, $lines[$i-1]);
135 $ans = new stdClass();
136 $ans->answertext
= $item_values[1];
139 foreach ($values as $value) {
140 //ist die Antwort gleich dem index der Antworten + 1?
141 if ($value->value
== $i) {
142 $avg +
= $item_values[0]; //erst alle Werte aufsummieren
146 $ans->answercount
= $anscount;
147 $ans->avg
= doubleval($avg) / doubleval(count($values));
148 $ans->value
= $item_values[0];
149 $ans->quotient
= $ans->answercount
/ count($values);
150 $analysed_answer[] = $ans;
152 $analysed_item[] = $analysed_answer;
153 return $analysed_item;
156 public function get_printval($item, $value) {
159 if (!isset($value->value
)) {
163 $info = $this->get_info($item);
165 $presentation = explode (FEEDBACK_MULTICHOICERATED_LINE_SEP
, $info->presentation
);
167 foreach ($presentation as $pres) {
168 if ($value->value
== $index) {
169 $item_label = explode(FEEDBACK_MULTICHOICERATED_VALUE_SEP
, $pres);
170 $printval = format_string($item_label[1]);
178 public function print_analysed($item, $itemnr = '', $groupid = false, $courseid = false) {
180 $analysed_item = $this->get_analysed($item, $groupid, $courseid);
181 if ($analysed_item) {
182 echo "<table class=\"analysis itemtype_{$item->typ}\">";
183 echo '<tr><th colspan="2" align="left">';
185 if (strval($item->label
) !== '') {
186 echo '('. format_string($item->label
).') ';
188 echo format_string($analysed_item[1]);
191 $analysed_vals = $analysed_item[2];
195 foreach ($analysed_vals as $val) {
197 $quotient = format_float($val->quotient
* 100, 2);
198 $answertext = '('.$val->value
.') ' . format_text(trim($val->answertext
), FORMAT_HTML
,
199 array('noclean' => true, 'para' => false));
201 if ($val->quotient
> 0) {
202 $strquotient = ' ('.$quotient.' %)';
207 $data['labels'][$count] = $answertext;
208 $data['series'][$count] = $val->answercount
;
209 $data['series_labels'][$count] = $val->answercount
. $strquotient;
212 $chart = new \core\
chart_bar();
213 $chart->set_horizontal(true);
214 $series = new \core\
chart_series(format_string(get_string("responses", "feedback")), $data['series']);
215 $series->set_labels($data['series_labels']);
216 $chart->add_series($series);
217 $chart->set_labels($data['labels']);
218 echo $OUTPUT->render($chart);
220 $avg = format_float($avg, 2);
221 echo '<tr><td align="left" colspan="2"><b>';
222 echo get_string('average', 'feedback').': '.$avg.'</b>';
227 public function excelprint_item(&$worksheet, $row_offset,
229 $groupid, $courseid = false) {
231 $analysed_item = $this->get_analysed($item, $groupid, $courseid);
233 $data = $analysed_item[2];
236 $worksheet->write_string($row_offset, 0, $item->label
, $xls_formats->head2
);
237 $worksheet->write_string($row_offset, 1, $analysed_item[1], $xls_formats->head2
);
238 if (is_array($data)) {
240 $sizeofdata = count($data);
241 for ($i = 0; $i < $sizeofdata; $i++
) {
242 $analysed_data = $data[$i];
244 $worksheet->write_string($row_offset,
246 trim($analysed_data->answertext
).' ('.$analysed_data->value
.')',
247 $xls_formats->value_bold
);
249 $worksheet->write_number($row_offset +
1,
251 $analysed_data->answercount
,
252 $xls_formats->default);
254 $avg +
= $analysed_data->avg
;
256 //mittelwert anzeigen
257 $worksheet->write_string($row_offset,
259 get_string('average', 'feedback'),
260 $xls_formats->value_bold
);
262 $worksheet->write_number($row_offset +
1,
265 $xls_formats->value_bold
);
272 * Options for the multichoice element
273 * @param stdClass $item
276 protected function get_options($item) {
277 $info = $this->get_info($item);
278 $lines = explode(FEEDBACK_MULTICHOICERATED_LINE_SEP
, $info->presentation
);
280 foreach ($lines as $idx => $line) {
281 list($weight, $optiontext) = explode(FEEDBACK_MULTICHOICERATED_VALUE_SEP
, $line);
283 $a->weight
= $weight;
284 $a->name
= format_text($optiontext, FORMAT_HTML
, array('noclean' => true, 'para' => false));
285 $options[$idx +
1] = get_string('multichoiceoption', 'feedback', $a);
287 if ($info->subtype
=== 'r' && !$this->hidenoselect($item)) {
288 $options = array(0 => get_string('not_selected', 'feedback')) +
$options;
295 * Adds an input element to the complete form
297 * @param stdClass $item
298 * @param mod_feedback_complete_form $form
300 public function complete_form_element($item, $form) {
301 $info = $this->get_info($item);
302 $name = $this->get_display_name($item);
303 $class = 'multichoicerated-' . $info->subtype
;
304 $inputname = $item->typ
. '_' . $item->id
;
305 $options = $this->get_options($item);
306 if ($info->subtype
=== 'd' ||
$form->is_frozen()) {
307 $el = $form->add_form_element($item,
308 ['select', $inputname, $name, array('' => '') +
$options, array('class' => $class)]);
311 if (!array_key_exists(0, $options)) {
312 // Always add '0' as hidden element, otherwise form submit data may not have this element.
313 $objs[] = ['hidden', $inputname];
315 foreach ($options as $idx => $label) {
316 $objs[] = ['radio', $inputname, '', $label, $idx];
318 // Span to hold the element id. The id is used for drag and drop reordering.
319 $objs[] = ['static', '', '', html_writer
::span('', '', ['id' => 'feedback_item_' . $item->id
])];
320 $separator = $info->horizontal ?
' ' : '<br>';
321 $class .= ' multichoicerated-' . ($info->horizontal ?
'horizontal' : 'vertical');
322 $el = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class);
323 $form->set_element_type($inputname, PARAM_INT
);
325 // Set previously input values.
326 $form->set_element_default($inputname, $form->get_item_value($item));
328 // Process "required" rule.
329 if ($item->required
) {
330 $form->add_validation_rule(function($values, $files) use ($item) {
331 $inputname = $item->typ
. '_' . $item->id
;
332 return empty($values[$inputname]) ?
array('group_' . $inputname => get_string('required')) : true;
339 * Compares the dbvalue with the dependvalue
341 * @param stdClass $item
342 * @param string $dbvalue is the value input by user in the format as it is stored in the db
343 * @param string $dependvalue is the value that it needs to be compared against
345 public function compare_value($item, $dbvalue, $dependvalue) {
347 if (is_array($dbvalue)) {
348 $dbvalues = $dbvalue;
350 $dbvalues = explode(FEEDBACK_MULTICHOICERATED_LINE_SEP
, $dbvalue);
353 $info = $this->get_info($item);
354 $presentation = explode (FEEDBACK_MULTICHOICERATED_LINE_SEP
, $info->presentation
);
356 foreach ($presentation as $pres) {
357 $presvalues = explode(FEEDBACK_MULTICHOICERATED_VALUE_SEP
, $pres);
359 foreach ($dbvalues as $dbval) {
360 if ($dbval == $index AND trim($presvalues[1]) == $dependvalue) {
369 public function get_info($item) {
370 $presentation = empty($item->presentation
) ?
'' : $item->presentation
;
372 $info = new stdClass();
373 //check the subtype of the multichoice
374 //it can be check(c), radio(r) or dropdown(d)
376 $info->presentation
= '';
377 $info->horizontal
= false;
379 $parts = explode(FEEDBACK_MULTICHOICERATED_TYPE_SEP
, $item->presentation
);
380 @list
($info->subtype
, $info->presentation
) = $parts;
382 if (!isset($info->subtype
)) {
383 $info->subtype
= 'r';
386 if ($info->subtype
!= 'd') {
387 $parts = explode(FEEDBACK_MULTICHOICERATED_ADJUST_SEP
, $info->presentation
);
388 @list
($info->presentation
, $info->horizontal
) = $parts;
390 if (isset($info->horizontal
) AND $info->horizontal
== 1) {
391 $info->horizontal
= true;
393 $info->horizontal
= false;
397 $info->values
= $this->prepare_presentation_values_print($info->presentation
,
398 FEEDBACK_MULTICHOICERATED_VALUE_SEP
,
399 FEEDBACK_MULTICHOICERATED_VALUE_SEP2
);
403 public function prepare_presentation_values($linesep1,
409 $lines = explode($linesep1, $valuestring);
411 foreach ($lines as $line) {
414 if (strpos($line, $valuesep1) === false) {
418 @list
($value, $text) = explode($valuesep1, $line, 2);
421 $value = intval($value);
422 $newlines[] = $value.$valuesep2.$text;
424 $newlines = implode($linesep2, $newlines);
428 public function prepare_presentation_values_print($valuestring, $valuesep1, $valuesep2) {
429 $valuestring = str_replace(array("\n","\r"), "", $valuestring);
430 return $this->prepare_presentation_values(FEEDBACK_MULTICHOICERATED_LINE_SEP
,
437 public function prepare_presentation_values_save($valuestring, $valuesep1, $valuesep2) {
438 $valuestring = str_replace("\r", "\n", $valuestring);
439 $valuestring = str_replace("\n\n", "\n", $valuestring);
440 return $this->prepare_presentation_values("\n",
441 FEEDBACK_MULTICHOICERATED_LINE_SEP
,
447 public function set_ignoreempty($item, $ignoreempty=true) {
448 $item->options
= str_replace(FEEDBACK_MULTICHOICERATED_IGNOREEMPTY
, '', $item->options
);
450 $item->options
.= FEEDBACK_MULTICHOICERATED_IGNOREEMPTY
;
454 public function ignoreempty($item) {
455 if (strstr($item->options
, FEEDBACK_MULTICHOICERATED_IGNOREEMPTY
)) {
461 public function set_hidenoselect($item, $hidenoselect=true) {
462 $item->options
= str_replace(FEEDBACK_MULTICHOICERATED_HIDENOSELECT
, '', $item->options
);
464 $item->options
.= FEEDBACK_MULTICHOICERATED_HIDENOSELECT
;
468 public function hidenoselect($item) {
469 if (strstr($item->options
, FEEDBACK_MULTICHOICERATED_HIDENOSELECT
)) {
476 * Return the analysis data ready for external functions.
478 * @param stdClass $item the item (question) information
479 * @param int $groupid the group id to filter data (optional)
480 * @param int $courseid the course id (optional)
481 * @return array an array of data with non scalar types json encoded
484 public function get_analysed_for_external($item, $groupid = false, $courseid = false) {
486 $externaldata = array();
487 $data = $this->get_analysed($item, $groupid, $courseid);
489 if (!empty($data[2]) && is_array($data[2])) {
490 foreach ($data[2] as $d) {
491 $externaldata[] = json_encode($d);
494 return $externaldata;