Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
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.
8
//
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.
13
//
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/>.
16
 
17
defined('MOODLE_INTERNAL') OR die('not allowed');
18
require_once($CFG->dirroot.'/mod/feedback/item/feedback_item_class.php');
19
 
20
define('FEEDBACK_MULTICHOICE_TYPE_SEP', '>>>>>');
21
define('FEEDBACK_MULTICHOICE_LINE_SEP', '|');
22
define('FEEDBACK_MULTICHOICE_ADJUST_SEP', '<<<<<');
23
define('FEEDBACK_MULTICHOICE_IGNOREEMPTY', 'i');
24
define('FEEDBACK_MULTICHOICE_HIDENOSELECT', 'h');
25
 
26
class feedback_item_multichoice extends feedback_item_base {
27
    protected $type = "multichoice";
28
 
29
    public function build_editform($item, $feedback, $cm) {
30
        global $DB, $CFG;
31
        require_once('multichoice_form.php');
32
 
33
        //get the lastposition number of the feedback_items
34
        $position = $item->position;
35
        $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
36
        if ($position == -1) {
37
            $i_formselect_last = $lastposition + 1;
38
            $i_formselect_value = $lastposition + 1;
39
            $item->position = $lastposition + 1;
40
        } else {
41
            $i_formselect_last = $lastposition;
42
            $i_formselect_value = $item->position;
43
        }
44
        //the elements for position dropdownlist
45
        $positionlist = array_slice(range(0, $i_formselect_last), 1, $i_formselect_last, true);
46
 
47
        $item->presentation = empty($item->presentation) ? '' : $item->presentation;
48
        $info = $this->get_info($item);
49
 
50
        $item->ignoreempty = $this->ignoreempty($item);
51
        $item->hidenoselect = $this->hidenoselect($item);
52
 
53
        //all items for dependitem
54
        $feedbackitems = feedback_get_depend_candidates_for_item($feedback, $item);
55
        $commonparams = array('cmid'=>$cm->id,
56
                             'id'=>isset($item->id) ? $item->id : null,
57
                             'typ'=>$item->typ,
58
                             'items'=>$feedbackitems,
59
                             'feedback'=>$feedback->id);
60
 
61
        //build the form
62
        $customdata = array('item' => $item,
63
                            'common' => $commonparams,
64
                            'positionlist' => $positionlist,
65
                            'position' => $position,
66
                            'info' => $info);
67
 
68
        $this->item_form = new feedback_multichoice_form('edit_item.php', $customdata);
69
    }
70
 
71
    public function save_item() {
72
        global $DB;
73
 
74
        if (!$this->get_data()) {
75
            return false;
76
        }
77
        $item = $this->item;
78
 
79
        if (isset($item->clone_item) AND $item->clone_item) {
80
            $item->id = ''; //to clone this item
81
            $item->position++;
82
        }
83
 
84
        $this->set_ignoreempty($item, $item->ignoreempty);
85
        $this->set_hidenoselect($item, $item->hidenoselect);
86
 
87
        $item->hasvalue = $this->get_hasvalue();
88
        if (!$item->id) {
89
            $item->id = $DB->insert_record('feedback_item', $item);
90
        } else {
91
            $DB->update_record('feedback_item', $item);
92
        }
93
 
94
        return $DB->get_record('feedback_item', array('id'=>$item->id));
95
    }
96
 
97
 
98
    //gets an array with three values(typ, name, XXX)
99
    //XXX is an object with answertext, answercount and quotient
100
 
101
    /**
102
     * Helper function for collected data, both for analysis page and export to excel
103
     *
104
     * @param stdClass $item the db-object from feedback_item
105
     * @param int $groupid
106
     * @param int $courseid
107
     * @return array|null
108
     */
109
    protected function get_analysed($item, $groupid = false, $courseid = false) {
110
        $info = $this->get_info($item);
111
 
112
        $analysed_item = array();
113
        $analysed_item[] = $item->typ;
114
        $analysed_item[] = format_string($item->name);
115
 
116
        //get the possible answers
117
        $answers = null;
118
        $answers = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
119
        if (!is_array($answers)) {
120
            return null;
121
        }
122
 
123
        //get the values
124
        $values = feedback_get_group_values($item, $groupid, $courseid, $this->ignoreempty($item));
125
        if (!$values) {
126
            return null;
127
        }
128
 
129
        //get answertext, answercount and quotient for each answer
130
        $analysed_answer = array();
131
        if ($info->subtype == 'c') {
132
            $sizeofanswers = count($answers);
133
            for ($i = 1; $i <= $sizeofanswers; $i++) {
134
                $ans = new stdClass();
135
                $ans->answertext = $answers[$i-1];
136
                $ans->answercount = 0;
137
                foreach ($values as $value) {
138
                    //ist die Antwort gleich dem index der Antworten + 1?
139
                    $vallist = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $value->value);
140
                    foreach ($vallist as $val) {
141
                        if ($val == $i) {
142
                            $ans->answercount++;
143
                        }
144
                    }
145
                }
146
                $ans->quotient = $ans->answercount / count($values);
147
                $analysed_answer[] = $ans;
148
            }
149
        } else {
150
            $sizeofanswers = count($answers);
151
            for ($i = 1; $i <= $sizeofanswers; $i++) {
152
                $ans = new stdClass();
153
                $ans->answertext = $answers[$i-1];
154
                $ans->answercount = 0;
155
                foreach ($values as $value) {
156
                    //ist die Antwort gleich dem index der Antworten + 1?
157
                    if ($value->value == $i) {
158
                        $ans->answercount++;
159
                    }
160
                }
161
                $ans->quotient = $ans->answercount / count($values);
162
                $analysed_answer[] = $ans;
163
            }
164
        }
165
        $analysed_item[] = $analysed_answer;
166
        return $analysed_item;
167
    }
