/** * Set the identifier of the template variable to be set. * * @param string $identifier A valid QTI identifier. */ public function setIdentifier($identifier) { if (Format::isIdentifier($identifier, false) === true) { $this->identifier = $identifier; } else { $msg = "The 'identifier' argument must be a valid QTI identifier, '" . $identifier . "' given."; throw new InvalidArgumentException($msg); } }
/** * Set the assessment section identifier to match. * * @param string $sectionIdentifier A QTI Identifier. * @throws \InvalidArgumentException If $sectionIdentifier is not a valid QTI Identifier. */ public function setSectionIdentifier($sectionIdentifier) { if (Format::isIdentifier($sectionIdentifier) || empty($sectionIdentifier)) { $this->sectionIdentifier = $sectionIdentifier; } else { $msg = "'{$sectionIndentifier}' is not a valid QTI Identifier."; throw new InvalidArgumentException($msg); } }
/** * Set the fieldIdentifier attribute. * * @param string $fieldIdentifier A QTI Identifier. * @throws InvalidArgumentException If $fieldIdentifier is not a valid QTI Identifier. */ public function setFieldIdentifier($fieldIdentifier) { if (Format::isIdentifier($fieldIdentifier)) { $this->fieldIdentifier = $fieldIdentifier; } else { $msg = "'{$fieldIdentifier}' is not a valid QTI Identifier."; throw new InvalidArgumentException($msg); } }
/** * Set the identifier of the associated mapping. * * @param string $identifier A QTI Identifier. * @throws \InvalidArgumentException If $identifier is not a valid QTI Identifier. */ public function setIdentifier($identifier) { if (Format::isIdentifier($identifier, false)) { $this->identifier = $identifier; } else { $msg = "{$identifier} is not a valid QTI Identifier."; throw new InvalidArgumentException($identifier); } }
/** * Check if $value is a valid QTI Identifier. * * @throws \InvalidArgumentException If $value is not a valid QTI Identifier. */ protected function checkType($value) { if (gettype($value) !== 'string') { $msg = "IdentifierCollection class only accept string values, '" . gettype($value) . "' given."; throw new InvalidArgumentException($msg); } elseif (!Format::isIdentifier($value)) { $msg = "IdentifierCollection class only accept valid QTI Identifiers, '{$value}' given."; throw new InvalidArgumentException($msg); } }
public function convert(BaseQuestionType $questionType, $interactionIdentifier, $interactionLabel) { /** @var mcq $question */ $question = $questionType; // Build <choiceInteraction> $valueIdentifierMap = []; $simpleChoiceCollection = new SimpleChoiceCollection(); foreach ($question->get_options() as $index => $option) { /** @var mcq_options_item $option */ $choiceContent = new FlowStaticCollection(); foreach (QtiMarshallerUtil::unmarshallElement($option->get_label()) as $component) { $choiceContent->attach($component); } // Use option['value'] as choice `identifier` if it has the correct format, // Otherwise, generate a valid using index such `CHOICE_1`, `CHOICE_2`, etc $originalOptionValue = $option->get_value(); $choiceIdentifier = Format::isIdentifier($originalOptionValue, false) ? $originalOptionValue : 'CHOICE_' . $index; // Store this reference in a map $valueIdentifierMap[$originalOptionValue] = $choiceIdentifier; $choice = new SimpleChoice($choiceIdentifier); $choice->setContent($choiceContent); $simpleChoiceCollection->attach($choice); } // Build final interaction and its corresponding <responseDeclaration>, and its <responseProcessingTemplate> $interaction = new ChoiceInteraction($interactionIdentifier, $simpleChoiceCollection); $interaction->setLabel($interactionLabel); $interaction->setMinChoices(1); $interaction->setMaxChoices($question->get_multiple_responses() ? $simpleChoiceCollection->count() : 1); // Build the prompt $interaction->setPrompt($this->convertStimulusForPrompt($question->get_stimulus())); // Set shuffle options $interaction->setShuffle($question->get_shuffle_options() ? true : false); // Set the layout if ($question->get_ui_style() instanceof mcq_ui_style && $question->get_ui_style()->get_type() === 'horizontal' && intval($question->get_ui_style()->get_columns()) === count($question->get_options())) { $interaction->setOrientation(Orientation::HORIZONTAL); } else { $interaction->setOrientation(Orientation::VERTICAL); LogService::log('ui_style` is ignored and `choiceInteraction` is assumed and set as `vertical`'); } if (empty($question->get_validation())) { return [$interaction, null, null]; } $builder = new McqValidationBuilder($question->get_multiple_responses(), $valueIdentifierMap); list($responseDeclaration, $responseProcessing) = $builder->buildValidation($interactionIdentifier, $question->get_validation()); return [$interaction, $responseDeclaration, $responseProcessing]; }
public function convert(item $item, array $questions) { // Make sure we clean up the log LogService::flush(); // Try to build the identifier using item `reference` // Otherwise, generate an alternative identifier and store the original reference as `label` $itemReference = $item->get_reference(); $itemIdentifier = Format::isIdentifier($itemReference, false) ? $itemReference : 'ITEM_' . StringUtil::generateRandomString(12); if ($itemReference !== $itemIdentifier) { LogService::log("The item `reference` ({$itemReference}) is not a valid identifier, thus can not be used for `assessmentItem` identifier. " . "Replaced it with randomly generated `{$itemIdentifier}` and stored the original `reference` as `label` attribute"); } $builder = new AssessmentItemBuilder(); $assessmentItem = $builder->build($itemIdentifier, $itemReference, $questions, $item->get_content()); $xml = new XmlDocument(); $xml->setDocumentComponent($assessmentItem); // Flush out all the error messages stored in this static class, also ensure they are unique $messages = array_values(array_unique(LogService::flush())); return [$xml->saveToString(true), $messages]; }
/** * Get the categories of an item * * @param RdfResource $item the item * * @return string[] the list of categories */ public function getItemCategories(RdfResource $item) { $categories = []; foreach ($item->getTypes() as $class) { $eligibleProperties = $this->getElligibleProperties($class); $propertiesValues = $item->getPropertiesValues(array_keys($eligibleProperties)); foreach ($propertiesValues as $property => $propertyValues) { foreach ($propertyValues as $value) { if ($value instanceof RdfResource) { $sanitizedIdentifier = self::sanitizeCategoryName($value->getLabel()); } else { $sanitizedIdentifier = self::sanitizeCategoryName((string) $value); } if (Format::isIdentifier($sanitizedIdentifier)) { $categories[] = $sanitizedIdentifier; } } } } return $categories; }
/** * Set the response variable associated with the interaction. * * @param string $responseIdentifier A QTI identifier. * @throws InvalidArgumentException If $responseIdentifier is not a valid QTI identifier. */ public function setResponseIdentifier($responseIdentifier) { if (Format::isIdentifier($responseIdentifier, false) === true) { $this->responseIdentifier = $responseIdentifier; } else { $msg = "The 'responseIdentifier' argument must be a valid QTI identifier."; throw new InvalidArgumentException($msg); } }
/** * Set the unique identifier of the body element. * * @param string $id A QTI Identifier. * @throws \InvalidArgumentException If $id is not a valid QTI identifier. */ public function setId($id = '') { if (is_string($id) && (empty($id) === true || Format::isIdentifier($id, false) === true)) { $this->id = $id; } else { $msg = "The 'id' argument of a body element must be a valid identifier or an empty string"; throw new InvalidArgumentException($msg); } }
/** * If the interaction is bound to a numeric response variable, set the identifier of the response variable where the * plain text entered by the candidate will be stored. If $stringIdentifier is an empty string, it means that * there is no value for the stringIdentifier attribute. * * @param string $stringIdentifier A QTI Identifier or an empty string. * @throws InvalidArgumentException If $stringIdentifier is not a valid QTIIdentifier nor an empty string. */ public function setStringIdentifier($stringIdentifier) { if (Format::isIdentifier($stringIdentifier, false) === true || is_string($stringIdentifier) && empty($stringIdentifier) === true) { $this->stringIdentifier = $stringIdentifier; } else { $msg = "The 'stringIdentifier' argument must be a valid QTI identifier or an empty string, '" . $stringIdentifier . "' given."; throw new InvalidArgumentException($msg); } }
/** * Transform a custom operator class e.g. 'org.qtism.custom.explode' into a PHP * fully qualified class name e.g. 'org\qtism\custom\Explode'. * * @param string $class A custom operator class name where namespace separator is '.' (dot). * @return boolean|string A fully qualified PHP class name corresponding to $class or false if the transformation failed. */ public static function customOperatorClassToPhpClass($class) { if (is_string($class) === false) { return false; } else { if (Format::isIdentifier($class, false) === false) { return false; } } $class = strval($class); $tokens = explode('.', $class); if ($tokens === false) { return $tokens; } else { $tokenCount = count($tokens); if ($tokenCount <= 1) { return false; } // ucfirst on last token (i.e. The actual class name) $lastPosition = $tokenCount - 1; $lastToken = ucfirst($tokens[$lastPosition]); $tokens[$lastPosition] = $lastToken; return implode("\\", $tokens); } }
/** * Set the template identifier of the choice. * * @param string $templateIdentifier An empty string if no identifier is provided or a QTI identifier. * @throws InvalidArgumentException If the given $templateIdentifier is not a valid QTI identifier. */ public function setTemplateIdentifier($templateIdentifier) { if (is_string($templateIdentifier) === true && empty($templateIdentifier) === true || Format::isIdentifier($templateIdentifier, false) === true) { $this->templateIdentifier = $templateIdentifier; } else { $msg = "The 'templateIdentifier' must be an empty string or a valid QTI identifier, '" . gettype($templateIdentifier) . "' given."; throw new InvalidArgumentException($msg); } }
/** * Get the target identifier of the BranchRule. * * @param string $target A QTI Identifier. * @throws \InvalidArgumentException If $target is not a valid QTI Identifier. */ public function setTarget($target) { if (Format::isIdentifier($target)) { $this->target = $target; } else { $msg = "'Target' must be a valid QTI Identifier."; throw new InvalidArgumentException($msg); } }
/** * Transform a string representing a QTI valueType value in a * the correct datatype. * * @param string $string The QTI valueType value as a string. * @param integer $baseType The QTI baseType that defines the datatype of $string. * @return mixed A converted object/primitive type. * @throws \InvalidArgumentException If $baseType is not a value from the BaseType enumeration. * @throws \UnexpectedValueException If $string cannot be transformed in a Value expression with the given $baseType. */ public static function stringToDatatype($string, $baseType) { if (in_array($baseType, BaseType::asArray())) { $value = null; switch ($baseType) { case BaseType::BOOLEAN: if (Format::isBoolean($string)) { $value = Format::toLowerTrim($string) == 'true' ? true : false; return $value; } else { $msg = "'{$string}' cannot be transformed into boolean."; throw new UnexpectedValueException($msg); } break; case BaseType::INTEGER: if (Format::isInteger($string)) { $value = intval($string); return $value; } else { $msg = "'{$string}' cannot be transformed into integer."; throw new UnexpectedValueException($msg); } break; case BaseType::FLOAT: if (Format::isFloat($string)) { $value = floatval($string); return $value; } else { $msg = "'{$string}' cannot be transformed into float."; throw new UnexpectedValueException($msg); } break; case BaseType::URI: if (Format::isUri($string)) { return $string; } else { $msg = "'{$string}' is not a valid URI."; throw new UnexpectedValueException($msg); } break; case BaseType::IDENTIFIER: if (Format::isIdentifier($string)) { return $string; } else { $msg = "'{$string}' is not a valid QTI Identifier."; throw new UnexpectedValueException($msg); } break; case BaseType::INT_OR_IDENTIFIER: if (Format::isIdentifier($string)) { return $string; } elseif (Format::isInteger($string)) { return intval($string); } else { $msg = "'{$string}' is not a valid QTI Identifier nor a valid integer."; throw new UnexpectedValueException($msg); } break; case BaseType::PAIR: if (Format::isPair($string)) { $pair = explode(" ", $string); return new Pair($pair[0], $pair[1]); } else { $msg = "'{$string}' is not a valid pair."; throw new UnexpectedValueException($msg); } break; case BaseType::DIRECTED_PAIR: if (Format::isDirectedPair($string)) { $pair = explode(" ", $string); return new DirectedPair($pair[0], $pair[1]); } else { $msg = "'{$string}' is not a valid directed pair."; throw new UnexpectedValueException($msg); } break; case BaseType::DURATION: if (Format::isDuration($string)) { return new Duration($string); } else { $msg = "'{$string}' is not a valid duration."; throw new UnexpectedValueException($msg); } break; case BaseType::FILE: throw new \RuntimeException("Unsupported baseType: file."); break; case BaseType::STRING: return '' . $string; break; case BaseType::POINT: if (Format::isPoint($string)) { $parts = explode(" ", $string); return new Point(intval($parts[0]), intval($parts[1])); } else { $msg = "'{$string}' is not valid point."; throw new UnexpectedValueException($msg); } break; default: throw new \RuntimeException("Unknown baseType."); break; } } else { $msg = "BaseType must be a value from the BaseType enumeration."; throw new InvalidArgumentException($msg); } }
private function map(Question $question) { $type = $question->get_type(); if (!in_array($type, Constants::$supportedQuestionTypes)) { throw new MappingException("Question type `{$type}` not yet supported to be mapped to QTI"); } $clazz = new \ReflectionClass(self::MAPPER_CLASS_BASE . ucfirst($type . 'Mapper')); $questionTypeMapper = $clazz->newInstance(); // Try to use question `reference` as identifier // Otherwise, generate an alternative identifier and store the original reference as `label` to be passed in $questionReference = $question->get_reference(); $interactionIdentifier = Format::isIdentifier($questionReference, false) ? $questionReference : strtoupper($type) . '_' . StringUtil::generateRandomString(12); if ($interactionIdentifier !== $questionReference) { LogService::log("The question `reference` ({$questionReference}) is not a valid identifier. " . "Replaced it with randomly generated `{$interactionIdentifier}` and stored the original `reference` as `label` attribute"); } $result = $questionTypeMapper->convert($question->get_data(), $interactionIdentifier, $questionReference); $result[] = $questionTypeMapper->getExtraContent(); return $result; }
/** * Set the weight identifier. Can be '' (empty string) if no weight specified. * * @param string $weightIdentifier A QTI Identifier or '' (empty string) if not specified. * @throws InvalidArgumentException If $weightIdentifier is not a valid QTI Identifier nor '' (empty string). */ public function setWeightIdentifier($weightIdentifier) { if (Format::isIdentifier($weightIdentifier) || $weightIdentifier == '') { $this->weightIdentifier = $weightIdentifier; } else { $msg = "'{$weightIdentifier}' is not a valid QTI Identifier."; throw new InvalidArgumentException($msg); } }
/** * @dataProvider invalidIdentifierFormatProvider */ public function testInvalidIdentifierFormat($string) { $this->assertFalse(Format::isIdentifier($string)); }
/** * Set the second identifier of the pair. * * @param string $second A QTI Identifier. * @throws \InvalidArgumentException If $identifier is not a valid QTI Identifier. */ public function setSecond($second) { if (Format::isIdentifier($second)) { $this->second = $second; } else { $msg = "'{$second}' is an invalid QTI identifier."; throw new InvalidArgumentException($msg); } }