Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
2.31% |
3 / 130 |
|
5.56% |
1 / 18 |
CRAP | |
0.00% |
0 / 1 |
Interaction | |
2.31% |
3 / 130 |
|
5.56% |
1 / 18 |
3645.97 | |
0.00% |
0 / 1 |
getUsedAttributes | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getChoices | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getChoiceBySerial | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getChoiceByIdentifier | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
addChoice | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
createChoice | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
removeChoice | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIdentifiedElements | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getTemplateQtiVariables | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getResponse | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
setResponse | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getCardinality | |
0.00% |
0 / 55 |
|
0.00% |
0 / 1 |
1122 | |||
getBaseType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
toArray | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
canRenderTesttakerResponse | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
renderTesttakerResponseXHTML | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
toForm | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | /** |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program 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 this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); |
19 | * |
20 | * The main QTI Interaction Class. |
21 | * Although a QTI Interaction has not the identifier attribute, |
22 | * it is defined as an IdentifiedElement internally to allox building composite Qti Items |
23 | * |
24 | * @access public |
25 | * @author Sam, <sam@taotesting.com> |
26 | * @package taoQTI |
27 | * @see http://www.imsglobal.org/question/qti_v2p0/imsqti_infov2p0.html#element10247 |
28 | |
29 | */ |
30 | |
31 | namespace oat\taoQtiItem\model\qti\interaction; |
32 | |
33 | use oat\taoQtiItem\model\qti\Element; |
34 | use oat\taoQtiItem\model\qti\IdentifiedElementContainer; |
35 | use oat\taoQtiItem\model\qti\choice\Choice; |
36 | use oat\taoQtiItem\model\qti\IdentifierCollection; |
37 | use oat\taoQtiItem\model\qti\ResponseDeclaration; |
38 | use oat\taoQtiItem\model\qti\exception\QtiModelException; |
39 | use Exception; |
40 | |
41 | abstract class Interaction extends Element implements IdentifiedElementContainer |
42 | { |
43 | /** |
44 | * Define the class of choice associate to the interaction, to be overwritten |
45 | * by concrete class |
46 | * |
47 | * @var choiceClass |
48 | */ |
49 | protected static $choiceClass = ''; |
50 | protected static $baseType = ''; |
51 | |
52 | /** |
53 | * The response of the interaction |
54 | * |
55 | * @access protected |
56 | * @var Response |
57 | */ |
58 | protected $choices = []; |
59 | |
60 | protected function getUsedAttributes() |
61 | { |
62 | return [ |
63 | 'oat\\taoQtiItem\\model\\qti\\attribute\\ResponseIdentifier' |
64 | ]; |
65 | } |
66 | |
67 | /** |
68 | * Return the array of Qti Choice objects |
69 | * |
70 | * @param mixed $setNumber |
71 | * @return array |
72 | */ |
73 | public function getChoices($setNumber = null) |
74 | { |
75 | return $this->choices; |
76 | } |
77 | |
78 | /** |
79 | * Find a choice identified by its serial |
80 | * |
81 | * @param string $serial |
82 | * @return oat\taoQtiItem\model\qti\choice\Choice |
83 | */ |
84 | public function getChoiceBySerial($serial) |
85 | { |
86 | $returnValue = null; |
87 | $choices = $this->getChoices(); |
88 | if (isset($choices[$serial])) { |
89 | $returnValue = $choices[$serial]; |
90 | } |
91 | return $returnValue; |
92 | } |
93 | |
94 | /** |
95 | * Find a choice by its identifier |
96 | * |
97 | * @param string $identifier |
98 | * @return oat\taoQtiItem\model\qti\choice\Choice |
99 | */ |
100 | public function getChoiceByIdentifier($identifier) |
101 | { |
102 | return $this->getIdentifiedElements()->getUnique($identifier, 'oat\\taoQtiItem\\model\\qti\\choice\\Choice'); |
103 | } |
104 | |
105 | /** |
106 | * Add a choice to the interaction |
107 | * |
108 | * @param oat\taoQtiItem\model\qti\choice\Choice $choice |
109 | * @param mixed $setNumber |
110 | * @return boolean |
111 | * @throws InvalidArgumentException |
112 | */ |
113 | public function addChoice(Choice $choice, $setNumber = null) |
114 | { |
115 | $returnValue = false; |
116 | |
117 | if (!empty(static::$choiceClass) && get_class($choice) == static::$choiceClass) { |
118 | $this->choices[$choice->getSerial()] = $choice; |
119 | $relatedItem = $this->getRelatedItem(); |
120 | if (!is_null($relatedItem)) { |
121 | $choice->setRelatedItem($relatedItem); |
122 | } |
123 | $returnValue = true; |
124 | } else { |
125 | throw new InvalidArgumentException('Wrong type of choice in argument: ' . static::$choiceClass); |
126 | } |
127 | |
128 | return $returnValue; |
129 | } |
130 | |
131 | /** |
132 | * Create a choice for the interaction |
133 | * |
134 | * @param array $choiceAttributes |
135 | * @param mixed $choiceValue |
136 | * @param mixed $setNumber |
137 | * @return oat\taoQtiItem\model\qti\choice\Choice |
138 | */ |
139 | public function createChoice($choiceAttributes = [], $choiceValue = null, $setNumber = null) |
140 | { |
141 | $returnValue = null; |
142 | |
143 | if ( |
144 | !empty(static::$choiceClass) |
145 | && is_subclass_of(static::$choiceClass, 'oat\\taoQtiItem\\model\\qti\\choice\\Choice') |
146 | ) { |
147 | $returnValue = new static::$choiceClass($choiceAttributes); |
148 | $returnValue->setContent($choiceValue); |
149 | $this->addChoice($returnValue); |
150 | } |
151 | |
152 | return $returnValue; |
153 | } |
154 | |
155 | /** |
156 | * Remove a choice from the interaction |
157 | * |
158 | * @param oat\taoQtiItem\model\qti\choice\Choice $choice |
159 | * @param mixed $setNumber |
160 | */ |
161 | public function removeChoice(Choice $choice, $setNumber = null) |
162 | { |
163 | unset($this->choices[$choice->getSerial()]); |
164 | } |
165 | |
166 | /** |
167 | * Every identified QTI element must declare the list of (string) identifers being used within it |
168 | * This method gets all identified Qti Elements contained in the current Qti Element |
169 | * |
170 | * @author Sam, <sam@taotesting.com> |
171 | * @return oat\taoQtiItem\model\qti\IdentifierCollection |
172 | */ |
173 | public function getIdentifiedElements() |
174 | { |
175 | $returnValue = new IdentifierCollection(); |
176 | $returnValue->addMultiple($this->getChoices()); |
177 | |
178 | return $returnValue; |
179 | } |
180 | |
181 | protected function getTemplateQtiVariables() |
182 | { |
183 | $variables = parent::getTemplateQtiVariables(); |
184 | // remove the identifier attribute to comply with the standard, it is used interally to manage multiple |
185 | // interactions within a single item. |
186 | unset($variables['attributes']['identifier']); |
187 | $variables['choices'] = ''; |
188 | foreach ($this->getChoices() as $choice) { |
189 | $variables['choices'] .= $choice->toQTI(); |
190 | } |
191 | return $variables; |
192 | } |
193 | |
194 | /** |
195 | * Get the response declaration associated to the interaction |
196 | * If no response exists, one will be created |
197 | * |
198 | * @access public |
199 | * @author Sam, <sam@taotesting.com> |
200 | * @return oat\taoQtiItem\model\qti\ResponseDeclaration |
201 | */ |
202 | public function getResponse() |
203 | { |
204 | $returnValue = null; |
205 | |
206 | $responseAttribute = $this->getAttribute('responseIdentifier'); |
207 | if (!is_null($responseAttribute)) { |
208 | $idenfierBaseType = $responseAttribute->getValue(true); |
209 | if (!is_null($idenfierBaseType)) { |
210 | $returnValue = $idenfierBaseType->getReferencedObject(); |
211 | } else { |
212 | $responseDeclaration = new ResponseDeclaration(); |
213 | if ($this->setResponse($responseDeclaration)) { |
214 | $returnValue = $responseDeclaration; |
215 | } else { |
216 | throw new QtiModelException('cannot create the interaction response'); |
217 | } |
218 | } |
219 | } |
220 | |
221 | return $returnValue; |
222 | } |
223 | |
224 | /** |
225 | * Define the interaction's response |
226 | * |
227 | * @access public |
228 | * @author Sam, <sam@taotesting.com> |
229 | * @param oat\taoQtiItem\model\qti\ResponseDeclaration response |
230 | * @return mixed |
231 | */ |
232 | public function setResponse(ResponseDeclaration $response) |
233 | { |
234 | $relatedItem = $this->getRelatedItem(); |
235 | if (!is_null($relatedItem)) { |
236 | $relatedItem->addResponse($response); |
237 | } |
238 | return $this->setAttribute('responseIdentifier', $response); |
239 | } |
240 | |
241 | /** |
242 | * Retrieve the interaction cardinality |
243 | * (single, multiple or ordered) |
244 | * |
245 | * @access public |
246 | * @author Sam, <sam@taotesting.com> |
247 | * @param boolean numeric |
248 | * @return mixed |
249 | */ |
250 | public function getCardinality($numeric = false) |
251 | { |
252 | $returnValue = null; |
253 | |
254 | // get maximum possibility: |
255 | switch (strtolower($this->getType())) { |
256 | case 'choice': |
257 | case 'hottext': |
258 | case 'hotspot': |
259 | case 'selectpoint': |
260 | case 'positionobject': |
261 | $max = intval($this->getAttributeValue('maxChoices')); |
262 | if ($numeric) { |
263 | $returnValue = $max; |
264 | } else { |
265 | $returnValue = ($max == 1) ? 'single' : 'multiple'; // default=1 |
266 | } |
267 | break; |
268 | |
269 | case 'associate': |
270 | case 'match': |
271 | case 'graphicassociate': |
272 | $max = intval($this->getAttributeValue('maxAssociations')); |
273 | if ($numeric) { |
274 | $returnValue = $max; |
275 | } else { |
276 | $returnValue = ($max == 1) ? 'single' : 'multiple'; |
277 | } // default=1 |
278 | break; |
279 | |
280 | case 'extendedtext': |
281 | // maxStrings + order or not? |
282 | $cardinality = $this->getAttributeValue('cardinality'); |
283 | if ($cardinality == 'ordered') { |
284 | if ($numeric) { |
285 | $returnValue = 0; |
286 | } else { // meaning, infinite |
287 | $returnValue = $cardinality; |
288 | } |
289 | break; |
290 | } |
291 | $max = intval($this->getAttributeValue('maxStrings')); |
292 | if ($numeric) { |
293 | $returnValue = $max; |
294 | } else { |
295 | $returnValue = ($max > 1) ? 'multiple' : 'single'; // optional |
296 | } |
297 | break; |
298 | |
299 | case 'gapmatch': |
300 | // count the number of gap, i.e. "groups" in the interaction: |
301 | $max = count($this->getGaps()); |
302 | if ($numeric) { |
303 | $returnValue = $max; |
304 | } else { |
305 | $returnValue = ($max > 1) ? 'multiple' : 'single'; |
306 | } |
307 | break; |
308 | |
309 | case 'graphicgapmatch': |
310 | // strange that the standard always specifies "multiple": |
311 | $returnValue = 'multiple'; |
312 | break; |
313 | |
314 | case 'order': |
315 | case 'graphicorder': |
316 | $returnValue = ($numeric) ? 1 : 'ordered'; |
317 | break; |
318 | |
319 | case 'inlinechoice': |
320 | case 'textentry': |
321 | case 'media': |
322 | case 'slider': |
323 | case 'upload': |
324 | case 'endattempt': |
325 | $returnValue = ($numeric) ? 1 : 'single'; |
326 | break; |
327 | |
328 | default: |
329 | throw new QtiModelException("the current interaction type \"{$this->type}\" is not available yet"); |
330 | } |
331 | |
332 | return $returnValue; |
333 | } |
334 | |
335 | /** |
336 | * Get the interaction base type: |
337 | * integer, string, identifier, pair, directedPair float, boolean or point |
338 | * |
339 | * @access public |
340 | * @author Sam, <sam@taotesting.com> |
341 | * @return string |
342 | */ |
343 | public function getBaseType() |
344 | { |
345 | return strtolower(static::$baseType); |
346 | } |
347 | |
348 | public function toArray($filterVariableContent = false, &$filtered = []) |
349 | { |
350 | $data = parent::toArray($filterVariableContent, $filtered); |
351 | $data['choices'] = $this->getArraySerializedElementCollection( |
352 | $this->getChoices(), |
353 | $filterVariableContent, |
354 | $filtered |
355 | ); |
356 | |
357 | return $data; |
358 | } |
359 | |
360 | /** |
361 | * Short description of method canRenderTesttakerResponse |
362 | * |
363 | * @access public |
364 | * @author Sam, <sam@taotesting.com> |
365 | * @return boolean |
366 | */ |
367 | public function canRenderTesttakerResponse() |
368 | { |
369 | $returnValue = in_array(strtolower($this->type), [ |
370 | 'extendedtext' |
371 | ]); |
372 | |
373 | return (bool) $returnValue; |
374 | } |
375 | |
376 | /** |
377 | * Short description of method renderTesttakerResponseXHTML |
378 | * |
379 | * @access public |
380 | * @author Sam, <sam@taotesting.com> |
381 | * @param responses |
382 | * @return string |
383 | */ |
384 | public function renderTesttakerResponseXHTML($responses) |
385 | { |
386 | throw new QtiModelException('method to be reimplemented'); |
387 | } |
388 | |
389 | /** |
390 | * Get the short name of the interaction |
391 | * |
392 | * @return string |
393 | */ |
394 | public function getType() |
395 | { |
396 | $tagName = static::$qtiTagName; |
397 | return str_replace('Interaction', '', $tagName); |
398 | } |
399 | |
400 | public function toForm() |
401 | { |
402 | $returnValue = null; |
403 | |
404 | $interactionFormClass = '\\oat\\taoQtiItem\\controller\\QTIform\\interaction\\' |
405 | . ucfirst(strtolower($this->getType())) . 'Interaction'; |
406 | |
407 | if (!class_exists($interactionFormClass)) { |
408 | throw new Exception("the class {$interactionFormClass} does not exist"); |
409 | } else { |
410 | $formContainer = new $interactionFormClass($this); |
411 | $myForm = $formContainer->getForm(); |
412 | $returnValue = $myForm; |
413 | } |
414 | |
415 | return $returnValue; |
416 | } |
417 | } |