168
 
169
    public function get_printval($item, $value) {
170
        $info = $this->get_info($item);
171
 
172
        $printval = '';
173
 
174
        if (!isset($value->value)) {
175
            return $printval;
176
        }
177
 
178
        $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
179
 
180
        if ($info->subtype == 'c') {
181
            $vallist = array_values(explode (FEEDBACK_MULTICHOICE_LINE_SEP, $value->value));
182
            $sizeofvallist = count($vallist);
183
            $sizeofpresentation = count($presentation);
184
            for ($i = 0; $i < $sizeofvallist; $i++) {
185
                for ($k = 0; $k < $sizeofpresentation; $k++) {
186
                    if ($vallist[$i] == ($k + 1)) {//Die Werte beginnen bei 1, das Array aber mit 0
187
                        $printval .= trim(format_string($presentation[$k])) . chr(10);
188
                        break;
189
                    }
190
                }
191
            }
192
        } else {
193
            $index = 1;
194
            foreach ($presentation as $pres) {
195
                if ($value->value == $index) {
196
                    $printval = format_string($pres);
197
                    break;
198
                }
199
                $index++;
200
            }
201
        }
202
        return $printval;
203
    }
204
 
205
    public function print_analysed($item, $itemnr = '', $groupid = false, $courseid = false) {
206
        global $OUTPUT;
207
 
208
        $analysed_item = $this->get_analysed($item, $groupid, $courseid);
209
        if ($analysed_item) {
210
            $itemname = $analysed_item[1];
211
            echo "<table class=\"analysis itemtype_{$item->typ}\">";
212
            echo '<tr><th class="text-left">';
213
            echo $itemnr . ' ';
214
            if (strval($item->label) !== '') {
215
                echo '('. format_string($item->label).') ';
216
            }
217
            echo format_string($itemname);
218
            echo '</th></tr>';
219
 
220
            $analysed_vals = $analysed_item[2];
221
            $count = 0;
222
            $data = [];
223
            foreach ($analysed_vals as $val) {
224
                $quotient = format_float($val->quotient * 100, 2);
225
                $strquotient = '';
226
                if ($val->quotient > 0) {
227
                    $strquotient = ' ('. $quotient . ' %)';
228
                }
229
                $answertext = format_text(trim($val->answertext), FORMAT_HTML,
230
                        array('noclean' => true, 'para' => false));
231
 
232
                $data['labels'][$count] = $answertext;
233
                $data['series'][$count] = $val->answercount;
234
                $data['series_labels'][$count] = $val->answercount . $strquotient;
235
                $count++;
236
            }
237
            $chart = new \core\chart_bar();
238
            $chart->set_horizontal(true);
239
            $series = new \core\chart_series(format_string(get_string("responses", "feedback")), $data['series']);
240
            $series->set_labels($data['series_labels']);
241
            $chart->add_series($series);
242
            $chart->set_labels($data['labels']);
243
 
244
            echo '<tr><td>'. $OUTPUT->render($chart) . '</td></tr>';
245
            echo "</table>";
246
        }
247
    }
