216 lines
8.0 KiB
PHP
216 lines
8.0 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);
|
|
*
|
|
*
|
|
*/
|
|
|
|
namespace oat\taoQtiItem\controller;
|
|
|
|
use oat\taoQtiItem\helpers\QtiFile;
|
|
use oat\taoQtiItem\controller\AbstractQtiItemRunner;
|
|
use \core_kernel_classes_Resource;
|
|
use \common_Exception;
|
|
use \taoQtiCommon_helpers_PciVariableFiller;
|
|
use \taoQtiCommon_helpers_PciStateOutput;
|
|
use \taoQtiCommon_helpers_Utils;
|
|
use \common_Logger;
|
|
use \taoQtiCommon_helpers_ResultTransmissionException;
|
|
use \taoQtiCommon_helpers_ResultTransmitter;
|
|
use \taoResultServer_models_classes_ResultServerStateFull;
|
|
use qtism\runtime\common\State;
|
|
use qtism\runtime\tests\SessionManager;
|
|
use qtism\runtime\tests\AssessmentItemSession;
|
|
use qtism\runtime\tests\AssessmentItemSessionException;
|
|
use qtism\data\storage\StorageException;
|
|
use qtism\data\storage\xml\XmlDocument;
|
|
|
|
/**
|
|
* Qti Item Runner Controller
|
|
*
|
|
* @author CRP Henri Tudor - TAO Team - {@link http://www.tao.lu}
|
|
* @package taoQTI
|
|
|
|
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
|
|
*/
|
|
class QtiItemRunner extends AbstractQtiItemRunner
|
|
{
|
|
|
|
/**
|
|
* The implementation of this method calls ItemRunner::setView in order to
|
|
* select the view to be displayed.
|
|
*/
|
|
protected function selectView()
|
|
{
|
|
$this->setInitialVariableElements();
|
|
|
|
$this->setData('itemDataPath', $this->getRequestParameter('itemDataPath'));
|
|
$this->setView('runtime/qti_item_runner.tpl', 'taoQtiItem');
|
|
}
|
|
|
|
/**
|
|
* The endpoint specific to QTI Items
|
|
* @return string
|
|
*/
|
|
protected function getResultServerEndpoint()
|
|
{
|
|
return _url('', 'QtiItemRunner', 'taoQtiItem');
|
|
}
|
|
|
|
protected function getItemUri()
|
|
{
|
|
return $this->hasRequestParameter("itemId") ? $this->getRequestParameter("itemId") : '';
|
|
}
|
|
|
|
protected function getTestUri()
|
|
{
|
|
return $this->hasRequestParameter("testId") ? $this->getRequestParameter("testId") : '';
|
|
}
|
|
|
|
protected function getServiceCallId()
|
|
{
|
|
return $this->hasRequestParameter("serviceCallId") ? $this->getRequestParameter("serviceCallId") : '';
|
|
}
|
|
|
|
protected function getPostedTraces()
|
|
{
|
|
return $this->hasRequestParameter("traceVariables") ? $this->getRequestParameter("traceVariables") : [];
|
|
}
|
|
|
|
/**
|
|
* The main entry poin for respon evaluation
|
|
*
|
|
* @throws common_Exception
|
|
*/
|
|
public function submitResponses()
|
|
{
|
|
|
|
$success = false;
|
|
$itemUri = $this->getItemUri();
|
|
|
|
if (!empty($itemUri)) {
|
|
$this->processResponses(new core_kernel_classes_Resource($itemUri));
|
|
$success = true;
|
|
} else {
|
|
throw new common_Exception('missing required itemId');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Item's ResponseProcessing.
|
|
*
|
|
* @throws RuntimeException If an error occurs while processing responses or transmitting results
|
|
*/
|
|
protected function processResponses(core_kernel_classes_Resource $item)
|
|
{
|
|
|
|
$jsonPayload = taoQtiCommon_helpers_Utils::readJsonPayload();
|
|
|
|
try {
|
|
$qtiXmlFileContent = QtiFile::getQtiFileContent($item);
|
|
$qtiXmlDoc = new XmlDocument();
|
|
$qtiXmlDoc->loadFromString($qtiXmlFileContent);
|
|
} catch (StorageException $e) {
|
|
$msg = "An error occured while loading QTI-XML file at expected location '${qtiXmlFilePath}'.";
|
|
throw new \RuntimeException($msg, 0, $e);
|
|
}
|
|
|
|
$itemSession = new AssessmentItemSession($qtiXmlDoc->getDocumentComponent(), new SessionManager());
|
|
$itemSession->beginItemSession();
|
|
|
|
$variables = [];
|
|
|
|
// Convert client-side data as QtiSm Runtime Variables.
|
|
foreach ($jsonPayload as $identifier => $response) {
|
|
$filler = new taoQtiCommon_helpers_PciVariableFiller($qtiXmlDoc->getDocumentComponent());
|
|
|
|
try {
|
|
$var = $filler->fill($identifier, $response);
|
|
// Do not take into account QTI File placeholders.
|
|
if (taoQtiCommon_helpers_Utils::isQtiFilePlaceHolder($var) === false) {
|
|
$variables[] = $var;
|
|
}
|
|
} catch (\OutOfRangeException $e) {
|
|
// A variable value could not be converted, ignore it.
|
|
// Developer's note: QTI Pairs with a single identifier (missing second identifier of the pair) are transmitted as an array of length 1,
|
|
// this might cause problem. Such "broken" pairs are simply ignored.
|
|
common_Logger::d("Client-side value for variable '${identifier}' is ignored due to data malformation.");
|
|
} catch (\OutOfBoundsException $e) {
|
|
// The response identifier does not match any response declaration.
|
|
common_Logger::d("Uknown item variable declaration '${identifier}.");
|
|
}
|
|
}
|
|
|
|
try {
|
|
$itemSession->beginAttempt();
|
|
$itemSession->endAttempt(new State($variables));
|
|
|
|
// Transmit results to the Result Server.
|
|
$this->transmitResults($item, $itemSession);
|
|
|
|
// Return the item session state to the client-side.
|
|
echo json_encode([
|
|
'success' => true,
|
|
'displayFeedback' => true,
|
|
'itemSession' => self::buildOutcomeResponse($itemSession),
|
|
'feedbacks' => $this->getFeedbacks($itemSession)
|
|
]);
|
|
} catch (AssessmentItemSessionException $e) {
|
|
$msg = "An error occured while processing the responses.";
|
|
throw new \RuntimeException($msg, 0, $e);
|
|
} catch (taoQtiCommon_helpers_ResultTransmissionException $e) {
|
|
$msg = "An error occured while transmitting variable '${identifier}' to the target Result Server.";
|
|
throw new \RuntimeException($msg, 0, $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transmit the variables contained in the AssessmentTestSession $itemSession as
|
|
* item results to the Result Server.
|
|
*
|
|
* @param core_kernel_classes_Resource $item The item definition in database.
|
|
* @param AssessmentItemSession $itemSession The AssessmentItemSession objects from where the results must be extracted.
|
|
* @throws taoQtiCommon_helpers_ResultTransmissionException If an error occurs while transmitting results to the ResultServer.
|
|
*/
|
|
protected function transmitResults(core_kernel_classes_Resource $item, AssessmentItemSession $itemSession)
|
|
{
|
|
$resultTransmitter = new taoQtiCommon_helpers_ResultTransmitter(taoResultServer_models_classes_ResultServerStateFull::singleton());
|
|
|
|
foreach ($itemSession->getKeys() as $identifier) {
|
|
// QTI built-in variables not suitable for this standalone QTI item execution case.
|
|
if (!in_array($identifier, ['completionStatus', 'numAttempts', 'duration'])) {
|
|
// Transmit to Result Server.
|
|
$resultTransmitter->transmitItemVariable($itemSession->getVariable($identifier), $this->getServiceCallId(), $item->getUri());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static function buildOutcomeResponse(AssessmentItemSession $itemSession)
|
|
{
|
|
|
|
$stateOutput = new taoQtiCommon_helpers_PciStateOutput();
|
|
|
|
foreach ($itemSession->getAllVariables() as $var) {
|
|
$stateOutput->addVariable($var);
|
|
}
|
|
|
|
$output = $stateOutput->getOutput();
|
|
return $output;
|
|
}
|
|
}
|