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;
 }