248
 
249
    public function excelprint_item(&$worksheet, $row_offset,
250
                             $xls_formats, $item,
251
                             $groupid, $courseid = false) {
252
 
253
        $analysed_item = $this->get_analysed($item, $groupid, $courseid);
254
        if (!$analysed_item) {
255
            return $row_offset;
256
        }
257
 
258
        $data = $analysed_item[2];
259
 
260
        //frage schreiben
261
        $worksheet->write_string($row_offset, 0, $item->label, $xls_formats->head2);
262
        $worksheet->write_string($row_offset, 1, $analysed_item[1], $xls_formats->head2);
263
        if (is_array($data)) {
264
            $sizeofdata = count($data);
265
            for ($i = 0; $i < $sizeofdata; $i++) {
266
                $analysed_data = $data[$i];
267
 
268
                $worksheet->write_string($row_offset,
269
                                         $i + 2,
270
                                         trim($analysed_data->answertext),
271
                                         $xls_formats->head2);
272
 
273
                $worksheet->write_number($row_offset + 1,
274
                                         $i + 2,
275
                                         $analysed_data->answercount,
276
                                         $xls_formats->default);
277
 
278
                $worksheet->write_number($row_offset + 2,
279
                                         $i + 2,
280
                                         $analysed_data->quotient,
281
                                         $xls_formats->procent);
282
            }
283
        }
284
        $row_offset += 3;
285
        return $row_offset;
286
    }
287
 
288
    /**
289
     * Options for the multichoice element
290
     * @param stdClass $item
291
     * @return array
292
     */
293
    protected function get_options($item) {
294
        $info = $this->get_info($item);
295
        $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
296
        $options = array();
297
        foreach ($presentation as $idx => $optiontext) {
298
            $options[$idx + 1] = format_text($optiontext, FORMAT_HTML, array('noclean' => true, 'para' => false));
299
        }
300
        if ($info->subtype === 'r' && !$this->hidenoselect($item)) {
301
            $options = array(0 => get_string('not_selected', 'feedback')) + $options;
302
        }
303
 
304
        return $options;
305
    }
306
 
307
    /**
308
     * Adds an input element to the complete form
309
     *
310
     * This element has many options - it can be displayed as group or radio elements,
311
     * group of checkboxes or a dropdown list.
312
     *
313
     * @param stdClass $item
314
     * @param mod_feedback_complete_form $form
315
     */
