/** * Apply the current SetCorrectResponse rule on the current state. * * A RuleProcessingException will be thrown if: * * * No variable corresponds to the given identifier in the current state. * * The target variable is not a ResponseVariable. * * The baseType and/or cardinality of the value to be set does not correspond to the baseType and/or cardinality of the target variable. * * An error occurs while processing the expression representing the value to be set. * * @throws \qtism\runtime\rules\RuleProcessingException */ public function process() { $rule = $this->getRule(); $state = $this->getState(); $variableIdentifier = $rule->getIdentifier(); $var = $state->getVariable($variableIdentifier); if (is_null($var) === true) { $msg = "No variable with identifier '{$variableIdentifier}' to be set in the current state."; throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE); } elseif (!$var instanceof ResponseVariable) { $msg = "The variable to set '{$variableIdentifier}' is not an instance of 'ResponseVariable'."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE); } try { $expressionEngine = new ExpressionEngine($rule->getExpression(), $state); $val = $expressionEngine->process(); $var->setCorrectResponse($val); } catch (InvalidArgumentException $e) { $varBaseType = BaseType::getNameByConstant($var->getBaseType()) === false ? 'noBaseType' : BaseType::getNameByConstant($var->getBaseType()); $varCardinality = Cardinality::getNameByConstant($var->getCardinality()); // The affected value does not match the baseType of the variable $var. $msg = "Unable to set value {$val} to variable '{$variableIdentifier}' (cardinality = {$varCardinality}, baseType = {$varBaseType})."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_BASETYPE, $e); } catch (ExpressionProcessingException $e) { $msg = "An error occured while processing the expression bound with the 'SetCorrectResponse' rule."; throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e); } }
/** * Checks whether or not $value: * * * is an instance of OutcomeVariable * * has a 'duration' QTI baseType. * * has 'single' QTI cardinality. * * @throws \InvalidArgumentException If one or more of the conditions above are not respected. */ protected function checkType($value) { parent::checkType($value); if (!$value instanceof OutcomeVariable) { $className = get_class($value); $msg = "The DurationStore only aims at storing OutcomeVariable objects, {$className} object given."; throw new InvalidArgumentException($msg); } if (($bt = $value->getBaseType()) !== BaseType::DURATION) { $baseTypeName = BaseType::getNameByConstant($bt); $msg = "The DurationStore only aims at storing OutcomeVariable objects with a 'duration' baseType, "; $msg .= "'{$baseTypeName}' baseType given "; $id = $value->getIdentifier(); $msg .= "for variable '{$id}'."; throw new InvalidArgumentException($msg); } if (($bt = $value->getCardinality()) !== Cardinality::SINGLE) { $cardinalityName = Cardinality::getNameByConstant($bt); $msg = "The DurationStore only aims at storing OutcomeVariable objects with a 'single' cardinality, "; $msg .= "'{$cardinalityName}' cardinality given "; $id = $value->getIdentifier(); $msg .= "for variable '{$id}'."; throw new InvalidArgumentException($msg); } }
/** * Marshall a VariableDeclaration object into a DOMElement object. * * @param \qtism\data\QtiComponent $component An OutcomeDeclaration object. * @return \DOMElement The according DOMElement object. */ protected function marshall(QtiComponent $component) { $element = static::getDOMCradle()->createElement($component->getQtiClassName()); self::setDOMElementAttribute($element, 'identifier', $component->getIdentifier()); self::setDOMElementAttribute($element, 'cardinality', Cardinality::getNameByConstant($component->getCardinality())); if ($component->getBaseType() != -1) { self::setDOMElementAttribute($element, 'baseType', BaseType::getNameByConstant($component->getBaseType())); } // deal with default value. if ($component->getDefaultValue() != null) { $defaultValue = $component->getDefaultValue(); $defaultValueMarshaller = $this->getMarshallerFactory()->createMarshaller($defaultValue, array($component->getBaseType())); $element->appendChild($defaultValueMarshaller->marshall($defaultValue)); } return $element; }
/** * Transmit a test-level QtiSm Runtime Variable to the target Result Server as a test result. * * @param mixed $variable An OutcomeVariable object to be transmitted to the target Result Server. * @param string $transmissionId A unique identifier that identifies uniquely the visited test. * @param string $testUri An optional URL that identifies uniquely the test the $variable comes from. */ public function transmitTestVariable($variable, $transmissionId, $testUri = '') { $resultVariable = new taoResultServer_models_classes_OutcomeVariable(); $resultVariable->setIdentifier($variable->getIdentifier()); $resultVariable->setBaseType(BaseType::getNameByConstant($variable->getBaseType())); $resultVariable->setCardinality(Cardinality::getNameByConstant($variable->getCardinality())); $value = $variable->getValue(); $resultVariable->setValue(self::transformValue($value)); try { $this->getResultServer()->storeTestVariable($testUri, $resultVariable, $transmissionId); } catch (Exception $e) { $msg = "An error occured while transmitting a Response Variable to the target result server."; $code = taoQtiCommon_helpers_ResultTransmissionException::OUTCOME; throw new taoQtiCommon_helpers_ResultTransmissionException($msg, $code); } }
/** * Process the setOutcomeValue/setTemplateValue rule. * * A RuleProcessingException will be thrown if: * * * The variable does not exist. * * The requested variable is not an OutcomeVariable/TemplateVariable. * * The variable's baseType does not match the baseType of the affected value. * * An error occurs while processing the related expression. * * @throws \qtism\runtime\rules\RuleProcessingException If one of the error described above arise. */ public function process() { $state = $this->getState(); $rule = $this->getRule(); $identifier = $rule->getIdentifier(); $var = $state->getVariable($identifier); if (is_null($var) === true) { $msg = "No variable with identifier '{$identifier}' to be set in the current state."; throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE); } elseif (Reflection::isInstanceOf($var, $this->getVariableType()) === false) { $msg = "The variable to set '{$identifier}' is not an instance of '" . $this->getVariableType() . "'."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE); } // Process the expression. // Its result will be the value to set to the target variable. try { $expressionEngine = new ExpressionEngine($rule->getExpression(), $state); $val = $expressionEngine->process(); } catch (ExpressionProcessingException $e) { $msg = "An error occured while processing the expression bound with the '" . Reflection::shortClassName($rule) . "' rule."; throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e); } // The variable exists, its new value is processed. try { // juggle a little bit (int -> float, float -> int) if ($val !== null && $var->getCardinality() === Cardinality::SINGLE) { $baseType = $var->getBaseType(); if ($baseType === BaseType::INTEGER && $val instanceof Float) { $val = new Integer(intval($val->getValue())); } elseif ($baseType === BaseType::FLOAT && $val instanceof Integer) { $val = new Float(floatval($val->getValue())); } } $var->setValue($val); } catch (InvalidArgumentException $e) { $varBaseType = BaseType::getNameByConstant($var->getBaseType()) === false ? 'noBaseType' : BaseType::getNameByConstant($var->getBaseType()); $varCardinality = Cardinality::getNameByConstant($var->getCardinality()); // The affected value does not match the baseType of the variable $var. $msg = "Unable to set value {$val} to variable '{$identifier}' (cardinality = {$varCardinality}, baseType = {$varBaseType})."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_BASETYPE, $e); } }
/** * Create a QTI Runtime value from Data Model ValueCollection * * @param \qtism\data\state\ValueCollection $valueCollection A collection of qtism\data\state\Value objects. * @param integer $baseType The baseType the Value objects in the ValueCollection must respect. * @param integer $cardinality The cardinality the Value objects in the ValueCollection must respect. * @throws \UnexpectedValueException If $baseType or/and $cardinality are not respected by the Value objects in the ValueCollection. * @return mixed The resulting QTI Runtime value (primitive or container depending on baseType/cardinality). */ protected static function dataModelValuesToRuntime(ValueCollection $valueCollection, $baseType, $cardinality) { // Cardinality? // -> Single? Multiple? Ordered? Record? if ($cardinality === Cardinality::SINGLE) { // We should find a single value in the DefaultValue's values. if (count($valueCollection) == 1) { $dataModelValue = RuntimeUtils::valueToRuntime($valueCollection[0]->getValue(), $baseType); return $dataModelValue; } else { // The Data Model is in an inconsistent state. // This should be handled by the Data Model but // I prefer to be defensive. $msg = "A Data Model VariableDeclaration with 'single' cardinality must contain a single value, "; $msg .= count($valueCollection) . " value(s) found."; throw new UnexpectedValueException($msg); } } else { // Multiple|Ordered|Record, use a container. $container = null; try { // Create the appropriate Container object. $className = ucfirst(Cardinality::getNameByConstant($cardinality)) . 'Container'; $nsClassName = 'qtism\\runtime\\common\\' . $className; $callback = array($nsClassName, 'createFromDataModel'); $container = call_user_func_array($callback, array($valueCollection, $baseType)); return $container; // return container. } catch (InvalidArgumentException $e) { $msg = "The default value found in the Data Model Variable Declaration is not consistent. "; $msg .= "The values must have a baseType compliant with the baseType of the VariableDeclaration."; $msg .= "If the VariableDeclaration's cardinality is 'record', make sure the values it contains have "; $msg .= "fieldIdentifiers."; throw new UnexpectedValueException($msg, 0, $e); } } }
/** * Get trace variable instance to save. * * @param string $identifier * @param string $value * @return \taoResultServer_models_classes_TraceVariable variable instance to save. */ private function getVariable($identifier, $value) { $metaVariable = new \taoResultServer_models_classes_TraceVariable(); $metaVariable->setIdentifier($identifier); $metaVariable->setBaseType('string'); $metaVariable->setCardinality(Cardinality::getNameByConstant(Cardinality::SINGLE)); $metaVariable->setTrace($value); return $metaVariable; }
public function getQtiResultXml($deliveryId, $resultId) { $delivery = new \core_kernel_classes_Resource($deliveryId); $resultService = $this->getServiceLocator()->get(ResultServerService::SERVICE_ID); $resultServer = $resultService->getResultStorage($deliveryId); $crudService = new CrudResultsService(); $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $itemResults = $crudService->format($resultServer, $resultId, CrudResultsService::GROUP_BY_ITEM); $testResults = $crudService->format($resultServer, $resultId, CrudResultsService::GROUP_BY_TEST); $assessmentResultElt = $dom->createElementNS(self::QTI_NS, 'assessmentResult'); $dom->appendChild($assessmentResultElt); /** Context */ $contextElt = $dom->createElementNS(self::QTI_NS, 'context'); $contextElt->setAttribute('sourcedId', \tao_helpers_Uri::getUniqueId($resultServer->getTestTaker($resultId))); $assessmentResultElt->appendChild($contextElt); /** Test Result */ foreach ($testResults as $testResultIdentifier => $testResult) { $identifierParts = explode('.', $testResultIdentifier); $testIdentifier = array_pop($identifierParts); $testResultElement = $dom->createElementNS(self::QTI_NS, 'testResult'); $testResultElement->setAttribute('identifier', $testIdentifier); $testResultElement->setAttribute('datestamp', \tao_helpers_Date::displayeDate($testResult[0]['epoch'], \tao_helpers_Date::FORMAT_ISO8601)); /** Item Variable */ foreach ($testResult as $itemVariable) { $isResponseVariable = $itemVariable['type']->getUri() === 'http://www.tao.lu/Ontologies/TAOResult.rdf#ResponseVariable'; $testVariableElement = $dom->createElementNS(self::QTI_NS, $isResponseVariable ? 'responseVariable' : 'outcomeVariable'); $testVariableElement->setAttribute('identifier', $itemVariable['identifier']); $testVariableElement->setAttribute('cardinality', $itemVariable['cardinality']); $testVariableElement->setAttribute('baseType', $itemVariable['basetype']); $valueElement = $this->createCDATANode($dom, 'value', trim($itemVariable['value'])); if ($isResponseVariable) { $candidateResponseElement = $dom->createElementNS(self::QTI_NS, 'candidateResponse'); $candidateResponseElement->appendChild($valueElement); $testVariableElement->appendChild($candidateResponseElement); } else { $testVariableElement->appendChild($valueElement); } $testResultElement->appendChild($testVariableElement); } $assessmentResultElt->appendChild($testResultElement); } /** Item Result */ foreach ($itemResults as $itemResultIdentifier => $itemResult) { // Retrieve identifier. $identifierParts = explode('.', $itemResultIdentifier); $occurenceNumber = array_pop($identifierParts); $refIdentifier = array_pop($identifierParts); $itemElement = $dom->createElementNS(self::QTI_NS, 'itemResult'); $itemElement->setAttribute('identifier', $refIdentifier); $itemElement->setAttribute('datestamp', \tao_helpers_Date::displayeDate($itemResult[0]['epoch'], \tao_helpers_Date::FORMAT_ISO8601)); $itemElement->setAttribute('sessionStatus', 'final'); /** Item variables */ foreach ($itemResult as $key => $itemVariable) { $isResponseVariable = $itemVariable['type']->getUri() === 'http://www.tao.lu/Ontologies/TAOResult.rdf#ResponseVariable'; if ($itemVariable['identifier'] == 'comment') { /** Comment */ $itemVariableElement = $dom->createElementNS(self::QTI_NS, 'candidateComment', $itemVariable['value']); } else { /** Item variable */ $itemVariableElement = $dom->createElementNS(self::QTI_NS, $isResponseVariable ? 'responseVariable' : 'outcomeVariable'); $itemVariableElement->setAttribute('identifier', $itemVariable['identifier']); $itemVariableElement->setAttribute('cardinality', $itemVariable['cardinality']); $itemVariableElement->setAttribute('baseType', $itemVariable['basetype']); /** Split multiple response */ $itemVariable['value'] = trim($itemVariable['value'], '[]'); if ($itemVariable['cardinality'] !== Cardinality::getNameByConstant(Cardinality::SINGLE)) { $values = explode(';', $itemVariable['value']); $returnValue = []; foreach ($values as $value) { $returnValue[] = $this->createCDATANode($dom, 'value', $value); } } else { $returnValue = $this->createCDATANode($dom, 'value', $itemVariable['value']); } /** Get response parent element */ if ($isResponseVariable) { /** Response variable */ $responseElement = $dom->createElementNS(self::QTI_NS, 'candidateResponse'); } else { /** Outcome variable */ $responseElement = $itemVariableElement; } /** Write a response node foreach answer */ if (is_array($returnValue)) { foreach ($returnValue as $valueElement) { $responseElement->appendChild($valueElement); } } else { $responseElement->appendChild($returnValue); } if ($isResponseVariable) { $itemVariableElement->appendChild($responseElement); } } $itemElement->appendChild($itemVariableElement); } $assessmentResultElt->appendChild($itemElement); } return $dom->saveXML(); }