/** * @dataProvider sequencedIdentifiersProvider * * @param string $identifier * @param string $expectedPrefix * @param string $expectedSequence * @param string $expectedVariableName */ public function testSequencedIdentifiers($identifier, $expectedPrefix, $expectedSequence, $expectedVariableName) { $v = new VariableIdentifier($identifier); $this->assertEquals($identifier, $v->getIdentifier()); $this->assertTrue($v->hasPrefix()); $this->assertTrue($v->hasSequenceNumber()); $this->assertEquals($expectedPrefix, $v->getPrefix()); $this->assertEquals($expectedVariableName, $v->getVariableName()); $this->assertEquals($expectedSequence, $v->getSequenceNumber()); }
/** * Get the item sessions held by the test session by item reference $identifier. * * @param string $identifier An item reference $identifier e.g. Q04. Prefixed or sequenced identifiers e.g. Q04.1.X are considered to be malformed. * @return AssessmentItemSessionCollection|false A collection of AssessmentItemSession objects or false if no item session could be found for $identifier. * @throws InvalidArgumentException If the given $identifier is malformed. */ public function getAssessmentItemSessions($identifier) { try { $v = new VariableIdentifier($identifier); if ($v->hasPrefix() === true || $v->hasSequenceNumber() === true) { $msg = "'{$identifier}' is not a valid item reference identifier."; throw new InvalidArgumentException($msg, 0); } $itemRefs = $this->getAssessmentItemRefs(); if (isset($itemRefs[$identifier]) === false) { return false; } try { return $this->getAssessmentItemSessionStore()->getAssessmentItemSessions($itemRefs[$identifier]); } catch (OutOfBoundsException $e) { return false; } } catch (InvalidArgumentException $e) { $msg = "'{$identifier}' is not a valid item reference identifier."; throw new InvalidArgumentException($msg, 0, $e); } }
/** * Check if a given variable identified by $offset exists in the global scope * of the AssessmentTestSession. * * @return boolean Whether the variable identified by $offset exists in the current context. * @throws \OutOfRangeException If $offset is not a simple variable identifier (no prefix, no sequence number). */ public function offsetExists($offset) { try { $v = new VariableIdentifier($offset); if ($v->hasPrefix() === true) { $msg = "Test existence of a variable in an AssessmentTestSession may only be addressed with simple variable "; $msg = "identifiers (no prefix, no sequence number). '" . $v->__toString() . "' given."; throw new OutOfRangeException($msg, 0); } $data =& $this->getDataPlaceHolder(); return isset($data[$offset]); } catch (InvalidArgumentException $e) { $msg = "'{$offset}' is not a valid variable identifier."; throw new OutOfRangeException($msg); } }
/** * Perform a branching on a TestPart, AssessmentSection or AssessmentItemRef with * the given $identifier. * * The target will be considered invalid if the following constraints are not fullfilled: * * From IMS QTI: * In the case of an item or section, the target must refer to an item or section in the same * testPart that has not yet been presented. For testParts, the target must refer to another testPart. * * @param string $identifier A QTI Identifier to be the target of the branching. * @throws \OutOfBoundsException If an error occurs while branching e.g. the $identifier is not referenced in the route or the target is invalid. * @throws \OutOfRangeException If $identifier is not a valid branching identifier. */ public function branch($identifier) { try { $identifier = new VariableIdentifier($identifier); $id = $identifier->hasPrefix() === false ? $identifier->getVariableName() : $identifier->getPrefix(); $occurence = $identifier->hasPrefix() === false ? 0 : intval($identifier->getVariableName() - 1); } catch (InvalidArgumentException $e) { $msg = "The given identifier '{$identifier}' is an invalid branching target."; throw new OutOfRangeException($msg); } // Check for an assessmentItemRef. $assessmentItemRefs = $this->getAssessmentItemRefs(); if (isset($assessmentItemRefs[$id]) === true) { $assessmentItemRefMap = $this->getAssessmentItemRefMap(); $targetRouteItems = $assessmentItemRefMap[$assessmentItemRefs[$id]]; if ($targetRouteItems[$occurence]->getTestPart() !== $this->current()->getTestPart()) { // From IMS QTI: // In case of an item or section, the target must refer to an item or section // in the same testPart [...] $msg = "Branchings to items outside of the current testPart is forbidden by the QTI 2.1 specification."; throw new OutOfBoundsException($msg); } $this->setPosition($this->getRouteItemPosition($targetRouteItems[$occurence])); return; } // Check for an assessmentSection. $assessmentSectionIdentifierMap = $this->getAssessmentSectionIdentifierMap(); if (isset($assessmentSectionIdentifierMap[$id]) === true) { if ($assessmentSectionIdentifierMap[$id][0]->getTestPart() !== $this->current()->getTestPart()) { // From IMS QTI: // In case of an item or section, the target must refer to an item or section // in the same testPart [...] $msg = "Branchings to assessmentSections outside of the current testPart is forbidden by the QTI 2.1 specification."; throw new OutOfBoundsException($msg); } // We branch to the first RouteItem belonging to the section. $this->setPosition($this->getRouteItemPosition($assessmentSectionIdentifierMap[$id][0])); return; } // Check for a testPart. $testPartIdentifierMap = $this->getTestPartIdentifierMap(); if (isset($testPartIdentifierMap[$id]) === true) { // We branch to the first RouteItem belonging to the testPart. if ($testPartIdentifierMap[$id][0]->getTestPart() === $this->current()->getTestPart()) { // From IMS QTI: // For testParts, the target must refer to another testPart. $msg = "Cannot branch to the same testPart."; throw new OutOfBoundsException($msg); } // We branch to the first RouteItem belonging to the testPart. $this->setPosition($this->getRouteItemPosition($testPartIdentifierMap[$id][0])); return; } // No such identifier referenced in the route, cannot branch. $msg = "No such identifier '{$id}' found in the route for branching."; throw new OutOfBoundsException($msg); }
/** * Process the Variable expression. * * * If the requested variable does not exist, NULL is returned. * * In a test context, if the requested weight does not exist, the raw value of the variable is returned. * * @returns null|mixed The value of the target variable or NULL if the variable does not exist. * @throws \qtism\runtime\expressions\ExpressionProcessingException */ public function process() { $state = $this->getState(); $variableIdentifier = $this->getExpression()->getIdentifier(); $weightIdentifier = $this->getExpression()->getWeightIdentifier(); $variable = $state->getVariable($variableIdentifier); if (empty($variable)) { return null; } $variableValue = $state[$variableIdentifier]; if ($variable->isNull()) { return $variableValue; // Even if empty string, it is considered by QTI as null. } // We have a value for this variable, is it weighted? if ($state instanceof AssessmentTestSession) { try { $vIdentifier = new VariableIdentifier($variableIdentifier); if ($vIdentifier->hasPrefix() === true && empty($weightIdentifier) === false) { $weight = $state->getWeight($vIdentifier->getPrefix() . '.' . $weightIdentifier); $baseType = $variableValue->getBaseType(); $cardinality = $variableValue->getCardinality(); // From IMS QTI: // Weights only apply to item variables with base types integer and float. // If the item variable is of any other type the weight is ignored. if (!empty($weight) && ($baseType === BaseType::INTEGER || $baseType === BaseType::FLOAT)) { if ($cardinality === Cardinality::SINGLE) { return new Float($variableValue->getValue() * $weight->getValue()); } elseif ($cardinality === Cardinality::MULTIPLE || $cardinality === Cardinality::ORDERED) { // variableValue is an object, the weighting should not // affect the content of the state so a new container is created. $finalValue = $cardinality === Cardinality::MULTIPLE ? new MultipleContainer(BaseType::FLOAT) : new OrderedContainer(BaseType::FLOAT); for ($i = 0; $i < count($variableValue); $i++) { if ($variableValue[$i] !== null) { $finalValue[] = new Float($variableValue[$i]->getValue() * $weight->getValue()); } else { $finalValue[] = null; } } return $finalValue; } } else { return $variableValue; } } else { return $variableValue; } } catch (InvalidArgumentException $e) { // Invalid $variableIdentifier. $msg = "Invalid identifier '{$variableIdentifier}' given for variable identifier."; throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::NONEXISTENT_VARIABLE, $e); } } else { return $variableValue; } }