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
namespace mod_questionnaire\responsetype;
18
 
19
use coding_exception;
20
use dml_exception;
21
use mod_questionnaire\db\bulk_sql_config;
22
use stdClass;
23
 
24
/**
25
 * Class for boolean response types.
26
 *
27
 * @author Mike Churchward
28
 * @copyright 2016 onward Mike Churchward (mike.churchward@poetopensource.org)
29
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
30
 * @package mod_questionnaire
31
 */
32
class boolean extends responsetype {
33
 
34
    /**
35
     * Provide the necessary response data table name. Should probably always be used with late static binding 'static::' form
36
     * rather than 'self::' form to allow for class extending.
37
     *
38
     * @return string response table name.
39
     */
40
    public static function response_table() {
41
        return 'questionnaire_response_bool';
42
    }
43
 
44
    /**
45
     * Provide an array of answer objects from web form data for the question.
46
     *
47
     * @param \stdClass $responsedata All of the responsedata as an object.
48
     * @param \mod_questionnaire\question\question $question
49
     * @return array \mod_questionnaire\responsetype\answer\answer An array of answer objects.
50
     */
51
    public static function answers_from_webform($responsedata, $question) {
52
        $answers = [];
53
        if (isset($responsedata->{'q'.$question->id}) && !empty($responsedata->{'q'.$question->id})) {
54
            $record = new \stdClass();
55
            $record->responseid = $responsedata->rid;
56
            $record->questionid = $question->id;
57
            $record->choiceid = $responsedata->{'q' . $question->id};
58
            $record->value = $responsedata->{'q' . $question->id};
59
            $answers[] = answer\answer::create_from_data($record);
60
        }
61
        return $answers;
62
    }
63
 
64
    /**
65
     * Provide an array of answer objects from mobile data for the question.
66
     *
67
     * @param \stdClass $responsedata All of the responsedata as an object.
68
     * @param \mod_questionnaire\question\question $question
69
     * @return array \mod_questionnaire\responsetype\answer\answer An array of answer objects.
70
     */
71
    public static function answers_from_appdata($responsedata, $question) {
72
        if (isset($responsedata->{'q'.$question->id}) && !empty($responsedata->{'q'.$question->id})) {
73
            $responsedata->{'q'.$question->id} = ($responsedata->{'q'.$question->id}[0] == 1) ? 'y' : 'n';
74
        }
75
        return static::answers_from_webform($responsedata, $question);
76
    }
77
 
78
    /**
79
     * Insert a provided response to the question.
80
     *
81
     * @param object $responsedata All of the responsedata as an object.
82
     * @return int|bool - on error the subtype should call set_error and return false.
83
     */
84
    public function insert_response($responsedata) {
85
        global $DB;
86
 
87
        if (!$responsedata instanceof \mod_questionnaire\responsetype\response\response) {
88
            $response = \mod_questionnaire\responsetype\response\response::response_from_webform($responsedata, [$this->question]);
89
        } else {
90
            $response = $responsedata;
91
        }
92
 
93
        if (!empty($response) && isset($response->answers[$this->question->id][0])) {
94
            $record = new \stdClass();
95
            $record->response_id = $response->id;
96
            $record->question_id = $this->question->id;
97
            $record->choice_id = $response->answers[$this->question->id][0]->choiceid;
98
            return $DB->insert_record(static::response_table(), $record);
99
        } else {
100
            return false;
101
        }
102
    }
103
 
104
    /**
105
     * Provide the result information for the specified result records.
106
     *
107
     * @param int|array $rids - A single response id, or array.
108
     * @param boolean $anonymous - Whether or not responses are anonymous.
109
     * @return array - Array of data records.
110
     */
111
    public function get_results($rids=false, $anonymous=false) {
112
        global $DB;
113
 
114
        $rsql = '';
115
        $params = array($this->question->id);
116
        if (!empty($rids)) {
117
            list($rsql, $rparams) = $DB->get_in_or_equal($rids);
118
            $params = array_merge($params, $rparams);
119
            $rsql = ' AND response_id ' . $rsql;
120
        }
121
        $params[] = '';
122
 
123
        $sql = 'SELECT choice_id, COUNT(response_id) AS num ' .
124
               'FROM {'.static::response_table().'} ' .
125
               'WHERE question_id= ? ' . $rsql . ' AND choice_id != ? ' .
126
               'GROUP BY choice_id';
127
        return $DB->get_records_sql($sql, $params);
128
    }
129
 
130
    /**
131
     * If the choice id needs to be transformed into a different value, override this in the child class.
132
     * @param int $choiceid
133
     * @return string
134
     */
135
    public function transform_choiceid($choiceid) {
136
        if ($choiceid == 0) {
137
            $choice = 'y';
138
        } else {
139
            $choice = 'n';
140
        }
141
        return $choice;
142
    }
143
 
144
    /**
145
     * Provide the feedback scores for all requested response id's. This should be provided only by questions that provide feedback.
146
     * @param array $rids
147
     * @return array | boolean
148
     */
149
    public function get_feedback_scores(array $rids) {
150
        global $DB;
151
 
152
        $rsql = '';
153
        $params = [$this->question->id];
154
        if (!empty($rids)) {
155
            list($rsql, $rparams) = $DB->get_in_or_equal($rids);
156
            $params = array_merge($params, $rparams);
157
            $rsql = ' AND response_id ' . $rsql;
158
        }
159
        $params[] = 'y';
160
 
161
        $feedbackscores = false;
162
        $sql = 'SELECT response_id, choice_id ' .
163
            'FROM {'.$this->response_table().'} ' .
164
            'WHERE question_id= ? ' . $rsql . ' ' .
165
            'ORDER BY response_id ASC';
166
        if ($responses = $DB->get_recordset_sql($sql, $params)) {
167
            $feedbackscores = [];
168
            foreach ($responses as $rid => $response) {
169
                $feedbackscores[$rid] = new stdClass();
170
                $feedbackscores[$rid]->rid = $rid;
171
                $feedbackscores[$rid]->score = ($response->choice_id == 'y') ? 1 : 0;
172
            }
173
        }
174
        return $feedbackscores;
175
    }
176
 
177
    /**
178
     * Provide a template for results screen if defined.
179
     * @param bool $pdf
180
     * @return mixed The template string or false/
181
     */
182
    public function results_template($pdf = false) {
183
        if ($pdf) {
184
            return 'mod_questionnaire/resultspdf_choice';
185
        } else {
186
            return 'mod_questionnaire/results_choice';
187
        }
188
    }
189
 
190
    /**
191
     * Return the JSON structure required for the template.
192
     *
193
     * @param bool $rids
194
     * @param string $sort
195
     * @param bool $anonymous
196
     * @return string
197
     */
198
    public function display_results($rids=false, $sort='', $anonymous=false) {
199
        $stryes = get_string('yes');
200
        $strno = get_string('no');
201
 
202
        if (is_array($rids)) {
203
            $prtotal = 1;
204
        } else if (is_int($rids)) {
205
            $prtotal = 0;
206
        }
207
        $numresps = count($rids);
208
 
209
        $counts = [$stryes => 0, $strno => 0];
210
        $numrespondents = 0;
211
        if ($rows = $this->get_results($rids, $anonymous)) {
212
            foreach ($rows as $row) {
213
                $choice = $row->choice_id;
214
                $count = $row->num;
215
                if ($choice == 'y') {
216
                    $choice = $stryes;
217
                } else {
218
                    $choice = $strno;
219
                }
220
                $counts[$choice] = intval($count);
221
                $numrespondents += $counts[$choice];
222
            }
223
            $pagetags = $this->get_results_tags($counts, $numresps, $numrespondents, $prtotal, '');
224
        } else {
225
            $pagetags = new stdClass();
226
        }
227
        return $pagetags;
228
    }
229
 
230
    /**
231
     * Return an array of answers by question/choice for the given response. Must be implemented by the subclass.
232
     *
233
     * @param int $rid The response id.
234
     * @return array
235
     */
236
    public static function response_select($rid) {
237
        global $DB;
238
 
239
        $values = [];
240
        $sql = 'SELECT q.id, q.content, a.choice_id '.
241
            'FROM {'.static::response_table().'} a, {questionnaire_question} q '.
242
            'WHERE a.response_id= ? AND a.question_id=q.id ';
243
        $records = $DB->get_records_sql($sql, [$rid]);
244
        foreach ($records as $qid => $row) {
245
            $choice = $row->choice_id;
246
            unset ($row->id);
247
            unset ($row->choice_id);
248
            $row = (array)$row;
249
            $newrow = [];
250
            foreach ($row as $key => $val) {
251
                if (!is_numeric($key)) {
252
                    $newrow[] = $val;
253
                }
254
            }
255
            $values[$qid] = $newrow;
256
            array_push($values[$qid], ($choice == 'y') ? '1' : '0');
257
            array_push($values[$qid], $choice); // DEV still needed for responses display.
258
        }
259
 
260
        return $values;
261
    }
262
 
263
    /**
264
     * Return an array of answer objects by question for the given response id.
265
     * THIS SHOULD REPLACE response_select.
266
     *
267
     * @param int $rid The response id.
268
     * @return array array answer
269
     * @throws dml_exception
270
     */
271
    public static function response_answers_by_question($rid) {
272
        global $DB;
273
 
274
        $answers = [];
275
        $sql = 'SELECT id, response_id as responseid, question_id as questionid, choice_id as choiceid, choice_id as value ' .
276
            'FROM {' . static::response_table() .'} ' .
277
            'WHERE response_id = ? ';
278
        $records = $DB->get_records_sql($sql, [$rid]);
279
        foreach ($records as $record) {
280
            $record->choiceid = ($record->choiceid == 'y') ? 1 : 0;
281
            $answers[$record->questionid][] = answer\answer::create_from_data($record);
282
        }
283
 
284
        return $answers;
285
    }
286
 
287
    /**
288
     * Configure bulk sql
289
     * @return bulk_sql_config
290
     */
291
    protected function bulk_sql_config() {
292
        return new bulk_sql_config(static::response_table(), 'qrb', true, false, false);
293
    }
294
 
295
    /**
296
     * Return sql for getting responses in bulk.
297
     * @author Guy Thomas
298
     * @author Mike Churchward
299
     * @return string
300
     */
301
    protected function bulk_sql() {
302
        global $DB;
303
 
304
        $userfields = $this->user_fields_sql();
305
        // Postgres requires all fields to be the same type. Boolean type returns a character value as "choice_id",
306
        // while all others are an integer. So put the boolean response in "response" field instead (CONTRIB-6436).
307
        // NOTE - the actual use of "boolean" should probably change to not use "choice_id" at all, or use it as
308
        // numeric zero and one instead.
309
        $alias = 'qrb';
310
        $extraselect = '0 AS choice_id, ' . $DB->sql_order_by_text('qrb.choice_id', 1000) . ' AS response, 0 AS rankvalue';
311
 
312
        return "
313
            SELECT " . $DB->sql_concat_join("'_'", ['qr.id', "'".$this->question->helpname()."'", $alias.'.id']) . " AS id,
314
                   qr.submitted, qr.complete, qr.grade, qr.userid, $userfields, qr.id AS rid, $alias.question_id,
315
                   $extraselect
316
              FROM {questionnaire_response} qr
317
              JOIN {".static::response_table()."} $alias ON $alias.response_id = qr.id
318
        ";
319
    }
320
}
321