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
/**
18
 * Upgrade library code for the randomsamatch question type.
19
 *
20
 * @package   qtype_randomsamatch
21
 * @copyright 2013 Jean-Michel Vedrine
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
 
29
/**
30
 * Class for converting attempt data for randomsamatch questions when upgrading
31
 * attempts to the new question engine.
32
 *
33
 * This class is used by the code in question/engine/upgrade/upgradelib.php.
34
 *
35
 * @copyright 2013 Jean-Michel Vedrine
36
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class qtype_randomsamatch_qe2_attempt_updater extends question_qtype_attempt_updater {
39
    /** @var array of question stems. */
40
    protected $stems;
41
    /** @var array of question stems format. */
42
    protected $stemformat;
43
    /** @var array of choices that can be matched to each stem. */
44
    protected $choices;
45
    /** @var array index of the right choice for each stem. */
46
    protected $right;
47
    /** @var array id of the right answer for each stem (used by {@link lookup_choice}). */
48
    protected $rightanswerid;
49
    /** @var array shuffled stem indexes. */
50
    protected $stemorder;
51
    /** @var array shuffled choice indexes. */
52
    protected $choiceorder;
53
    /** @var array flipped version of the choiceorder array. */
54
    protected $flippedchoiceorder;
55
    /** @var array of right answer for each stem. */
56
    protected $rightanswer;
57
 
58
    public function question_summary() {
59
        return ''; // Done later, after we know which shortanswer questions are used.
60
    }
61
 
62
    public function right_answer() {
63
        return ''; // Done later, after we know which shortanswer questions are used.
64
    }
65
 
66
    /**
67
     * Explode the answer saved as a string in state
68
     *
69
     * @param string $answer comma separated list of dash separated pairs
70
     * @return array
71
     */
72
    protected function explode_answer($answer) {
73
        if (!$answer) {
74
            return array();
75
        }
76
        $bits = explode(',', $answer);
77
        $selections = array();
78
        foreach ($bits as $bit) {
79
            list($stem, $choice) = explode('-', $bit);
80
            $selections[$stem] = $choice;
81
        }
82
        return $selections;
83
    }
84
 
85
    protected function make_summary($pairs) {
86
        $bits = array();
87
        foreach ($pairs as $stem => $answer) {
88
            $bits[] = $stem . ' -> ' . $answer;
89
        }
90
        return implode('; ', $bits);
91
    }
92
 
93
    /**
94
     * Find the index corresponding to a choice
95
     *
96
     * @param integer $choice
97
     * @return integer
98
     */
99
    protected function lookup_choice($choice) {
100
        if (array_key_exists($choice, $this->choices)) {
101
            // Easy case: choice is a key in the choices array.
102
            return $choice;
103
        } else {
104
            // But choice can also be the id of a shortanser correct answer
105
            // without been a key of the choices array, in that case we need
106
            // to first find the shortanswer id, then find the choices index
107
            // associated to it.
108
            $questionid = array_search($choice, $this->rightanswerid);
109
            if ($questionid) {
110
                return $this->right[$questionid];
111
            }
112
        }
113
        return null;
114
    }
115
 
