331 lines
14 KiB
PHP
331 lines
14 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) 2016 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
|
|
*
|
|
*/
|
|
|
|
namespace oat\taoQtiTest\models;
|
|
|
|
use qtism\common\enums\BaseType;
|
|
use qtism\common\enums\Cardinality;
|
|
use qtism\common\collections\IdentifierCollection;
|
|
use qtism\data\AssessmentTest;
|
|
use qtism\data\state\OutcomeDeclaration;
|
|
use qtism\data\state\DefaultValue;
|
|
use qtism\data\state\ValueCollection;
|
|
use qtism\data\state\Value;
|
|
use qtism\data\rules\SetOutcomeValue;
|
|
use qtism\data\rules\OutcomeRuleCollection;
|
|
use qtism\data\rules\OutcomeRule;
|
|
use qtism\data\processing\OutcomeProcessing;
|
|
use qtism\data\expressions\ExpressionCollection;
|
|
use qtism\data\expressions\NumberCorrect;
|
|
use qtism\data\expressions\TestVariables;
|
|
use qtism\data\expressions\operators\Sum;
|
|
|
|
/**
|
|
* Utility class for Test Category Rules Generation.
|
|
*
|
|
* Provides utility methods supporting Test Category Rules Generation process.
|
|
*/
|
|
class TestCategoryRulesUtils
|
|
{
|
|
const NUMBER_ITEMS_SUFFIX = '_CATEGORY_NUMBER_ITEMS';
|
|
const NUMBER_CORRECT_SUFFIX = '_CATEGORY_NUMBER_CORRECT';
|
|
const TOTAL_SCORE_SUFFIX = '_CATEGORY_TOTAL_SCORE';
|
|
|
|
/**
|
|
* Extract all categories from a given QTI-SDK AssessmentTest object.
|
|
*
|
|
* This method will extract all category identifiers found as assessmentItemRef
|
|
* category identifiers from a given $test object.
|
|
*
|
|
* An optional $exclusion array can be given as a second parameter. Each of the Pearl
|
|
* Compatible Regular Expression will be applied on the categories to be extracted. If
|
|
* one of them matches a category, it will be excluded from the extraction process.
|
|
*
|
|
* Identifiers returned will be unique.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param array $exclusion (optional) An optional array of Pearl Compatible Regular Expressions.
|
|
* @return array An array of QTI category identifiers.
|
|
*/
|
|
public static function extractCategories(AssessmentTest $test, array $exclusion = [])
|
|
{
|
|
$categories = [];
|
|
|
|
$assessmentItemRefs = $test->getComponentsByClassName('assessmentItemRef');
|
|
foreach ($assessmentItemRefs as $assessmentItemRef) {
|
|
$assessmentItemRefCategories = $assessmentItemRef->getCategories()->getArrayCopy();
|
|
|
|
$count = count($assessmentItemRefCategories);
|
|
for ($i = 0; $i < $count; $i++) {
|
|
foreach ($exclusion as $excl) {
|
|
if (@preg_match($excl, $assessmentItemRefCategories[$i]) === 1) {
|
|
unset($assessmentItemRefCategories[$i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$categories = array_merge($categories, $assessmentItemRefCategories);
|
|
}
|
|
|
|
return array_unique($categories);
|
|
}
|
|
|
|
/**
|
|
* Append a variable dedicated to counting number of items related to a given category.
|
|
*
|
|
* This method will append a QTI outcome variable dedicated to count the number of items
|
|
* related to a given QTI $category, to a given QTI $test.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category A QTI category identifier.
|
|
* @return string the identifier of the created QTI outcome variable.
|
|
*/
|
|
public static function appendNumberOfItemsVariable(AssessmentTest $test, $category)
|
|
{
|
|
$varName = strtoupper($category) . self::NUMBER_ITEMS_SUFFIX;
|
|
self::appendOutcomeDeclarationToTest($test, $varName, BaseType::INTEGER, self::countNumberOfItemsWithCategory($test, $category));
|
|
|
|
return $varName;
|
|
}
|
|
|
|
/**
|
|
* Append a variable dedicated to counting number of correctly responded items related to a given category.
|
|
*
|
|
* This method will append a QTI outcome variable dedicated to count the number of items that are correctly responded
|
|
* related to a given QTI $category, to a given QTI $test.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category A QTI category identifier.
|
|
* @return string the identifier of the created QTI outcome variable.
|
|
*/
|
|
public static function appendNumberCorrectVariable(AssessmentTest $test, $category)
|
|
{
|
|
$varName = strtoupper($category) . self::NUMBER_CORRECT_SUFFIX;
|
|
self::appendOutcomeDeclarationToTest($test, $varName, BaseType::INTEGER);
|
|
|
|
return $varName;
|
|
}
|
|
|
|
/**
|
|
* Append a variable dedicated to store the total score items related to a given category.
|
|
*
|
|
* This method will append a QTI outcome variable dedicated to store the total score of items
|
|
* related to a given QTI $category, to a given QTI $test.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category A QTI category identifier.
|
|
* @return string the identifier of the created QTI outcome variable.
|
|
*/
|
|
public static function appendTotalScoreVariable(AssessmentTest $test, $category)
|
|
{
|
|
$varName = strtoupper($category) . self::TOTAL_SCORE_SUFFIX;
|
|
self::appendOutcomeDeclarationToTest($test, $varName, BaseType::FLOAT);
|
|
|
|
return $varName;
|
|
}
|
|
|
|
/**
|
|
* Append the outcome processing rules to populate an outcome variable with the number of items correctly responded related to a given category.
|
|
*
|
|
* This method will append a QTI outcome processing to a given QTI-SDK AssessmentTest $test, dedicated to count the number
|
|
* of correctly responded items related to a given QTI $category.
|
|
*
|
|
* In case of an outcome processing rule targetting a variable name $varName already exists in the test, the outcome
|
|
* processing rule is not appended to the test.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category A QTI category identifier.
|
|
* @param string $varName The QTI identifier of the variable to be populated by the outcome processing rule.
|
|
*/
|
|
public static function appendNumberCorrectOutcomeProcessing(AssessmentTest $test, $category, $varName)
|
|
{
|
|
if (self::isVariableSetOutcomeValueTarget($test, $varName) === false) {
|
|
$numberCorrectExpression = new NumberCorrect();
|
|
$numberCorrectExpression->setIncludeCategories(
|
|
new IdentifierCollection(
|
|
[$category]
|
|
)
|
|
);
|
|
|
|
$setOutcomeValue = new SetOutcomeValue(
|
|
$varName,
|
|
$numberCorrectExpression
|
|
);
|
|
|
|
self::appendOutcomeRule($test, $setOutcomeValue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append the outcome processing rules to populate an outcome variable with total score of items related to a given category.
|
|
*
|
|
* This method will append a QTI outcome processing to a given QTI-SDK AssessmentTest $test, dedicated to store
|
|
* the total score of items related to a given QTI $category.
|
|
*
|
|
* In case of the $weightIdentifier argument is given, the score will consider weights defined at the assessmentItemRef
|
|
* level identified by $weightIdentifier. Otherwise, no weights are taken into account while computing total scores.
|
|
*
|
|
* In case of an outcome processing rule targetting a variable name $varName already exists in the test, the outcome
|
|
* processing rule is not appended to the test.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category A QTI category identifier.
|
|
* @param string $varName The QTI identifier of the variable to be populated by the outcome processing rule.
|
|
* @param string $scoreIdentifier (optional) An optional QTI identifier to be used as items' score variable (defaults to "SCORE").
|
|
* @param string $weightIdentifier (optional) An optional QTI identifier to be used as items' weight to be considered for total score. (defaults to empty string).
|
|
*/
|
|
public static function appendTotalScoreOutcomeProcessing(AssessmentTest $test, $category, $varName, $scoreVariableIdentifier = 'SCORE', $weightIdentifier = '')
|
|
{
|
|
if (self::isVariableSetOutcomeValueTarget($test, $varName) === false) {
|
|
$testVariablesExpression = new TestVariables($scoreVariableIdentifier, BaseType::FLOAT);
|
|
$testVariablesExpression->setWeightIdentifier($weightIdentifier);
|
|
$testVariablesExpression->setIncludeCategories(
|
|
new IdentifierCollection(
|
|
[$category]
|
|
)
|
|
);
|
|
|
|
$setOutcomeValue = new SetOutcomeValue(
|
|
$varName,
|
|
new Sum(
|
|
new ExpressionCollection(
|
|
[
|
|
$testVariablesExpression
|
|
]
|
|
)
|
|
)
|
|
);
|
|
|
|
self::appendOutcomeRule($test, $setOutcomeValue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append an outcome declaration to a test.
|
|
*
|
|
* This method will append an outcome declaration with identifier $varName, single cardinality
|
|
* to a given QTI-SDK AssessmentTest $test object.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $varName The variable name to be used for the outcome declaration.
|
|
* @param integer $baseType A QTI-SDK Base Type.
|
|
* @param mixed (optional) A default value for the variable.
|
|
*/
|
|
public static function appendOutcomeDeclarationToTest(AssessmentTest $test, $varName, $baseType, $defaultValue = null)
|
|
{
|
|
$outcomeDeclarations = $test->getOutcomeDeclarations();
|
|
$outcome = new OutcomeDeclaration($varName, $baseType, Cardinality::SINGLE);
|
|
|
|
if ($defaultValue !== null) {
|
|
$outcome->setDefaultValue(
|
|
new DefaultValue(
|
|
new ValueCollection(
|
|
[
|
|
new Value(
|
|
$defaultValue,
|
|
$baseType
|
|
)
|
|
]
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
$outcomeDeclarations->attach($outcome);
|
|
}
|
|
|
|
/**
|
|
* Count the number of items in a test that belong to a given category.
|
|
*
|
|
* This method will count the number of items in a test that belong to a given category.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $category a QTI category identifier.
|
|
* @return integer The number of items that belong to $category.
|
|
*/
|
|
public static function countNumberOfItemsWithCategory(AssessmentTest $test, $category)
|
|
{
|
|
$count = 0;
|
|
|
|
$assessmentItemRefs = $test->getComponentsByClassName('assessmentItemRef');
|
|
foreach ($assessmentItemRefs as $assessmentItemRef) {
|
|
if (in_array($category, $assessmentItemRef->getCategories()->getArrayCopy()) === true) {
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Know whether or not a variable is the target of an existing setOutcomeValue QTI rule.
|
|
*
|
|
* This method enables the client code to know whether or not a variable with identifier
|
|
* $varName is the target of an existing setOutcomeValue QTI rule with a given
|
|
* AssessmentTest $test object.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param string $varName A QTI variable identifier.
|
|
* @return boolean
|
|
*/
|
|
public static function isVariableSetOutcomeValueTarget(AssessmentTest $test, $varName)
|
|
{
|
|
$setOutcomeValues = $test->getComponentsByClassName('setOutcomeValue');
|
|
foreach ($setOutcomeValues as $setOutcomeValue) {
|
|
if ($setOutcomeValue->getIdentifier() === $varName) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Append a QTI-SDK OutcomeRule object in an AssessmentTest's OutcomeProcessing.
|
|
*
|
|
* In case of no OutcomeProcessing is set yet for the AssessmentTest $test object,
|
|
* it will be automatically created, with the OutcomeRule $rule as its first
|
|
* rule. Otherwise, the OutcomeRule $rule is simply appended to the existing
|
|
* OutcomeProcessing object.
|
|
*
|
|
* @param qtism\data\AssessmentTest $test A QTI-SDK AssessmentTest object.
|
|
* @param qtism\data\rules\OutcomeRule A QTI-SDK OutcomeRule object.
|
|
*/
|
|
private static function appendOutcomeRule(AssessmentTest $test, OutcomeRule $rule)
|
|
{
|
|
$outcomeProcessing = $test->getOutcomeProcessing();
|
|
if ($outcomeProcessing === null) {
|
|
$test->setOutcomeProcessing(
|
|
new OutcomeProcessing(
|
|
new OutcomeRuleCollection(
|
|
[
|
|
$rule
|
|
]
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
$outcomeProcessing->getOutcomeRules()[] = $rule;
|
|
}
|
|
}
|
|
}
|