public function testCartesianResponse() { $res = ArrayUtil::cartesianProductForResponses([[new ValidResponse(1, ['a', 'b']), new ValidResponse(2, ['c', 'd'])], [new ValidResponse(3, ['a', 'e'])]]); $this->assertCount(2, $res); $this->assertEquals(['a', 'b', 'a', 'e'], $res[0]->getValue()); $this->assertEquals(4, $res[0]->getScore()); $this->assertEquals(['c', 'd', 'a', 'e'], $res[1]->getValue()); $this->assertEquals(5, $res[1]->getScore()); }
public function testMergedTextInteraction() { $file = $this->getFixtureFileContents('interactions/textentryinteraction.xml'); list($item, $questions) = $this->mapper->parse($file); /** @var \LearnosityQti\Entities\Item\item $item */ $this->assertInstanceOf('LearnosityQti\\Entities\\Item\\item', $item); $this->assertTrue($item->get_reference() === 'res_AA-FIB_B13_CH1_geoc_f1f1'); $this->assertTrue($item->get_status() === 'published'); $this->assertTrue(count($item->get_questionReferences()) === 1); $this->assertTrue(substr_count($item->get_content(), '<span class="learnosity-response question-' . $item->get_questionReferences()[0] . '"></span>') === 1); $this->assertTrue(count($questions) === 1); $q = $questions[0]; $this->assertInstanceOf('\\LearnosityQti\\Entities\\Question', $q); /* @var $q \LearnosityQti\Entities\Question */ $this->assertTrue($q->get_type() === 'clozetext'); /* @var $questionType \LearnosityQti\Entities\QuestionTypes\clozetext */ $questionType = $q->get_data(); $this->assertInstanceOf('\\LearnosityQti\\Entities\\QuestionTypes\\clozetext', $questionType); $this->assertEmpty($questionType->get_stimulus()); $this->assertTrue($questionType->get_type() === 'clozetext'); $this->assertTrue(substr_count($questionType->get_template(), '{{response}}') === 2); /* @var $validation \LearnosityQti\Entities\QuestionTypes\clozetext_validation */ $validation = $questionType->get_validation(); $this->assertInstanceOf('\\LearnosityQti\\Entities\\QuestionTypes\\clozetext_validation', $validation); $this->assertEquals($validation->get_scoring_type(), 'exactMatch'); /* @var $validResponse \LearnosityQti\Entities\QuestionTypes\clozetext_validation_valid_response */ $validResponse = $validation->get_valid_response(); $this->assertInstanceOf('\\LearnosityQti\\Entities\\QuestionTypes\\clozetext_validation_valid_response', $validResponse); $this->assertEquals(6, $validResponse->get_score()); $options = []; $options[] = $validResponse->get_value(); $altResponses = $validation->get_alt_responses(); $this->assertTrue(count($altResponses) === 3); /* @var $altResponse \LearnosityQti\Entities\QuestionTypes\clozetext_validation_alt_responses_item */ foreach ($altResponses as $altResponse) { $this->assertInstanceOf('\\LearnosityQti\\Entities\\QuestionTypes\\clozetext_validation_alt_responses_item', $altResponse); $options[] = $altResponse->get_value(); } $this->assertEquals(5, $altResponses[0]->get_score()); $this->assertEquals(5, $altResponses[1]->get_score()); $this->assertEquals(4, $altResponses[2]->get_score()); $expectedOptions = ArrayUtil::cartesianProduct([['a', 'b'], ['OHMYGOD', 'x7']]); $matchCount = 0; foreach ($expectedOptions as $expectedKey => $expectedValue) { foreach ($options as $optionKey => $optionValue) { $diff = array_diff($expectedValue, $optionValue); if (!$diff) { $matchCount++; } } } $this->assertEquals($matchCount, count($options)); }
protected function getMapResponseTemplateValidation() { $mapEntryValueMap = []; foreach ($this->responseDeclaration->getMapping()->getMapEntries() as $mapEntry) { /** @var MapEntry $mapEntry */ $mapEntryValueMap[] = ['key' => $this->hottextComponents[$mapEntry->getMapKey()], 'score' => $mapEntry->getMappedValue()]; } $combinations = ArrayUtil::combinations($mapEntryValueMap); $correctResponses = []; foreach ($combinations as $combination) { if (count($combination) > 0 && count($combination) <= $this->maxChoices) { $score = array_sum(array_column($combination, 'score')); $value = array_column($combination, 'key'); $correctResponses[] = new ValidResponse($score, $value); } } return ValidationBuilder::build('tokenhighlight', 'exactMatch', $correctResponses); }
protected function getMapResponseTemplateValidation() { $interactionResponses = []; /** @var ResponseDeclaration $responseDeclaration */ foreach ($this->responseDeclarations as $responseIdentifier => $responseDeclaration) { $responses = []; foreach ($responseDeclaration->getMapping()->getMapEntries()->getArrayCopy(true) as $mapEntry) { /** @var MapEntry $mapEntry */ $responses[] = new ValidResponse($mapEntry->getMappedValue(), [$this->possibleResponses[$responseIdentifier][$mapEntry->getMapKey()]]); // Find out if one of them is case sensitive if (!$mapEntry->isCaseSensitive()) { LogService::log('Could not support `caseSensitive` attribute for this interaction type. This question validation is always case sensitive'); } } $interactionResponses[] = $responses; } $responses = ArrayUtil::cartesianProductForResponses($interactionResponses); return ValidationBuilder::build('clozedropdown', 'exactMatch', $responses); }
protected function getMapResponseTemplateValidation() { $interactionResponses = []; /** @var ResponseDeclaration $responseDeclaration */ foreach ($this->responseDeclarations as $responseIdentifier => $responseDeclaration) { $responses = []; foreach ($responseDeclaration->getMapping()->getMapEntries()->getArrayCopy(true) as $mapEntry) { /** @var MapEntry $mapEntry */ $responses[] = new ValidResponse($mapEntry->getMappedValue(), [$mapEntry->getMapKey()]); // Find out if one of them is case sensitive if ($mapEntry->isCaseSensitive()) { $this->isCaseSensitive = true; } } $interactionResponses[] = $responses; } $responses = ArrayUtil::cartesianProductForResponses($interactionResponses); return ValidationBuilder::build('clozetext', 'exactMatch', $responses); }
protected function getMapResponseTemplateValidation() { $validResponses = []; foreach ($this->responseDeclaration->getMapping()->getMapEntries() as $mapEntry) { /** @var MapEntry $mapEntry */ if (!isset($this->options[$mapEntry->getMapKey()])) { LogService::log('Invalid choice `' . $mapEntry->getMapKey() . '`'); continue; } if ($mapEntry->getMappedValue() < 0) { LogService::log('Invalid score ` ' . $mapEntry->getMappedValue() . ' `. Negative score is ignored'); continue; } $validResponses[] = new ValidResponse($mapEntry->getMappedValue(), [$mapEntry->getMapKey()]); } // Handle `multiple` cardinality if ($this->responseDeclaration->getCardinality() === Cardinality::MULTIPLE) { $combinationChoicesCount = $this->maxChoices === 0 ? count($validResponses) : $this->maxChoices; $combinationResponses = ArrayUtil::combinations($validResponses, $combinationChoicesCount); $validResponses = ArrayUtil::combineValidResponsesWithSummedScore($combinationResponses); } return ValidationBuilder::build('mcq', 'exactMatch', $validResponses); }
protected function getMapResponseTemplateValidation() { $gapIdentifiersIndexMap = array_flip($this->gapIdentifiers); $responseIndexSet = []; $responses = []; foreach ($this->responseDeclaration->getMapping()->getMapEntries() as $mapEntry) { /** @var MapEntry $mapEntry */ /** @var QtiDirectedPair $mapKey */ $mapKey = $mapEntry->getMapKey(); // Map response value and index based from the `mapKey`, try to guess which one is which since they // apparently can swap :( if (isset($this->possibleResponses[$mapKey->getFirst()]) && isset($gapIdentifiersIndexMap[$mapKey->getSecond()])) { $responseValue = $this->possibleResponses[$mapKey->getFirst()]; $responseIndex = $gapIdentifiersIndexMap[$mapKey->getSecond()]; } else { if (isset($this->possibleResponses[$mapKey->getSecond()]) && isset($gapIdentifiersIndexMap[$mapKey->getFirst()])) { $responseValue = $this->possibleResponses[$mapKey->getSecond()]; $responseIndex = $gapIdentifiersIndexMap[$mapKey->getFirst()]; } else { throw new MappingException('Fail to match identifiers on `mapKey` attribute from `mapping`'); } } // Check for duplicated response if (!$this->isDuplicatedResponse) { if (!isset($responseIndexSet[$responseValue])) { $responseIndexSet[$responseValue] = true; } else { $this->isDuplicatedResponse = true; } } // Wrap the identifier => score into an array as the identifier can be duplicated (Duplicated Response) // Build ValidResponse object array in the correct order matching the `gap` elements $responses[$responseIndex][] = new ValidResponse($mapEntry->getMappedValue(), [$responseValue]); } $this->assertEachGapHasCorrespondingValidResponses($responses); $responses = ArrayUtil::cartesianProductForResponses($responses); return ValidationBuilder\ValidationBuilder::build($this->questionTypeName, 'exactMatch', $responses); }