116
    public function response_summary($state) {
117
        $choices = $this->explode_answer($state->answer);
118
        if (empty($choices)) {
119
            return null;
120
        }
121
 
122
        $pairs = array();
123
        foreach ($choices as $stemid => $choicekey) {
124
            if (array_key_exists($stemid, $this->stems) && $choices[$stemid]) {
125
                $choiceid = $this->lookup_choice($choicekey);
126
                if ($choiceid) {
127
                    $pairs[$this->stems[$stemid]] = $this->choices[$choiceid];
128
                } else {
129
                    $this->logger->log_assumption("Dealing with a place where the
130
                            student selected a choice that was later deleted for
131
                            randomsamatch question {$this->question->id}");
132
                    $pairs[$this->stems[$stemid]] = '[CHOICE THAT WAS LATER DELETED]';
133
                }
134
            }
135
        }
136
 
137
        if ($pairs) {
138
            return $this->make_summary($pairs);
139
        } else {
140
            return '';
141
        }
142
    }
143
 
144
    public function was_answered($state) {
145
        $choices = $this->explode_answer($state->answer);
146
        foreach ($choices as $choice) {
147
            if ($choice) {
148
                return true;
149
            }
150
        }
151
        return false;
152
    }
153
 
154
    public function set_first_step_data_elements($state, &$data) {
155
        $this->stems = array();
156
        $this->stemformat = array();
157
        $this->choices = array();
158
        $this->right = array();
159
        $this->rightanswer = array();
160
        $choices = $this->explode_answer($state->answer);
161
        $this->stemorder = array();
162
        foreach ($choices as $key => $notused) {
163
            $this->stemorder[] = $key;
164
        }
165
        $wrappedquestions = array();
166
        // TODO test what happen when some questions are missing.
167
        foreach ($this->stemorder as $questionid) {
168
            $wrappedquestions[] = $this->load_question($questionid);
169
        }
170
        foreach ($wrappedquestions as $wrappedquestion) {
171
 
172
            // We only take into account the first correct answer.
173
            $foundcorrect = false;
174
            foreach ($wrappedquestion->options->answers as $answer) {
175
                if ($foundcorrect || $answer->fraction != 1.0) {
176
                    unset($wrappedquestion->options->answers[$answer->id]);
177
                } else if (!$foundcorrect) {
178
                    $foundcorrect = true;
179
                    // Store right answer id, so we can use it later in lookup_choice.
180
                    $this->rightanswerid[$wrappedquestion->id] = $answer->id;
181
                    $key = array_search($answer->answer, $this->choices);
182
                    if ($key === false) {
183
                        $key = $answer->id;
184
                        $this->choices[$key] = $answer->answer;
185
                        $data['_choice_' . $key] = $answer->answer;
186
                    }
187
                    $this->stems[$wrappedquestion->id] = $wrappedquestion->questiontext;
188
                    $this->stemformat[$wrappedquestion->id] = $wrappedquestion->questiontextformat;
189
                    $this->right[$wrappedquestion->id] = $key;
190
                    $this->rightanswer[$wrappedquestion->id] = $answer->answer;
191
 
192
                    $data['_stem_' . $wrappedquestion->id] = $wrappedquestion->questiontext;
193
                    $data['_stemformat_' . $wrappedquestion->id] = $wrappedquestion->questiontextformat;
194
                    $data['_right_' . $wrappedquestion->id] = $key;
195
 
196
                }
197
            }
198
        }
199
        $this->choiceorder = array_keys($this->choices);
200
        // We don't shuffle the choices as that seems unnecessary for old upgraded attempts.
201
        $this->flippedchoiceorder = array_combine(
202
                array_values($this->choiceorder), array_keys($this->choiceorder));
203
 
204
        $data['_stemorder'] = implode(',', $this->stemorder);
205
        $data['_choiceorder'] = implode(',', $this->choiceorder);
206
 
207
        $this->updater->qa->questionsummary = $this->to_text($this->question->questiontext) . ' {' .
208
                implode('; ', $this->stems) . '} -> {' . implode('; ', $this->choices) . '}';
209
 
210
        $answer = array();
211
        foreach ($this->stems as $key => $stem) {
212
            $answer[$stem] = $this->choices[$this->right[$key]];
213
        }
214
        $this->updater->qa->rightanswer = $this->make_summary($answer);
215
    }
216
 
217
    public function supply_missing_first_step_data(&$data) {
218
        throw new coding_exception('qtype_randomsamatch_updater::supply_missing_first_step_data ' .
219
                'not tested');
220
        $data['_stemorder'] = array();
221
        $data['_choiceorder'] = array();
222
    }
223
 
224
    public function set_data_elements_for_step($state, &$data) {
225
        $choices = $this->explode_answer($state->answer);
226
 
227
        foreach ($this->stemorder as $i => $key) {
228
            if (empty($choices[$key])) {
229
                $data['sub' . $i] = 0;
230
                continue;
231
            }
232
            $choice = $this->lookup_choice($choices[$key]);
233
 
234
            if (array_key_exists($choice, $this->flippedchoiceorder)) {
235
                $data['sub' . $i] = $this->flippedchoiceorder[$choice] + 1;
236
            } else {
237
                $data['sub' . $i] = 0;
238
            }
239
        }
240
    }
241
 
242
    public function load_question($questionid) {
243
        return $this->qeupdater->load_question($questionid);
244
    }
245
}