316
    public function complete_form_element($item, $form) {
317
        $info = $this->get_info($item);
318
        $name = $this->get_display_name($item);
319
        $class = 'multichoice-' . $info->subtype;
320
        $inputname = $item->typ . '_' . $item->id;
321
        $options = $this->get_options($item);
322
        $separator = !empty($info->horizontal) ? ' ' : '<br>';
323
        $tmpvalue = $form->get_item_value($item) ?? 0; // Used for element defaults, so must be a valid value (not null).
324
 
325
        // Subtypes:
326
        // r = radio
327
        // c = checkbox
328
        // d = dropdown.
329
        if ($info->subtype === 'd' || ($info->subtype === 'r' && $form->is_frozen())) {
330
            // Display as a dropdown in the complete form or a single value in the response view.
331
            $element = $form->add_form_element($item,
332
                    ['select', $inputname, $name, array(0 => '') + $options, array('class' => $class)],
333
                    false, false);
334
            $form->set_element_default($inputname, $tmpvalue);
335
            $form->set_element_type($inputname, PARAM_INT);
336
        } else if ($info->subtype === 'c' && $form->is_frozen()) {
337
            // Display list of checkbox values in the response view.
338
            $objs = [];
339
            foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $form->get_item_value($item)) as $v) {
340
                $objs[] = ['static', $inputname."[$v]", '', isset($options[$v]) ? $options[$v] : ''];
341
            }
342
            $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class);
343
        } else {
344
            // Display group or radio or checkbox elements.
345
            $class .= ' multichoice-' . ($info->horizontal ? 'horizontal' : 'vertical');
346
            $objs = [];
347
            if ($info->subtype === 'c') {
348
                // Checkboxes.
349
                $objs[] = ['hidden', $inputname.'[0]', 0];
350
                $form->set_element_type($inputname.'[0]', PARAM_INT);
351
                foreach ($options as $idx => $label) {
352
                    $objs[] = ['advcheckbox', $inputname.'['.$idx.']', '', $label, null, array(0, $idx)];
353
                    $form->set_element_type($inputname.'['.$idx.']', PARAM_INT);
354
                }
355
                // Span to hold the element id. The id is used for drag and drop reordering.
356
                $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])];
357
                $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class);
358
                if ($tmpvalue) {
359
                    foreach (explode(FEEDBACK_MULTICHOICE_LINE_SEP, $tmpvalue) as $v) {
360
                        $form->set_element_default($inputname.'['.$v.']', $v);
361
                    }
362
                }
363
            } else {
364
                // Radio.
365
                if (!array_key_exists(0, $options)) {
366
                    // Always add a hidden element to the group to guarantee we get a value in the submit data.
367
                    $objs[] = ['hidden', $inputname, 0];
368
                }
369
                foreach ($options as $idx => $label) {
370
                    $objs[] = ['radio', $inputname, '', $label, $idx];
371
                }
372
                // Span to hold the element id. The id is used for drag and drop reordering.
373
                $objs[] = ['static', '', '', html_writer::span('', '', ['id' => 'feedback_item_' . $item->id])];
374
                $element = $form->add_form_group_element($item, 'group_'.$inputname, $name, $objs, $separator, $class);
375
                $form->set_element_default($inputname, $tmpvalue);
376
                $form->set_element_type($inputname, PARAM_INT);
377
            }
378
        }
379
 
380
        // Process 'required' rule.
381
        if ($item->required) {
382
            $elementname = $element->getName();
383
            $form->add_validation_rule(function($values) use ($elementname, $item) {
384
                $inputname = $item->typ . '_' . $item->id;
385
                return empty($values[$inputname]) || (is_array($values[$inputname]) && !array_filter($values[$inputname])) ?
386
                    array($elementname => get_string('required')) : true;
387
            });
388
        }
389
    }
390
 
391
    /**
392
     * Prepares value that user put in the form for storing in DB
393
     * @param array $value
394
     * @return string
395
     */
396
    public function create_value($value) {
397
        // Could be an array (multichoice checkbox) or single value (multichoice radio or dropdown).
398
        $value = is_array($value) ? $value : [$value];
399
 
400
        $value = array_unique(array_filter($value));
401
        return join(FEEDBACK_MULTICHOICE_LINE_SEP, $value);
402
    }
403
 
404
    /**
405
     * Compares the dbvalue with the dependvalue
406
     *
407
     * @param stdClass $item
408
     * @param string $dbvalue is the value input by user in the format as it is stored in the db
409
     * @param string $dependvalue is the value that it needs to be compared against
410
     */
