protected function getMapResponseTemplateValidation() { // Build `value` array for a `valid_response` objects $values = []; $score = 0; foreach ($this->responseDeclaration->getMapping()->getMapEntries() as $mapEntry) { /** @var MapEntry $mapEntry */ $score += $mapEntry->getMappedValue(); $mapKey = $mapEntry->getMapKey(); // Map response value and index based from `QtiDirectedPair` Value, try to guess which one is which since they // apparently can swap :( if (isset($this->stemsMapping[$mapKey->getFirst()]) && isset($this->optionsMapping[$mapKey->getSecond()])) { $responseIndex = $this->stemsMapping[$mapKey->getFirst()]; $responseValue = $this->optionsMapping[$mapKey->getSecond()]; } else { if (isset($this->stemsMapping[$mapKey->getSecond()]) && isset($this->optionsMapping[$mapKey->getFirst()])) { $responseIndex = $this->stemsMapping[$mapKey->getSecond()]; $responseValue = $this->optionsMapping[$mapKey->getFirst()]; } else { throw new MappingException('Fail to match identifiers on `mapKey` attribute from `mapping`'); } } // Build values array in the correct order if (!isset($values[$responseIndex])) { $values[$responseIndex] = [$responseValue]; } else { array_push($values[$responseIndex], $responseValue); } } // Just to make sure we don't screw the order ksort($values); return ValidationBuilder::build('choicematrix', 'exactMatch', [new ValidResponse($score, $values)]); }
protected function getMatchCorrectTemplateValidation() { $choicesIndexes = array_flip(array_keys($this->choices)); $correctResponses = $this->responseDeclaration->getCorrectResponse()->getValues()->getArrayCopy(true); $values = []; /** @var Value $response */ foreach ($correctResponses as $response) { $identifier = $response->getValue(); $values[] = strval($choicesIndexes[$identifier]); } // No alt responses for hotspot interaction return ValidationBuilder::build('hotspot', 'exactMatch', [new ValidResponse(1, $values)]); }
protected function getMatchCorrectTemplateValidation() { // TODO: Validate against mismatch possible responses and correct response // Build the `value` object on `valid_response` $values = []; foreach ($this->responseDeclaration->getCorrectResponse()->getValues() as $v) { /** @var Value $v */ $value = $v->getValue(); if (!isset($this->orderMapping[$value])) { LogService::log('Cannot locate ' . $value . ' in responseDeclaration'); continue; } $values[] = $this->orderMapping[$value]; } return ValidationBuilder::build('orderlist', 'exactMatch', [new ValidResponse(1, $values)]); }
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); }
public function testMcqWithExactMatchValidation() { $question = $this->buildMcq(['ChoiceA' => 'Melbourne', 'ChoiceB' => 'Sydney', 'ChoiceC' => 'Jakarta']); $question->set_stimulus('<strong>Where is Learnosity office in Australia located?</strong>'); /** @var mcq_validation $validation */ $validation = ValidationBuilder::build('mcq', 'exactMatch', [new ValidResponse(1, ['ChoiceB'])]); $question->set_validation($validation); $mcqMapper = new McqMapper(); /** @var ResponseDeclaration $responseDeclaration */ /** @var ResponseProcessing $responseProcessing */ list($interaction, $responseDeclaration, $responseProcessing) = $mcqMapper->convert($question, 'testIdentifier', 'testIdentifierLabel'); $this->assertTrue($interaction instanceof ChoiceInteraction); // Check on the responseDeclaration and responseProcessing objects to be correctly generated $this->assertEquals(Constants::RESPONSE_PROCESSING_TEMPLATE_MATCH_CORRECT, $responseProcessing->getTemplate()); $this->assertEquals(Cardinality::SINGLE, $responseDeclaration->getCardinality()); $this->assertEquals(BaseType::IDENTIFIER, $responseDeclaration->getBaseType()); $this->assertNotNull($responseDeclaration->getCorrectResponse()); $this->assertEquals('ChoiceB', $responseDeclaration->getCorrectResponse()->getValues()->getArrayCopy(true)[0]->getValue()); $this->assertNull($responseDeclaration->getMapping()); }
public function testWithComplexExactMatchValidation() { $orderlist = $this->buildOrderlist(['ant', 'elephant', 'dog'], 'Order these animal from big to small'); /** @var orderlist_validation $validation */ $validation = ValidationBuilder::build('orderlist', 'exactMatch', [new ValidResponse(3, [1, 2, 0]), new ValidResponse(1, [2, 1, 0])]); $orderlist->set_validation($validation); /** @var orderlist_validation $validation */ $orderlistMapper = new OrderlistMapper(); /** @var ResponseDeclaration $responseDeclaration */ /** @var ResponseProcessing $responseProcessing */ list($interaction, $responseDeclaration, $responseProcessing) = $orderlistMapper->convert($orderlist, 'testIdentifier', 'testIdentifierLabel'); /** @var OrderInteraction $interaction */ $this->assertTrue($interaction instanceof OrderInteraction); // Assert response declaration is correct /** @var Value[] $correctResponseValues */ $this->assertEquals(Cardinality::ORDERED, $responseDeclaration->getCardinality()); $correctResponseValues = $responseDeclaration->getCorrectResponse()->getValues()->getArrayCopy(true); $this->assertEquals('CHOICE_1', $correctResponseValues[0]->getValue()); $this->assertEquals('CHOICE_2', $correctResponseValues[1]->getValue()); $this->assertEquals('CHOICE_0', $correctResponseValues[2]->getValue()); $this->assertEquals(Constants::RESPONSE_PROCESSING_TEMPLATE_MATCH_CORRECT, $responseProcessing->getTemplate()); }
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); }
public function testMultipleResponsesWithValidation() { $question = $this->buildSimpleChoiceMatrixQuestion(); /** @var choicematrix_validation $validation */ $validation = ValidationBuilder::build('choicematrix', 'exactMatch', [new ValidResponse(3, [0, [1, 2]]), new ValidResponse(1, [0, 1])]); $question->set_validation($validation); $question->set_multiple_responses(true); /** @var MatchInteraction $interaction */ /** @var ResponseDeclaration $responseDeclaration */ /** @var ResponseProcessing $responseProcessing */ $mapper = new ChoicematrixMapper(); list($interaction, $responseDeclaration, $responseProcessing) = $mapper->convert($question, 'testIdentifier', 'testIdentifier'); $this->assertEquals(6, $interaction->getMaxAssociations()); $this->assertEquals(1, $interaction->getMinAssociations()); // Stem count /** @var Value[] $correctResponseValues */ $correctResponseValues = $responseDeclaration->getCorrectResponse()->getValues()->getArrayCopy(true); $this->assertDirectPair($correctResponseValues[0]->getValue(), 'STEM_0', 'OPTION_0'); $this->assertDirectPair($correctResponseValues[1]->getValue(), 'STEM_1', 'OPTION_1'); $this->assertDirectPair($correctResponseValues[2]->getValue(), 'STEM_1', 'OPTION_2'); // The validation in `choicematrix` relies on its key to describe the index of stem/option pair // Scoring is always set to `1`, with the upper bound is set to the actual valid_response`'s score $this->assertNull($responseDeclaration->getMapping()); }
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); }
private function buildShorttextWithValidation(array $validResponses) { $question = new shorttext('shorttext'); $question->set_placeholder('placeholdertest'); $question->set_stimulus('<strong>stimulushere</strong>'); $validation = ValidationBuilder::build('shorttext', 'exactMatch', $validResponses); $question->set_validation($validation); return $question; }