tao-test/app/taoQtiTest/actions/class.TestRunner.php

980 lines
34 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*
*
*/
use oat\taoDelivery\model\execution\DeliveryServerService;
use qtism\runtime\tests\AssessmentTestSessionException;
use qtism\runtime\tests\AssessmentTestSessionState;
use qtism\runtime\tests\AssessmentTestSession;
use qtism\data\AssessmentTest;
use qtism\runtime\common\State;
use qtism\runtime\common\ResponseVariable;
use qtism\common\enums\BaseType;
use qtism\common\enums\Cardinality;
use qtism\common\datatypes\QtiString as QtismString;
use qtism\runtime\storage\binary\BinaryAssessmentTestSeeker;
use qtism\runtime\storage\common\AbstractStorage;
use qtism\data\SubmissionMode;
use qtism\data\NavigationMode;
use oat\taoQtiItem\helpers\QtiRunner;
use oat\taoQtiTest\models\TestSessionMetaData;
use oat\taoQtiTest\models\QtiTestCompilerIndex;
use oat\taoQtiTest\models\files\QtiFlysystemFileManager;
use oat\taoQtiTest\models\runner\StorageManager;
use oat\oatbox\service\ServiceManager;
use oat\taoQtiTest\models\CompilationDataService;
/**
* Runs a QTI Test.
*
* @author Joel Bout <joel@taotesting.com>
* @author Jérôme Bogaerts <jerome@taotesting.com>
* @package taoQtiTest
* @deprecated old testrunner is deprecated. use taoQtiTest_actions_Runner instead
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
*/
class taoQtiTest_actions_TestRunner extends tao_actions_ServiceModule
{
/**
* The current AssessmentTestSession object.
*
* @var AssessmentTestSession
*/
private $testSession = null;
/**
* The current AssessmentTest definition object.
*
* @var AssessmentTest
*/
private $testDefinition = null;
/**
* The current AbstractStorage object.
*
* @var AbstractStorage
*/
private $storage = null;
/**
* The error that occured during the current request.
*
*/
private $currentError = -1;
/**
* The compilation directory.
*
* @var string
*/
private $compilationDirectory;
/**
* The meta data about the test definition
* being executed.
*
* @var array
*/
private $testMeta;
/**
* The index of compiled items.
*
* @var QtiTestCompilerIndex
*/
private $itemIndex;
/**
* Testr session metadata manager
*
* @var TestSessionMetaData
*/
private $metaDataHandler;
/**
* Get the current assessment test session.
*
* @return AssessmentTestSession An AssessmentTestSession object.
*/
protected function getTestSession()
{
return $this->testSession;
}
/**
* Set the current assessment test session.
*
* @param AssessmentTestSession $testSession An AssessmentTestSession object.
*/
protected function setTestSession(AssessmentTestSession $testSession)
{
$this->testSession = $testSession;
}
/**
* Get the current test definition.
*
* @return AssessmentTest An AssessmentTest object.
*/
protected function getTestDefinition()
{
return $this->testDefinition;
}
/**
* Set the current test defintion.
*
* @param AssessmentTest $testDefinition An AssessmentTest object.
*/
protected function setTestDefinition(AssessmentTest $testDefinition)
{
$this->testDefinition = $testDefinition;
}
/**
* Get the QtiSm AssessmentTestSession Storage Service.
*
* @return AbstractStorage An AssessmentTestSession Storage Service.
*/
protected function getStorage()
{
return $this->storage;
}
/**
* Set the QtiSm AssessmentTestSession Storage Service.
*
* @param AbstractStorage $storage An AssessmentTestSession Storage Service.
*/
protected function setStorage(AbstractStorage $storage)
{
$this->storage = $storage;
}
/**
* Get the error that occured during the previous request.
*
* @return integer
*/
protected function getPreviousError()
{
return $this->getStorage()->getLastError();
}
/**
* Set the error that occured during the current request.
*
* @param integer $error
*/
protected function setCurrentError($currentError)
{
$this->currentError = $currentError;
}
/**
* Get the error that occured during the current request.
*
* @return integer
*/
protected function getCurrentError()
{
return $this->currentError;
}
/**
* Set the path to the directory where the test is compiled.
*
* @param string $compilationDirectory An absolute path.
*/
protected function setCompilationDirectory($compilationDirectory)
{
$this->compilationDirectory = $compilationDirectory;
}
/**
* Get the path to the directory where the test is compiled.
*
* @return tao_models_classes_service_StorageDirectory
*/
protected function getCompilationDirectory()
{
return $this->compilationDirectory;
}
/**
* Set the meta-data array about the test definition
* being executed.
*
* @param array $testMeta
*/
protected function setTestMeta(array $testMeta)
{
$this->testMeta = $testMeta;
}
/**
* Get the meta-data array about the test definition
* being executed.
*
* @return array
*/
protected function getTestMeta()
{
return $this->testMeta;
}
/**
* @return QtiTestCompilerIndex
*/
protected function getItemIndex()
{
return $this->itemIndex;
}
/**
* @param QtiTestCompilerIndex $itemIndex
* @return taoQtiTest_actions_TestRunner
*/
protected function setItemIndex($itemIndex)
{
$this->itemIndex = $itemIndex;
return $this;
}
/**
* Print an error report into the response.
* After you have called this method, you must prevent other actions to be processed and must close the response.
* @param string $message
* @param int $code
*/
protected function notifyError($message, $code = 0)
{
$ctx = [
'success' => false,
'state' => $this->getTestSession()->getState(),
'message' => $message,
'code' => $code,
];
$this->setData('assessmentTestContext', $ctx);
if (\tao_helpers_Request::isAjax()) {
$this->returnJson($ctx);
}
}
/**
* Common stuff processessed on almost all actions.
* If something goes wrong, print a report and return false, otherwise return true.
* @param bool $notifyError Allow to print error message if needed
* @return bool Returns a flag telling whether or not the action can be processed
* @throws \common_Exception
* @throws \InvalidArgumentException
* @throws common_exception_Error
* @throws common_exception_InconsistentData
* @throws common_ext_ExtensionException
*/
protected function beforeAction($notifyError = true)
{
// Controller initialization.
$this->retrieveTestDefinition($this->getRequestParameter('QtiTestCompilation'));
/** @var DeliveryServerService $deliveryServerService */
$deliveryServerService = $this->getServiceManager()->get(DeliveryServerService::SERVICE_ID);
$resultStore = $deliveryServerService->getResultStoreWrapper($this->getRequestParameter('serviceCallId'));
// Initialize storage and test session.
$testResource = new core_kernel_classes_Resource($this->getRequestParameter('QtiTestDefinition'));
$sessionManager = new taoQtiTest_helpers_SessionManager($resultStore, $testResource);
$userUri = common_session_SessionManager::getSession()->getUserUri();
$seeker = new BinaryAssessmentTestSeeker($this->getTestDefinition());
$config = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner');
$storageClassName = $config['test-session-storage'];
$this->setStorage(new $storageClassName($sessionManager, $seeker, $userUri));
$this->retrieveTestSession();
// @TODO: use some storage to get the potential reason of the state (close/suspended)
$session = $this->getTestSession();
$state = $session->getState();
if ($state == AssessmentTestSessionState::CLOSED) {
if ($notifyError) {
$this->notifyError(__('The assessment has been terminated. You cannot interact with it anymore.'), $state);
}
return false;
}
// @TODO: maybe use an option to enable this behavior
if ($state == AssessmentTestSessionState::SUSPENDED) {
if ($notifyError) {
$this->notifyError(__('The assessment has been suspended. To resume your assessment, please relaunch it and contact your proctor if required.'), $state);
}
return false;
}
$sessionStateService = $this->getServiceManager()->get('taoQtiTest/SessionStateService');
$sessionStateService->resumeSession($session);
$this->retrieveTestMeta();
$this->retrieveItemIndex();
// Prevent anything to be cached by the client.
taoQtiTest_helpers_TestRunnerUtils::noHttpClientCache();
$metaData = $this->getMetaDataHandler()->getData();
if (!empty($metaData)) {
$this->getMetaDataHandler()->save($metaData);
}
return true;
}
/**
* Get instance og session metadata handler
*
* @return TestSessionMetaData
*/
protected function getMetaDataHandler()
{
if ($this->metaDataHandler === null) {
$this->metaDataHandler = new TestSessionMetaData($this->getTestSession());
}
return $this->metaDataHandler;
}
/**
* Does some complementary stuff to finish the action. Builds the test context object and binds it to the response.
* @param bool $withContext
* @throws \qtism\runtime\storage\common\StorageException
*/
protected function afterAction($withContext = true)
{
$testSession = $this->getTestSession();
$sessionId = $testSession->getSessionId();
// Build assessment test context.
$ctx = taoQtiTest_helpers_TestRunnerUtils::buildAssessmentTestContext(
$this->getTestSession(),
$this->getTestMeta(),
$this->getItemIndex(),
$this->getRequestParameter('QtiTestDefinition'),
$this->getRequestParameter('QtiTestCompilation'),
$this->getRequestParameter('standalone'),
$this->getCompilationDirectory()
);
// add a flag to allow distinction with error responses
$ctx['success'] = true;
// Put the assessment test context in request data.
$this->setData('assessmentTestContext', $ctx);
if ($withContext === true) {
// Output only if requested by client-code.
echo json_encode($ctx);
}
common_Logger::t("Persisting QTI Assessment Test Session '${sessionId}'...");
$this->getStorage()->persist($testSession);
$this->getServiceManager()->get(StorageManager::SERVICE_ID)->persist();
}
/**
* Main action of the TestRunner module.
*
*/
public function index()
{
$config = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner');
$noError = $this->beforeAction();
// this part is only accessible if beforeAction did not return an error
if ($noError) {
$session = $this->getTestSession();
/** @var \oat\taoQtiTest\models\SessionStateService $sessionStateService */
$sessionStateService = $this->getServiceManager()->get('taoQtiTest/SessionStateService');
$resetTimerAfterResume = isset($config['reset-timer-after-resume']) && $config['reset-timer-after-resume'];
if ($resetTimerAfterResume) {
$sessionStateService->updateTimeReference($session);
}
$this->setData(
'client_session_state_service',
$sessionStateService->getClientImplementation($resetTimerAfterResume)
);
if ($session->getState() === AssessmentTestSessionState::INITIAL) {
// The test has just been instantiated.
$session->beginTestSession();
common_Logger::i("Assessment Test Session begun.");
}
if (taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
}
// loads the specific config
// this part must be processed no matter if beforeAction returned an error:
// the context object is provided through the view
$this->setData('review_screen', !empty($config['test-taker-review']));
$this->setData('review_region', isset($config['test-taker-review-region']) ? $config['test-taker-review-region'] : '');
$this->setData('client_config_url', $this->getClientConfigUrl());
$this->setData('client_timeout', $this->getClientTimeout());
$this->setView('test_runner.tpl');
// this part is only accessible if beforeAction did not return an error
if ($noError) {
$this->afterAction(false);
}
}
/**
* Keep item activity time up to date
* @throws \oat\oatbox\service\ServiceNotFoundException
* @throws common_Exception
* @throws common_ext_ExtensionException
* @throws \qtism\runtime\storage\common\StorageException
*/
public function keepItemTimed()
{
if ($this->beforeAction()) {
$config = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner');
if (isset($config['reset-timer-after-resume']) && $config['reset-timer-after-resume'] && $this->hasRequestParameter('duration')) {
$session = $this->getTestSession();
// originally in milliseconds, but we have to convert to seconds now
$durationInSeconds = (int) ($this->getRequestParameter('duration') / 1000);
$time = new \DateTime('now', new \DateTimeZone('UTC'));
$duration = new DateInterval('PT' . $durationInSeconds . 'S');
$time->sub($duration);
/** @var \oat\taoQtiTest\models\SessionStateService $sessionStateService */
$sessionStateService = $this->getServiceManager()->get('taoQtiTest/SessionStateService');
$sessionStateService->updateTimeReference($session, $time);
$this->afterAction();
}
}
}
/**
* Mark an item for review in the Assessment Test Session flow.
*
*/
public function markForReview()
{
if ($this->beforeAction()) {
$testSession = $this->getTestSession();
$sessionId = $testSession->getSessionId();
try {
if ($this->hasRequestParameter('position')) {
$itemPosition = intval($this->getRequestParameter('position'));
} else {
$itemPosition = $testSession->getRoute()->getPosition();
}
if ($this->hasRequestParameter('flag')) {
$flag = $this->getRequestParameter('flag');
if (is_numeric($flag)) {
$flag = !!(intval($flag));
} else {
$flag = 'false' != strtolower($flag);
}
} else {
$flag = true;
}
taoQtiTest_helpers_TestRunnerUtils::setItemFlag($testSession, $itemPosition, $flag);
$this->returnJson([
'success' => true,
'position' => $itemPosition,
'flag' => $flag
]);
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
common_Logger::t("Persisting QTI Assessment Test Session '${sessionId}'...");
$this->getStorage()->persist($testSession);
}
}
/**
* Jump to an item in the Assessment Test Session flow.
*
*/
public function jumpTo()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
$nextPosition = intval($this->getRequestParameter('position'));
try {
$this->endTimedSection($nextPosition);
$session->jumpTo($nextPosition);
if ($session->isRunning() === true && taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction();
}
}
protected function endTimedSection($nextPosition)
{
$config = \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest')->getConfig('testRunner');
if (empty($config['keep-timer-up-to-timeout'])) {
$isJumpOutOfSection = false;
$session = $this->getTestSession();
$section = $session->getCurrentAssessmentSection();
$route = $session->getRoute();
if (($nextPosition >= 0) && ($nextPosition < $route->count())) {
$nextSection = $route->getRouteItemAt($nextPosition);
$isJumpOutOfSection = ($section->getIdentifier() !== $nextSection->getAssessmentSection()->getIdentifier());
}
$limits = $section->getTimeLimits();
//ensure that jumping out and section is timed
if ($isJumpOutOfSection && $limits != null && $limits->hasMaxTime()) {
$components = $section->getComponents();
foreach ($components as $object) {
if ($object instanceof \qtism\data\ExtendedAssessmentItemRef) {
$items = $session->getAssessmentItemSessions($object->getIdentifier());
foreach ($items as $item) {
if ($item instanceof \qtism\runtime\tests\AssessmentItemSession) {
$item->endItemSession();
}
}
}
}
}
}
}
/**
* Move forward in the Assessment Test Session flow.
*
*/
public function moveForward()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
$nextPosition = $session->getRoute()->getPosition() + 1;
try {
$this->endTimedSection($nextPosition);
$session->moveNext();
if ($session->isRunning() === true && taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction();
}
}
/**
* Move backward in the Assessment Test Session flow.
*
*/
public function moveBackward()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
$nextPosition = $session->getRoute()->getPosition() - 1;
try {
$this->endTimedSection($nextPosition);
$session->moveBack();
if (taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction();
}
}
/**
* Moves to the next available section in the Assessment Test Session flow.
*
*/
public function nextSection()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
try {
$session->moveNextAssessmentSection();
if ($session->isRunning() === true && taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction();
}
}
/**
* Skip the current item in the Assessment Test Session flow.
*
*/
public function skip()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
try {
$session->skip();
$session->moveNext();
if ($session->isRunning() === true && taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction();
}
}
/**
* Action to call when a structural QTI component times out in linear mode.
*
*/
public function timeout()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
try {
$session->checkTimeLimits(false, true, false);
} catch (AssessmentTestSessionException $e) {
$this->onTimeout($e);
}
// If we are here, without executing onTimeout() there is an inconsistency. Simply respond
// to the client with the actual assessment test context. Maybe the client will be able to
// continue...
$this->afterAction();
}
}
/**
* Action to end test session
*/
public function endTestSession()
{
if ($this->beforeAction()) {
$session = $this->getTestSession();
$sessionId = $session->getSessionId();
common_Logger::i("The user has requested termination of the test session '{$sessionId}'");
$session->endTestSession();
$this->afterAction();
}
}
/**
* Stuff to be undertaken when the Assessment Item presented to the candidate
* times out.
*
* @param AssessmentTestSessionException $timeOutException The AssessmentTestSessionException object thrown to indicate the timeout.
*/
protected function onTimeout(AssessmentTestSessionException $timeOutException)
{
$session = $this->getTestSession();
if ($session->getCurrentNavigationMode() === NavigationMode::LINEAR) {
switch ($timeOutException->getCode()) {
case AssessmentTestSessionException::ASSESSMENT_TEST_DURATION_OVERFLOW:
$session->endTestSession();
break;
case AssessmentTestSessionException::TEST_PART_DURATION_OVERFLOW:
$session->moveNextTestPart();
break;
case AssessmentTestSessionException::ASSESSMENT_SECTION_DURATION_OVERFLOW:
$session->moveNextAssessmentSection();
break;
case AssessmentTestSessionException::ASSESSMENT_ITEM_DURATION_OVERFLOW:
$session->moveNextAssessmentItem();
break;
}
if ($session->isRunning() === true && taoQtiTest_helpers_TestRunnerUtils::isTimeout($session) === false) {
taoQtiTest_helpers_TestRunnerUtils::beginCandidateInteraction($session);
}
} else {
$itemSession = $session->getCurrentAssessmentItemSession();
$itemSession->endItemSession();
}
}
/**
* Action called when a QTI Item embedded in a QTI Test submit responses.
*
*/
public function storeItemVariableSet()
{
if ($this->beforeAction()) {
// --- Deal with provided responses.
$jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
$responses = new State();
$currentItem = $this->getTestSession()->getCurrentAssessmentItemRef();
$currentOccurence = $this->getTestSession()->getCurrentAssessmentItemRefOccurence();
if ($currentItem === false) {
$msg = "Trying to store item variables but the state of the test session is INITIAL or CLOSED.\n";
$msg .= "Session state value: " . $this->getTestSession()->getState() . "\n";
$msg .= "Session ID: " . $this->getTestSession()->getSessionId() . "\n";
$msg .= "JSON Payload: " . mb_substr(json_encode($jsonPayload), 0, 1000);
common_Logger::e($msg);
}
$filler = new taoQtiCommon_helpers_PciVariableFiller(
$currentItem,
ServiceManager::getServiceManager()->get(QtiFlysystemFileManager::SERVICE_ID)
);
if (is_array($jsonPayload)) {
foreach ($jsonPayload as $id => $response) {
try {
$var = $filler->fill($id, $response);
// Do not take into account QTI File placeholders.
if (taoQtiCommon_helpers_Utils::isQtiFilePlaceHolder($var) === false) {
$responses->setVariable($var);
}
} catch (OutOfRangeException $e) {
common_Logger::d("Could not convert client-side value for variable '${id}'.");
} catch (OutOfBoundsException $e) {
common_Logger::d("Could not find variable with identifier '${id}' in current item.");
}
}
} else {
common_Logger::e('Invalid json payload');
}
$displayFeedback = $this->getTestSession()->getCurrentSubmissionMode() !== SubmissionMode::SIMULTANEOUS;
$stateOutput = new taoQtiCommon_helpers_PciStateOutput();
try {
common_Logger::t('Responses sent from the client-side. The Response Processing will take place.');
$this->getTestSession()->endAttempt($responses, true);
// Return the item session state to the client side.
$itemSession = $this->getTestSession()->getAssessmentItemSessionStore()->getAssessmentItemSession($currentItem, $currentOccurence);
foreach ($itemSession->getAllVariables() as $var) {
$stateOutput->addVariable($var);
}
$itemCompilationDirectory = $this->getDirectory($this->getRequestParameter('itemDataPath'));
$jsonReturn = ['success' => true,
'displayFeedback' => $displayFeedback,
'itemSession' => $stateOutput->getOutput(),
'feedbacks' => []];
if ($displayFeedback === true) {
$jsonReturn['feedbacks'] = QtiRunner::getFeedbacks($itemCompilationDirectory, $itemSession);
}
echo json_encode($jsonReturn);
} catch (AssessmentTestSessionException $e) {
$this->handleAssessmentTestSessionException($e);
}
$this->afterAction(false);
}
}
/**
* Action to call to comment an item.
*
*/
public function comment()
{
if ($this->beforeAction()) {
$testSession = $this->getTestSession();
// prepare transmission Id for result server.
$item = $testSession->getCurrentAssessmentItemRef()->getIdentifier();
$occurence = $testSession->getCurrentAssessmentItemRefOccurence();
$sessionId = $testSession->getSessionId();
$transmissionId = "${sessionId}.${item}.${occurence}";
// retrieve comment's intrinsic value.
$comment = $this->getRequestParameter('comment');
/** @var DeliveryServerService $deliveryServerService */
$deliveryServerService = $this->getServiceManager()->get(DeliveryServerService::SERVICE_ID);
$resultStore = $deliveryServerService->getResultStoreWrapper($sessionId);
// build variable and send it.
$itemUri = taoQtiTest_helpers_TestRunnerUtils::getCurrentItemUri($testSession);
$testUri = $testSession->getTest()->getUri();
$variable = new ResponseVariable('comment', Cardinality::SINGLE, BaseType::STRING, new QtismString($comment));
$transmitter = new taoQtiCommon_helpers_ResultTransmitter($resultStore);
$transmitter->transmitItemVariable($variable, $transmissionId, $itemUri, $testUri);
}
}
/**
* Retrieve the Test Definition the test session is built
* from as an AssessmentTest object. This method
* also retrieves the compilation directory.
*
* @param string $qtiTestCompilation (e.g. <i>'http://sample/first.rdf#i14363448108243883-|http://sample/first.rdf#i14363448109065884+'</i>)
*
* @return AssessmentTest The AssessmentTest object the current test session is built from.
*/
protected function retrieveTestDefinition($qtiTestCompilation)
{
$directoryIds = explode('|', $qtiTestCompilation);
$directories = [
'private' => $this->getDirectory($directoryIds[0]),
'public' => $this->getDirectory($directoryIds[1])
];
$this->setCompilationDirectory($directories);
$testDefinition = \taoQtiTest_helpers_Utils::getTestDefinition($qtiTestCompilation);
$this->setTestDefinition($testDefinition);
}
/**
* Retrieve the current test session as an AssessmentTestSession object from
* persistent storage.
*
*/
protected function retrieveTestSession()
{
$qtiStorage = $this->getStorage();
$sessionId = $this->getServiceCallId();
if ($qtiStorage->exists($sessionId) === false) {
common_Logger::t("Instantiating QTI Assessment Test Session");
$this->setTestSession($qtiStorage->instantiate($this->getTestDefinition(), $sessionId));
$testTaker = \common_session_SessionManager::getSession()->getUser();
taoQtiTest_helpers_TestRunnerUtils::setInitialOutcomes($this->getTestSession(), $testTaker);
} else {
common_Logger::t("Retrieving QTI Assessment Test Session '${sessionId}'...");
$this->setTestSession($qtiStorage->retrieve($this->getTestDefinition(), $sessionId));
}
taoQtiTest_helpers_TestRunnerUtils::preserveOutcomes($this->getTestSession());
}
/**
* Retrieve the QTI Test Definition meta-data array stored
* into the private compilation directory.
*
* @return array
* @throws common_exception_InconsistentData
*/
protected function retrieveTestMeta()
{
$directories = $this->getCompilationDirectory();
/** @var tao_models_classes_service_StorageDirectory $privateDirectory */
$privateDirectory = $directories['private'];
/** @var CompilationDataService $compilationDataService */
$compilationDataService = $this->getServiceLocator()->get(CompilationDataService::SERVICE_ID);
$this->setTestMeta($compilationDataService->readCompilationMetadata($privateDirectory));
}
/**
* Retrieves the index of compiled items.
*/
protected function retrieveItemIndex()
{
$this->setItemIndex(new QtiTestCompilerIndex());
try {
$directories = $this->getCompilationDirectory();
/** @var tao_models_classes_service_StorageDirectory $privateDirectory */
$privateDirectory = $directories['private'];
$data = $privateDirectory->read(taoQtiTest_models_classes_QtiTestService::TEST_COMPILED_INDEX);
if ($data) {
$this->getItemIndex()->unserialize($data);
}
} catch (\Exception $e) {
\common_Logger::d('Ignoring file not found exception for Items Index');
}
}
protected function handleAssessmentTestSessionException(AssessmentTestSessionException $e)
{
switch ($e->getCode()) {
case AssessmentTestSessionException::ASSESSMENT_TEST_DURATION_OVERFLOW:
case AssessmentTestSessionException::TEST_PART_DURATION_OVERFLOW:
case AssessmentTestSessionException::ASSESSMENT_SECTION_DURATION_OVERFLOW:
case AssessmentTestSessionException::ASSESSMENT_ITEM_DURATION_OVERFLOW:
$this->onTimeout($e);
break;
default:
$msg = "Non managed QTI Test exception caught:\n";
do {
$msg .= "[" . get_class($e) . "] " . $e->getMessage() . "\n";
} while ($e = $e->getPrevious());
common_Logger::e($msg);
break;
}
}
}