411
    public function compare_value($item, $dbvalue, $dependvalue) {
412
 
413
        if (is_array($dbvalue)) {
414
            $dbvalues = $dbvalue;
415
        } else {
416
            $dbvalues = explode(FEEDBACK_MULTICHOICE_LINE_SEP, $dbvalue);
417
        }
418
 
419
        $info = $this->get_info($item);
420
        $presentation = explode (FEEDBACK_MULTICHOICE_LINE_SEP, $info->presentation);
421
        $index = 1;
422
        foreach ($presentation as $pres) {
423
            foreach ($dbvalues as $dbval) {
424
                if ($dbval == $index AND trim($pres) == $dependvalue) {
425
                    return true;
426
                }
427
            }
428
            $index++;
429
        }
430
        return false;
431
    }
432
 
433
    public function get_info($item) {
434
        $presentation = empty($item->presentation) ? '' : $item->presentation;
435
 
436
        $info = new stdClass();
437
        //check the subtype of the multichoice
438
        //it can be check(c), radio(r) or dropdown(d)
439
        $info->subtype = '';
440
        $info->presentation = '';
441
        $info->horizontal = false;
442
 
443
        $parts = explode(FEEDBACK_MULTICHOICE_TYPE_SEP, $item->presentation);
444
        $info->subtype = $parts[0];
445
        if (count($parts) > 1) {
446
            $info->presentation = $parts[1];
447
        }
448
        if (!isset($info->subtype)) {
449
            $info->subtype = 'r';
450
        }
451
 
452
        if ($info->subtype != 'd') {
453
            $parts = explode(FEEDBACK_MULTICHOICE_ADJUST_SEP, $info->presentation);
454
            $info->presentation = $parts[0];
455
            if (count($parts) > 1) {
456
                $info->horizontal = $parts[1];
457
            }
458
            if (isset($info->horizontal) AND $info->horizontal == 1) {
459
                $info->horizontal = true;
460
            } else {
461
                $info->horizontal = false;
462
            }
463
        }
464
        return $info;
465
    }
466
 
467
    public function set_ignoreempty($item, $ignoreempty=true) {
468
        $item->options = str_replace(FEEDBACK_MULTICHOICE_IGNOREEMPTY, '', $item->options);
469
        if ($ignoreempty) {
470
            $item->options .= FEEDBACK_MULTICHOICE_IGNOREEMPTY;
471
        }
472
    }
473
 
474
    public function ignoreempty($item) {
475
        if (strstr($item->options, FEEDBACK_MULTICHOICE_IGNOREEMPTY)) {
476
            return true;
477
        }
478
        return false;
479
    }
480
 
481
    public function set_hidenoselect($item, $hidenoselect=true) {
482
        $item->options = str_replace(FEEDBACK_MULTICHOICE_HIDENOSELECT, '', $item->options);
483
        if ($hidenoselect) {
484
            $item->options .= FEEDBACK_MULTICHOICE_HIDENOSELECT;
485
        }
486
    }
487
 
488
    public function hidenoselect($item) {
489
        if (strstr($item->options, FEEDBACK_MULTICHOICE_HIDENOSELECT)) {
490
            return true;
491
        }
492
        return false;
493
    }
494
 
495
    /**
496
     * Return the analysis data ready for external functions.
497
     *
498
     * @param stdClass $item     the item (question) information
499
     * @param int      $groupid  the group id to filter data (optional)
500
     * @param int      $courseid the course id (optional)
501
     * @return array an array of data with non scalar types json encoded
502
     * @since  Moodle 3.3
503
     */
504
    public function get_analysed_for_external($item, $groupid = false, $courseid = false) {
505
 
506
        $externaldata = array();
507
        $data = $this->get_analysed($item, $groupid, $courseid);
508
 
509
        if ($data && !empty($data[2]) && is_array($data[2])) {
510
            foreach ($data[2] as $d) {
511
                $externaldata[] = json_encode($d);
512
            }
513
        }
514
        return $externaldata;
515
    }
516
}