public function testIsDefault() { $itemSessionControl = new ItemSessionControl(); $this->assertTrue($itemSessionControl->isDefault()); $itemSessionControl->setMaxAttempts(0); $this->assertFalse($itemSessionControl->isDefault()); }
public function testAssigningScoresAndCorrectResponses() { $doc = new XmlDocument(); $doc->load(self::samplesDir() . 'custom/items/template_processing.xml'); $session = new AssessmentItemSession($doc->getDocumentComponent()); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setMaxAttempts(0); $session->setItemSessionControl($itemSessionControl); $session->beginItemSession(); // Check that the templateProcessing was correctly processed. $this->assertEquals('ChoiceA', $session->getVariable('RESPONSE')->getCorrectResponse()->getValue()); $this->assertEquals(1.0, $session['GOODSCORE']->getValue()); $this->assertEquals(0.0, $session['WRONGSCORE']->getValue()); // Check that it really works... // With a correct response. $session->beginAttempt(); $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new QtiIdentifier('ChoiceA')))); $session->endAttempt($responses); $this->assertEquals(1.0, $session['SCORE']->getValue()); // With an incorrect response. $session->beginAttempt(); $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new QtiIdentifier('ChoiceB')))); $session->endAttempt($responses); $this->assertEquals(0.0, $session['SCORE']->getValue()); }
public function testMarshallMaximal() { $assessmentSection1 = new ExtendedAssessmentSection('section1', 'My Section 1', true); $assessmentSection2 = new ExtendedAssessmentSection('section2', 'My Section 2', true); $preCondition = new PreCondition(new BaseValue(BaseType::BOOLEAN, true)); $branching = new BranchRule(new BaseValue(BaseType::BOOLEAN, true), 'EXIT_TESTPART'); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setShowSolution(true); $timeLimits = new TimeLimits(null, new QtiDuration('PT1M40S')); $p = new P(); $p->setContent(new InlineCollection(array(new TextRun('Prima!')))); $testFeedback = new TestFeedback('feedback1', 'show', new FlowStaticCollection(array($p))); $testFeedback->setTitle('hello!'); $testFeedback->setAccess(TestFeedbackAccess::AT_END); $testFeedback->setShowHide(ShowHide::SHOW); $testFeedbackRef = new TestFeedbackRef('feedback1', 'show', TestFeedbackAccess::AT_END, ShowHide::SHOW, './TF01.xml'); $assessmentSections = new AssessmentSectionCollection(array($assessmentSection1, $assessmentSection2)); $preConditions = new PreConditionCollection(array($preCondition)); $branchings = new BranchRuleCollection(array($branching)); $testFeedbacks = new TestFeedbackCollection(array($testFeedback)); $testFeedbackRefs = new TestFeedbackRefCollection(array($testFeedbackRef)); $extendedTestPart = new ExtendedTestPart('part1', $assessmentSections); $extendedTestPart->setPreConditions($preConditions); $extendedTestPart->setBranchRules($branchings); $extendedTestPart->setItemSessionControl($itemSessionControl); $extendedTestPart->setTimeLimits($timeLimits); $extendedTestPart->setTestFeedbacks($testFeedbacks); $extendedTestPart->setTestFeedbackRefs($testFeedbackRefs); $factory = new CompactMarshallerFactory(); $element = $factory->createMarshaller($extendedTestPart)->marshall($extendedTestPart); $dom = new DOMDocument('1.0', 'UTF-8'); $element = $dom->importNode($element, true); $this->assertEquals('<testPart identifier="part1" navigationMode="linear" submissionMode="individual"><preCondition><baseValue baseType="boolean">true</baseValue></preCondition><branchRule target="EXIT_TESTPART"><baseValue baseType="boolean">true</baseValue></branchRule><itemSessionControl maxAttempts="1" showFeedback="false" allowReview="true" showSolution="true" allowComment="false" allowSkipping="true" validateResponses="false"/><timeLimits maxTime="100" allowLateSubmission="false"/><assessmentSection identifier="section1" required="false" fixed="false" title="My Section 1" visible="true" keepTogether="true"/><assessmentSection identifier="section2" required="false" fixed="false" title="My Section 2" visible="true" keepTogether="true"/><testFeedback access="atEnd" outcomeIdentifier="show" showHide="show" identifier="feedback1" title="hello!"><p>Prima!</p></testFeedback><testFeedbackRef identifier="feedback1" outcomeIdentifier="show" access="atEnd" showHide="show" href="./TF01.xml"/></testPart>', $dom->saveXML($element)); }
/** * @see \qtism\data\storage\xml\marshalling\Marshaller::unmarshall() */ protected function unmarshall(DOMElement $element) { $object = new ItemSessionControl(); if (($value = static::getDOMElementAttributeAs($element, 'maxAttempts', 'integer')) !== null) { $object->setMaxAttempts($value); } if (($value = static::getDOMElementAttributeAs($element, 'showFeedback', 'boolean')) !== null) { $object->setShowFeedback($value); } if (($value = static::getDOMElementAttributeAs($element, 'allowReview', 'boolean')) !== null) { $object->setAllowReview($value); } if (($value = static::getDOMElementAttributeAs($element, 'showSolution', 'boolean')) !== null) { $object->setShowSolution($value); } if (($value = static::getDOMElementAttributeAs($element, 'allowComment', 'boolean')) !== null) { $object->setAllowComment($value); } if (($value = static::getDOMElementAttributeAs($element, 'allowSkipping', 'boolean')) !== null) { $object->setAllowSkipping($value); } if (($value = static::getDOMElementAttributeAs($element, 'validateResponses', 'boolean')) !== null) { $object->setValidateResponses($value); } return $object; }
public function testMarshall() { $component = new ItemSessionControl(); $component->setAllowComment(true); $component->setMaxAttempts(2); $component->setValidateResponses(false); $marshaller = $this->getMarshallerFactory()->createMarshaller($component); $element = $marshaller->marshall($component); $this->assertInstanceOf('\\DOMElement', $element); $this->assertEquals('itemSessionControl', $element->nodeName); $this->assertEquals('true', $element->getAttribute('allowComment')); $this->assertEquals('2', $element->getAttribute('maxAttempts')); $this->assertEquals('false', $element->getAttribute('validateResponses')); $this->assertEquals('true', $element->getAttribute('allowReview')); $this->assertEquals('false', $element->getAttribute('showSolution')); $this->assertEquals('true', $element->getAttribute('allowSkipping')); }
public function testMarshallNotRecursive() { $identifier = 'myAssessmentSection'; $title = 'A non Recursive Assessment Section'; $visible = true; $keepTogether = false; // preConditions $preConditions = new PreConditionCollection(); $preConditions[] = new PreCondition(new BaseValue(BaseType::BOOLEAN, true)); // branchRules $branchRules = new BranchRuleCollection(); $branchRules[] = new BranchRule(new BaseValue(BaseType::BOOLEAN, false), 'EXIT_TEST'); // itemSessionControl $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setAllowReview(true); // sectionParts $sectionParts = new SectionPartCollection(); $sectionParts[] = new AssessmentItemRef('Q01', './questions/Q01.xml'); $sectionParts[] = new AssessmentItemRef('Q02', './questions/Q02.xml'); $sectionParts[] = new AssessmentSectionRef('S01', './sections/S01.xml'); $component = new AssessmentSection($identifier, $title, $visible); $component->setKeepTogether($keepTogether); $component->setPreConditions($preConditions); $component->setBranchRules($branchRules); $component->setItemSessionControl($itemSessionControl); $component->setSectionParts($sectionParts); $marshaller = $this->getMarshallerFactory('2.1.0')->createMarshaller($component); $element = $marshaller->marshall($component); $this->assertInstanceOf('\\DOMElement', $element); $this->assertEquals($identifier, $element->getAttribute('identifier')); $this->assertEquals($title, $element->getAttribute('title')); $this->assertEquals('true', $element->getAttribute('visible')); $this->assertEquals('false', $element->getAttribute('keepTogether')); $this->assertEquals(1, $element->getElementsByTagName('preCondition')->length); $this->assertEquals(1, $element->getElementsByTagName('preCondition')->item(0)->getElementsByTagName('baseValue')->length); $this->assertEquals(1, $element->getElementsByTagName('branchRule')->length); $this->assertEquals(1, $element->getElementsByTagName('branchRule')->item(0)->getElementsByTagName('baseValue')->length); $this->assertEquals(1, $element->getElementsByTagName('itemSessionControl')->length); $this->assertEquals('true', $element->getElementsByTagName('itemSessionControl')->item(0)->getAttribute('allowReview')); $this->assertEquals(2, $element->getElementsByTagName('assessmentItemRef')->length); $this->assertEquals('Q02', $element->getElementsByTagName('assessmentItemRef')->item(1)->getAttribute('identifier')); $this->assertEquals(1, $element->getElementsByTagName('assessmentSectionRef')->length); $this->assertEquals('S01', $element->getElementsByTagName('assessmentSectionRef')->item(0)->getAttribute('identifier')); }
public function testOnlyScalarPropertiesConstructorAndProperties() { $component = new ItemSessionControl(); $ctx = $this->createMarshallingContext(); $scalarMarshaller = new PhpScalarMarshaller($ctx, $component->getMaxAttempts()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->mustShowFeedback()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->doesAllowReview()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->mustShowSolution()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->doesAllowComment()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->mustValidateResponses()); $scalarMarshaller->marshall(); $scalarMarshaller->setToMarshall($component->doesAllowSkipping()); $scalarMarshaller->marshall(); $componentMarshaller = new PhpQtiComponentMarshaller($ctx, $component); $componentMarshaller->marshall(); $expected = "\$integer_0 = 1;\n"; $expected .= "\$boolean_0 = false;\n"; $expected .= "\$boolean_1 = true;\n"; $expected .= "\$boolean_2 = false;\n"; $expected .= "\$boolean_3 = false;\n"; $expected .= "\$boolean_4 = false;\n"; $expected .= "\$boolean_5 = true;\n"; $expected .= "\$itemsessioncontrol_0 = new qtism\\data\\ItemSessionControl();\n"; $expected .= "\$itemsessioncontrol_0->setMaxAttempts(\$integer_0);\n"; $expected .= "\$itemsessioncontrol_0->setShowFeedback(\$boolean_0);\n"; $expected .= "\$itemsessioncontrol_0->setAllowReview(\$boolean_1);\n"; $expected .= "\$itemsessioncontrol_0->setShowSolution(\$boolean_2);\n"; $expected .= "\$itemsessioncontrol_0->setAllowComment(\$boolean_3);\n"; $expected .= "\$itemsessioncontrol_0->setValidateResponses(\$boolean_4);\n"; $expected .= "\$itemsessioncontrol_0->setAllowSkipping(\$boolean_5);\n"; $this->assertEquals($expected, $this->getStream()->getBinary()); }
public function testTemplateVariableDefault() { // This test aims at testing whether template variables // are correctly instantiated as part of the item session and // they can be used in response processing. $doc = new XmlDocument('2.1.0'); $doc->load(self::samplesDir() . 'custom/items/template_declaration_default.xml'); $itemSession = new AssessmentItemSession($doc->getDocumentComponent()); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setMaxAttempts(0); $itemSession->setItemSessionControl($itemSessionControl); $itemSession->beginItemSession(); $this->assertTrue($itemSession['WRONGSCORE']->equals(new Float(0.0))); $this->assertTrue($itemSession['GOODSCORE']->equals(new Float(1.0))); // 1st attempt to get 'GOODSCORE' as 'SCORE'. $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('ChoiceA')))); $itemSession->beginAttempt(); $itemSession->endAttempt($responses); $this->assertTrue($itemSession['SCORE']->equals($itemSession['GOODSCORE'])); // 2nd attempt to get 'WRONGSCORE' as 'SCORE'. $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('ChoiceB')))); $itemSession->beginAttempt(); $itemSession->endAttempt($responses); $this->assertTrue($itemSession['SCORE']->equals($itemSession['WRONGSCORE'])); }
/** * begin an attempt for this item session. The value of the built-in outcome variable 'completionStatus' is set to the 'unknown' value at * the beginning of the very first attempt on this session. * * * If the attempt to begin is the first one of the session, response variables are applied their default value. * * If the current submissionMode of the session is SIMULTANEOUS, only one call to beginAttempt() is allowed, otherwise an exception will be thrown. * * @throws \qtism\runtime\tests\AssessmentItemSessionException If the maximum number of attempts or the maximum time limit in force is reached. * @see http://www.imsglobal.org/question/qtiv2p1/imsqti_infov2p1.html#section10055 The IMS QTI 2.1 Item Session Lifecycle. */ public function beginAttempt() { $maxAttempts = $this->itemSessionControl->getMaxAttempts(); $numAttempts = $this['numAttempts']->getValue(); $submissionMode = $this->getSubmissionMode(); /* If the current submission mode is SIMULTANEOUS, only 1 attempt is allowed per item. * * From IMS QTI: * * In simultaneous mode, response processing cannot take place until the testPart is * complete so each item session passes between the interacting and suspended states only. * By definition the candidate can take one and only one attempt at each item and feedback * cannot be seen during the test. Whether or not the candidate can return to review * their responses and/or any item-level feedback after the test, is outside the scope * of this specification. Simultaneous mode is typical of paper-based tests. * */ if ($submissionMode === SubmissionMode::SIMULTANEOUS) { $maxAttempts = 1; } // Check if we can perform a new attempt. if ($this->getState() === AssessmentItemSessionState::CLOSED) { if ($submissionMode === SubmissionMode::SIMULTANEOUS && $numAttempts > 0) { $identifier = $this->getAssessmentItem()->getIdentifier(); $msg = "A new attempt for item '{$identifier}' is not allowed. The submissionMode is simultaneous and the only accepted attempt is already begun."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::ATTEMPTS_OVERFLOW); } elseif ($submissionMode === SubmissionMode::INDIVIDUAL && $maxAttempts !== 0 && $numAttempts >= $maxAttempts) { $identifier = $this->getAssessmentItem()->getIdentifier(); $msg = "A new attempt for item '{$identifier}' is not allowed. The maximum number of attempts ({$maxAttempts}) is reached."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::ATTEMPTS_OVERFLOW); } elseif ($this->isMaxTimeReached() === true) { $identifier = $this->getAssessmentItem()->getIdentifier(); $msg = "A new attempt for item '{$identifier}' is not allowed. The maximum time limit in force is reached."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::DURATION_OVERFLOW); } elseif ($this->getAssessmentItem()->isAdaptive() === true && $this['completionStatus']->getValue() === self::COMPLETION_STATUS_COMPLETED) { $identifier = $this->getAssessmentItem()->getIdentifier(); $msg = "A new attempt for item '{$identifier}' is not allowed. It is adaptive and its completion status is 'completed'."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::ATTEMPTS_OVERFLOW); } } // Response variables' default values are set at the beginning of the first attempt. if ($numAttempts === 0) { $data =& $this->getDataPlaceHolder(); // At the start of the first attempt, the completionStatus goes to 'unknown' and response // variables get their default values if any. foreach (array_keys($data) as $k) { if ($data[$k] instanceof ResponseVariable) { $data[$k]->applyDefaultValue(); } } $this['duration'] = new QtiDuration('PT0S'); $this['numAttempts'] = new QtiInteger(0); // At the start of the first attempt, the completionStatus goes // to 'unknown'. $this['completionStatus']->setValue(self::COMPLETION_STATUS_UNKNOWN); } // For any attempt, the variables related to endAttemptInteractions are reset to false. foreach ($this->getAssessmentItem()->getEndAttemptIdentifiers() as $endAttemptIdentifier) { $this[$endAttemptIdentifier] = new QtiBoolean(false); } // Increment the built-in variable 'numAttempts' by one. $this['numAttempts']->setValue($numAttempts + 1); // The session get the INTERACTING state. $this->setState(AssessmentItemSessionState::INTERACTING); $this->setAttempting(true); }
public function testEvolutionBasicMultipleAttempts() { $count = 5; $attempts = array(new Identifier('ChoiceA'), new Identifier('ChoiceB'), new Identifier('ChoiceC'), new Identifier('ChoiceD'), new Identifier('ChoiceE')); $expected = array(new Float(0.0), new Float(1.0), new Float(0.0), new Float(0.0), new Float(0.0)); $itemSession = self::instantiateBasicAssessmentItemSession(); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setMaxAttempts($count); $itemSession->setItemSessionControl($itemSessionControl); $itemSession->beginItemSession(); for ($i = 0; $i < $count; $i++) { // Here, manual set up of responses. $this->assertTrue($itemSession->isAttemptable()); $itemSession->beginAttempt(); // simulate some time... 1 second to answer the item. sleep(1); $itemSession['RESPONSE'] = $attempts[$i]; $itemSession->endAttempt(); $this->assertInstanceOf('qtism\\common\\datatypes\\Float', $itemSession['SCORE']); $this->assertTrue($expected[$i]->equals($itemSession['SCORE'])); $this->assertEquals($i + 1, $itemSession['numAttempts']->getValue()); // 1 more second before the next attempt. // we are here in suspended mode so it will not be // added to the duration. sleep(1); } // The total duration shold have taken 5 seconds, the rest of the time was in SUSPENDED state. $this->assertEquals(5, $itemSession['duration']->getSeconds(true)); // one more and we get an expection... :) try { $this->assertFalse($itemSession->isAttemptable()); $itemSession->beginAttempt(); $this->assertTrue(false); } catch (AssessmentItemSessionException $e) { $this->assertEquals(AssessmentItemSessionException::STATE_VIOLATION, $e->getCode()); } }
public function testEvolutionBasicMultipleAttempts() { $count = 5; $attempts = array(new QtiIdentifier('ChoiceA'), new QtiIdentifier('ChoiceB'), new QtiIdentifier('ChoiceC'), new QtiIdentifier('ChoiceD'), new QtiIdentifier('ChoiceE')); $expected = array(new QtiFloat(0.0), new QtiFloat(1.0), new QtiFloat(0.0), new QtiFloat(0.0), new QtiFloat(0.0)); $itemSession = self::instantiateBasicAssessmentItemSession(); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setMaxAttempts($count); $itemSession->setItemSessionControl($itemSessionControl); $itemSession->setTime(self::createDate('2014-07-14 13:00:00')); $itemSession->beginItemSession(); for ($i = 0; $i < $count; $i++) { // Here, manual set up of responses. $this->assertTrue($itemSession->isAttemptable()); $itemSession->beginAttempt(); // simulate some time... 1 second to answer the item. $t = $i + 1; $itemSession->setTime(self::createDate("2014-07-14 13:00:0{$t}")); $itemSession['RESPONSE'] = $attempts[$i]; $itemSession->endAttempt(); $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $itemSession['SCORE']); $this->assertTrue($expected[$i]->equals($itemSession['SCORE'])); $this->assertEquals($t, $itemSession['numAttempts']->getValue()); } // The total duration should have taken 5 seconds. $this->assertEquals(5, $itemSession['duration']->getSeconds(true)); // one more and we get an exception... :) try { $this->assertFalse($itemSession->isAttemptable()); $itemSession->beginAttempt(); $this->assertTrue(false); } catch (AssessmentItemSessionException $e) { $this->assertEquals(AssessmentItemSessionException::ATTEMPTS_OVERFLOW, $e->getCode()); } }
/** * End the attempt by providing responses or by another action. If $responses * is provided, the values found into it will be merged to the current state * before ResponseProcessing is executed. * * * If the item is adaptive and the completionStatus is indicated to be 'completed', the item session ends. * * If the item is non-adaptive, and the number of attempts is exceeded, the item session ends and the completionStatus is set to 'completed'. * * Otherwise, the item session goes to the SUSPENDED state, waiting for a next attempt. * * @param State $responses (optional) A State composed by the candidate's responses to the item. * @param boolean $responseProcessing (optional) Whether to execute the responseProcessing or not. * @param boolean $allowLateSubmission If set to true, maximum time limits will not be taken into account, even if the a maximum time limit is in force. * @throws AssessmentItemSessionException */ public function endAttempt(State $responses = null, $responseProcessing = true, $allowLateSubmission = false) { // End of attempt, go in SUSPEND state. $this->suspend(); // Flag to indicate if time is exceed or not. $maxTimeExceeded = false; // Is timeLimits in force. if ($this->hasTimeLimits() === true) { // As per QTI 2.1 Spec, Minimum times are only applicable to assessmentSections and // assessmentItems only when linear navigation mode is in effect. if ($this->isNavigationLinear() === true && $this->timeLimits->hasMinTime() === true) { if ($this->mustConsiderMinTime() === true && $this['duration']->getSeconds(true) <= $this->timeLimits->getMinTime()->getSeconds(true)) { // An exception is thrown to prevent the numAttempts to be incremented. // Suspend and wait for a next attempt. $this->suspend(); $msg = "The minimal duration is not yet reached."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::DURATION_UNDERFLOW); } } // Check if the maxTime constraint is respected. // If late submission is allowed but time exceeded, the item session will be considered 'completed'. // Otherwise, if late submission is not allowed but time exceeded, the session goes to 'incomplete'. if ($this->isMaxTimeReached() === true) { $maxTimeExceeded = true; if ($this->timeLimits->doesAllowLateSubmission() === false && $allowLateSubmission === false) { $this['completionStatus']->setValue(self::COMPLETION_STATUS_INCOMPLETE); $msg = "The maximal duration is exceeded."; $this->endItemSession(); throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::DURATION_OVERFLOW); } else { $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED); $this->endItemSession(); } } } // As per specs, when validateResponses is turned on (true) then the candidates are not // allowed to submit the item until they have provided valid responses for all // interactions. When turned off (false) invalid responses may be accepted by the // system. if ($this->submissionMode === SubmissionMode::INDIVIDUAL && $this->itemSessionControl->mustValidateResponses() === true) { // Use the correct expression to control if the responses // are correct. foreach ($responses as $response) { // @todo Reconsider the 'valid' concept. $correctExpression = new Correct($response->getIdentifier()); $correctProcessor = new CorrectProcessor($correctExpression); $correctProcessor->setState($responses); try { if ($correctProcessor->process() !== true) { $responseIdentifier = $response->getIdentifier(); $msg = "The current itemSessionControl.validateResponses attribute is set to true but "; $msg .= "response '{$responseIdentifier}' is incorrect."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::INVALID_RESPONSE); } } catch (ExpressionProcessingException $e) { $responseIdentifier = $response->getIdentifier(); $msg = "The current itemSessionControl.validResponses attribute is set to true but an error "; $msg .= "occured while trying to detect if response '{$responseIdentifier}' was correct."; throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::RUNTIME_ERROR, $e); } } } // Apply the responses (if provided) to the current state and deal with the responseProcessing. if ($responses !== null) { foreach ($responses as $identifier => $value) { $this[$identifier] = $value->getValue(); } } // Apply response processing. // As per QTI 2.1 specs, For Non-adaptive Items, the values of the outcome variables are reset to their // default values prior to each invocation of responseProcessing. For Adaptive Items the outcome variables // retain the values that were assigned to them during the previous invocation of response processing. // For more information, see Response Processing. // // The responseProcessing can be skipped by given a false value to $responseProcessing. Why? // Because when the SubmissionMode is SubmissionMode::SIMULTANEOUS, the responseProcessing must // deffered to the end of the current testPart. if ($responseProcessing === true) { if ($this->assessmentItem->isAdaptive() === false) { $this->resetOutcomeVariables(); } $responseProcessing = $this->assessmentItem->getResponseProcessing(); // Some items (especially to collect information) have no response processing! if ($responseProcessing !== null && ($responseProcessing->hasTemplate() === true || $responseProcessing->hasTemplateLocation() === true || count($responseProcessing->getResponseRules()) > 0)) { $engine = $this->createResponseProcessingEngine($responseProcessing); $engine->process(); } } $maxAttempts = $this->itemSessionControl->getMaxAttempts(); if ($this->submissionMode === SubmissionMode::SIMULTANEOUS) { $maxAttempts = 1; } // -- Adaptive or non-adaptive item, maximum time limit reached but late submission allowed. if ($maxTimeExceeded === true) { $this->endItemSession(); $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED); } else { if ($this->assessmentItem->isAdaptive() === true && $this->submissionMode === SubmissionMode::INDIVIDUAL && $this['completionStatus']->getValue() === self::COMPLETION_STATUS_COMPLETED) { $this->endItemSession(); } else { if ($this->assessmentItem->isAdaptive() === false && $this['numAttempts']->getValue() >= $maxAttempts) { // Close only if $maxAttempts !== 0 because 0 means no limit! if ($maxAttempts !== 0) { $this->endItemSession(); } // Even if there is no limit of attempts, we consider the item completed. $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED); } else { if ($this->assessmentItem->isAdaptive() === false && $this['numAttempts']->getValue() < $maxAttempts) { $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED); } } } } // else... // Wait for the next attempt. $this->attempting = false; }
public function testValidResponsesInForceValid() { $itemSession = self::instantiateBasicAssessmentItemSession(); $itemSessionControl = new ItemSessionControl(); $itemSessionControl->setValidateResponses(false); $itemSession->setItemSessionControl($itemSessionControl); $itemSession->beginItemSession(); $itemSession->beginAttempt(); $responses = new State(); $responses->setVariable(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('ChoiceD'))); $itemSession->endAttempt($responses); }