/** * Add an AssessmentItemSession to the store, for a given $occurence number. * * @param \qtism\runtime\tests\AssessmentItemSession $assessmentItemSession * @param integer $occurence The occurence number of the session. */ public function addAssessmentItemSession(AssessmentItemSession $assessmentItemSession, $occurence = 0) { $assessmentItemRef = $assessmentItemSession->getAssessmentItem(); if (isset($this->shelves[$assessmentItemRef]) === false) { $this->shelves[$assessmentItemRef] = new AssessmentItemSessionCollection(); } $this->shelves[$assessmentItemRef][$occurence] = $assessmentItemSession; }
public function testGetShuffledChoiceIdentifierAtInvalidShuffledChoiceIndex() { $doc = new XmlDocument(); $doc->load(self::samplesDir() . 'ims/items/2_1/choice_fixed.xml'); $session = new AssessmentItemSession($doc->getDocumentComponent()); $session->beginItemSession(); $this->setExpectedException('\\OutOfBoundsException', 'No identifier at index 1337.'); $session->getShuffledChoiceIdentifierAt(0, 1337); }
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()); }
/** * Add an item session to the current assessment test session. * * @param AssessmentItemSession $session * @throws LogicException If the AssessmentItemRef object bound to $session is unknown by the AssessmentTestSession. */ protected function addItemSession(AssessmentItemSession $session, $occurence = 0) { $assessmentItemRefs = $this->getAssessmentItemRefs(); $sessionAssessmentItemRefIdentifier = $session->getAssessmentItem()->getIdentifier(); if ($this->getAssessmentItemRefs()->contains($session->getAssessmentItem()) === false) { // The session that is requested to be set is bound to an item // which is not referenced in the test. This is a pure logic error. $msg = "The item session to set is bound to an unknown AssessmentItemRef."; throw new LogicException($msg); } $this->getAssessmentItemSessionStore()->addAssessmentItemSession($session, $occurence); }
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'])); }
public function testWriteAssessmentItemSession() { $doc = new XmlCompactDocument(); $doc->load(self::samplesDir() . 'custom/runtime/itemsubset.xml'); $seeker = new AssessmentTestSeeker($doc->getDocumentComponent(), array('assessmentItemRef', 'outcomeDeclaration', 'responseDeclaration', 'itemSessionControl')); $stream = new MemoryStream(); $stream->open(); $access = new QtiBinaryStreamAccess($stream, new FileSystemFileManager()); $session = new AssessmentItemSession($doc->getDocumentComponent()->getComponentByIdentifier('Q02')); $session->beginItemSession(); $access->writeAssessmentItemSession($seeker, $session); $stream->rewind(); $session = $access->readAssessmentItemSession(new SessionManager(), $seeker); $this->assertEquals(AssessmentItemSessionState::INITIAL, $session->getState()); $this->assertEquals(NavigationMode::LINEAR, $session->getNavigationMode()); $this->assertEquals(SubmissionMode::INDIVIDUAL, $session->getSubmissionMode()); $this->assertEquals('PT0S', $session['duration']->__toString()); $this->assertEquals(0, $session['numAttempts']->getValue()); $this->assertEquals('not_attempted', $session['completionStatus']->getValue()); $this->assertFalse($session->isAttempting()); $this->assertEquals(0.0, $session['SCORE']->getValue()); $this->assertTrue($session['RESPONSE']->equals(new MultipleContainer(BaseType::PAIR))); $this->assertSame(null, $session->getTimeReference()); $this->assertFalse($session->hasTimeReference()); }
protected static function buildOutcomeResponse(AssessmentItemSession $itemSession) { $stateOutput = new taoQtiCommon_helpers_PciStateOutput(); foreach ($itemSession->getOutcomeVariables(false) as $var) { $stateOutput->addVariable($var); } $output = $stateOutput->getOutput(); return $output; }
protected function submitItemResults(AssessmentItemSession $assessmentItemSession, $occurence = 0) { foreach ($assessmentItemSession as $id => $var) { $this->addItemResultResult($assessmentItemSession->getAssessmentItem()->getIdentifier() . '.' . $occurence . '.' . $id, $var->getValue()); } }
/** * AssessmentTestSession implementations must override this method in order * to submit item results from a given $assessmentItemSession to the appropriate * data source. * * This method is triggered each time response processing takes place. * * @param AssessmentItemSession $itemSession The lastly updated AssessmentItemSession. * @param integer $occurrence The occurrence number of the item bound to $assessmentItemSession. * @throws AssessmentTestSessionException With error code RESULT_SUBMISSION_ERROR if an error occurs while transmitting results. */ public function submitItemResults(AssessmentItemSession $itemSession, $occurrence = 0) { $itemRef = $itemSession->getAssessmentItem(); $identifier = $itemRef->getIdentifier(); $duration = $this->getTimerDuration($identifier); $itemDurationVar = $itemSession->getVariable('duration'); $sessionDuration = $itemDurationVar->getValue(); \common_Logger::i("Force duration of item '{$identifier}' to {$duration} instead of {$sessionDuration}"); $itemSession->getVariable('duration')->setValue($duration); parent::submitItemResults($itemSession, $occurrence); }
/** * Write an AssessmetnItemSession from the current binary stream. * * @param AssessmentTestSeeker $seeker The AssessmentTestSeeker object from where the position of components will be pulled out. * @param AssessmentItemSession $session An AssessmentItemSession object. * @throws QtiBinaryStreamAccessException */ public function writeAssessmentItemSession(AssessmentTestSeeker $seeker, AssessmentItemSession $session) { try { $this->writeShort($seeker->seekPosition($session->getAssessmentItem())); $this->writeTinyInt($session->getState()); $this->writeTinyInt($session->getNavigationMode()); $this->writeTinyInt($session->getSubmissionMode()); $this->writeBoolean($session->isAttempting()); $isItemSessionControlDefault = $session->getItemSessionControl()->isDefault(); if ($isItemSessionControlDefault === true) { $this->writeBoolean(false); } else { $this->writeBoolean(true); $this->writeShort($seeker->seekPosition($session->getItemSessionControl())); } if ($session->getState() !== AssessmentItemSessionState::NOT_SELECTED) { $this->writeTinyInt($session['numAttempts']->getValue()); $this->writeDuration($session['duration']); $this->writeString($session['completionStatus']->getValue()); if ($session['numAttempts']->getValue() > 0) { $this->writeDateTime($session->getTimeReference()); } } // minus the 3 built-in variables $varCount = count($session) - 3; $this->writeTinyInt($varCount); $itemOutcomes = $session->getAssessmentItem()->getOutcomeDeclarations(); $itemResponses = $session->getAssessmentItem()->getResponseDeclarations(); foreach ($session->getKeys() as $varId) { if (in_array($varId, array('numAttempts', 'duration', 'completionStatus')) === false) { $var = $session->getVariable($varId); $isOutcome = $var instanceof OutcomeVariable; $variableDeclaration = $isOutcome === true ? $itemOutcomes[$varId] : $itemResponses[$varId]; try { $this->writeBoolean($isOutcome); $this->writeShort($seeker->seekPosition($variableDeclaration)); $this->writeVariableValue($var); } catch (OutOfBoundsException $e) { $msg = "No variable found in the assessmentTest tree structure."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } } } } catch (BinaryStreamAccessException $e) { $msg = "An error occured while writing an assessment item session."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } catch (OutOfBoundsException $e) { $msg = "No assessmentItemRef found in the assessmentTest tree structure."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } }
/** * Get the feedback to be displayed on an AssessmentItemSession * * @param tao_models_classes_service_StorageDirectory $directory * @param \qtism\runtime\tests\AssessmentItemSession $itemSession * @return array */ public static function getFeedbacks(tao_models_classes_service_StorageDirectory $directory, AssessmentItemSession $itemSession) { $returnValue = array(); $feedbackClasses = array('modalFeedback', 'feedbackInline', 'feedbackBlock'); $elements = self::getContentVariableElements($directory); $outcomes = array(); foreach ($elements as $data) { if (empty($data['qtiClass']) === false && in_array($data['qtiClass'], $feedbackClasses)) { $feedbackIdentifier = $data['attributes']['identifier']; $outcomeIdentifier = $data['attributes']['outcomeIdentifier']; if (!isset($outcomes[$outcomeIdentifier])) { $outcomes[$outcomeIdentifier] = array(); } $outcomes[$outcomeIdentifier][$feedbackIdentifier] = $data; } } foreach ($itemSession->getAllVariables() as $var) { $identifier = $var->getIdentifier(); if (isset($outcomes[$identifier])) { $feedbacks = $outcomes[$identifier]; $feedbackIds = QtiRunner::getVariableValues($var); foreach ($feedbackIds as $feedbackId) { if (isset($feedbacks[$feedbackId])) { $data = $feedbacks[$feedbackId]; $returnValue[$data['serial']] = $data; } } } } return $returnValue; }
public function testStandaloneMultipleInteractions() { $doc = new XmlDocument('2.1'); $doc->load(self::samplesDir() . 'custom/items/multiple_interactions.xml'); $itemSession = new AssessmentItemSession($doc->getDocumentComponent(), new SessionManager()); $itemSession->beginItemSession(); $itemSession->beginAttempt(); $this->assertInstanceOf('qtism\\common\\datatypes\\Float', $itemSession['SCORE']); $this->assertEquals(0.0, $itemSession['SCORE']->getValue()); $responses = new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new Identifier('Choice_3')))); $itemSession->endAttempt($responses); $this->assertEquals(6.0, $itemSession['SCORE']->getValue()); }
/** * Write an AssessmetnItemSession from the current binary stream. * * @param \qtism\runtime\storage\common\AssessmentTestSeeker $seeker The AssessmentTestSeeker object from where the position of components will be pulled out. * @param \qtism\runtime\tests\AssessmentItemSession $session An AssessmentItemSession object. * @throws \qtism\runtime\storage\binary\QtiBinaryStreamAccessException */ public function writeAssessmentItemSession(AssessmentTestSeeker $seeker, AssessmentItemSession $session) { try { $this->writeShort($seeker->seekPosition($session->getAssessmentItem())); $this->writeTinyInt($session->getState()); $this->writeTinyInt($session->getNavigationMode()); $this->writeTinyInt($session->getSubmissionMode()); $this->writeBoolean($session->isAttempting()); $isItemSessionControlDefault = $session->getItemSessionControl()->isDefault(); if ($isItemSessionControlDefault === true) { $this->writeBoolean(false); } else { $this->writeBoolean(true); $this->writeShort($seeker->seekPosition($session->getItemSessionControl())); } $this->writeTinyInt($session['numAttempts']->getValue()); $this->writeDuration($session['duration']); $this->writeString($session['completionStatus']->getValue()); $timeReference = $session->getTimeReference(); if (is_null($timeReference) === true) { // Describe that we have no time reference for the session. $this->writeBoolean(false); } else { // Describe that we have a time reference for the session. $this->writeBoolean(true); // Write the time reference. $this->writeDateTime($timeReference); } // Write the session variables. // (minus the 3 built-in variables) $varCount = count($session) - 3; $this->writeTinyInt($varCount); $itemOutcomes = $session->getAssessmentItem()->getOutcomeDeclarations(); $itemResponses = $session->getAssessmentItem()->getResponseDeclarations(); $itemTemplates = $session->getAssessmentItem()->getTemplateDeclarations(); foreach ($session->getKeys() as $varId) { if (in_array($varId, array('numAttempts', 'duration', 'completionStatus')) === false) { $var = $session->getVariable($varId); if ($var instanceof OutcomeVariable) { $variableDeclaration = $itemOutcomes[$varId]; $variable = OutcomeVariable::createFromDataModel($variableDeclaration); $varNature = 0; } elseif ($var instanceof ResponseVariable) { $variableDeclaration = $itemResponses[$varId]; $variable = ResponseVariable::createFromDataModel($variableDeclaration); $varNature = 1; } elseif ($var instanceof TemplateVariable) { $variableDeclaration = $itemTemplates[$varId]; $variable = TemplateVariable::createFromDataModel($variableDeclaration); $varNature = 2; } try { $this->writeShort($varNature); $this->writeShort($seeker->seekPosition($variableDeclaration)); // If defaultValue or correct response is different from what's inside // the variable declaration, just write it. $hasDefaultValue = !Utils::equals($variable->getDefaultValue(), $var->getDefaultValue()); $hasCorrectResponse = false; if ($varNature === 1 && !Utils::equals($variable->getCorrectResponse(), $var->getCorrectResponse())) { $hasCorrectResponse = true; } $this->writeBoolean($hasDefaultValue); $this->writeBoolean($hasCorrectResponse); $this->writeVariableValue($var, self::RW_VALUE); if ($hasDefaultValue === true) { $this->writeVariableValue($var, self::RW_DEFAULTVALUE); } if ($hasCorrectResponse === true) { $this->writeVariableValue($var, self::RW_CORRECTRESPONSE); } } catch (OutOfBoundsException $e) { $msg = "No variable found in the assessmentTest tree structure."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } } } // Write shuffling states if any. $shufflingStates = $session->getShufflingStates(); $this->writeTinyInt(count($shufflingStates)); foreach ($shufflingStates as $shufflingState) { $this->writeShufflingState($shufflingState); } } catch (BinaryStreamAccessException $e) { $msg = "An error occured while writing an assessment item session."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } catch (OutOfBoundsException $e) { $msg = "No assessmentItemRef found in the assessmentTest tree structure."; throw new QtiBinaryStreamAccessException($msg, $this, QtiBinaryStreamAccessException::ITEM_SESSION, $e); } }
/** * Checks if an item has been completed * @param RouteItem $routeItem * @param AssessmentItemSession $itemSession * @return bool */ public static function isItemCompleted(RouteItem $routeItem, AssessmentItemSession $itemSession) { $completed = false; if ($routeItem->getTestPart()->getNavigationMode() === NavigationMode::LINEAR) { // In linear mode, we consider the item completed if it was presented. if ($itemSession->isPresented() === true) { $completed = true; } } else { // In nonlinear mode we consider: // - an adaptive item completed if it's completion status is 'completed'. // - a non-adaptive item to be completed if it is responded. $isAdaptive = $itemSession->getAssessmentItem()->isAdaptive(); if ($isAdaptive === true && $itemSession['completionStatus']->getValue() === AssessmentItemSession::COMPLETION_STATUS_COMPLETED) { $completed = true; } else { if ($isAdaptive === false && $itemSession->isResponded() === true) { $completed = true; } } } return $completed; }