272 lines
9.1 KiB
PHP
272 lines
9.1 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-2018 (original work) Open Assessment Technologies SA ;
|
|
*/
|
|
|
|
/**
|
|
* @author Jean-Sébastien Conan <jean-sebastien.conan@vesperiagroup.com>
|
|
*/
|
|
|
|
namespace oat\taoQtiTest\models\runner\time;
|
|
|
|
use oat\oatbox\service\ServiceManager;
|
|
use oat\taoTests\models\runner\time\TimePoint;
|
|
use qtism\common\datatypes\QtiDuration;
|
|
use qtism\data\NavigationMode;
|
|
use qtism\data\QtiComponent;
|
|
use qtism\runtime\tests\TimeConstraint;
|
|
use taoQtiTest_helpers_TestRunnerUtils as TestRunnerUtils;
|
|
|
|
/**
|
|
* Class QtiTimeConstraint
|
|
*
|
|
* Represents a time constraint during an AssessmentTestSession.
|
|
*
|
|
* @package oat\taoQtiTest\models\runner\time
|
|
*/
|
|
class QtiTimeConstraint extends TimeConstraint implements \JsonSerializable
|
|
{
|
|
/**
|
|
* @var QtiTimer
|
|
*/
|
|
protected $timer;
|
|
|
|
/**
|
|
* Allow to take care of extra time
|
|
* @var boolean
|
|
*/
|
|
protected $applyExtraTime;
|
|
|
|
/**
|
|
* @var integer
|
|
*/
|
|
protected $timerTarget;
|
|
|
|
/**
|
|
* @return QtiTimer
|
|
*/
|
|
public function getTimer()
|
|
{
|
|
return $this->timer;
|
|
}
|
|
|
|
/**
|
|
* @param QtiTimer $timer
|
|
* @return QtiTimeConstraint
|
|
*/
|
|
public function setTimer($timer)
|
|
{
|
|
$this->timer = $timer;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return boolean
|
|
*/
|
|
public function getApplyExtraTime()
|
|
{
|
|
return $this->applyExtraTime;
|
|
}
|
|
|
|
/**
|
|
* @param boolean $applyExtraTime
|
|
* @return QtiTimeConstraint
|
|
*/
|
|
public function setApplyExtraTime($applyExtraTime)
|
|
{
|
|
$this->applyExtraTime = $applyExtraTime;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param integer $timerTarget
|
|
* @return QtiTimeConstraint
|
|
*/
|
|
public function setTimerTarget($timerTarget)
|
|
{
|
|
$this->timerTarget = $timerTarget;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Create a new TimeConstraint object.
|
|
*
|
|
* @param QtiComponent $source The TestPart or SectionPart the constraint applies on.
|
|
* @param QtiDuration $duration The already spent duration by the candidate on $source.
|
|
* @param int|NavigationMode $navigationMode The current navigation mode.
|
|
* @param boolean $considerMinTime Whether or not to consider minimum time limits.
|
|
* @param boolean $applyExtraTime Allow to take care of extra time
|
|
* @param integer $timerTarget client/server
|
|
*/
|
|
public function __construct(
|
|
QtiComponent $source,
|
|
QtiDuration $duration,
|
|
$navigationMode = NavigationMode::LINEAR,
|
|
$considerMinTime = true,
|
|
$applyExtraTime = false,
|
|
$timerTarget = TimePoint::TARGET_SERVER
|
|
) {
|
|
$this->setSource($source);
|
|
$this->setDuration($duration);
|
|
$this->setNavigationMode($navigationMode);
|
|
$this->setApplyExtraTime($applyExtraTime);
|
|
$this->setTimerTarget($timerTarget);
|
|
}
|
|
|
|
/**
|
|
* Get the remaining duration from a source (min or max time, usually)
|
|
* @param QtiDuration $duration the source duration
|
|
* @return Duration|false A Duration object (or false of not available) that represents the remaining time
|
|
*/
|
|
protected function getRemainingTimeFrom(QtiDuration $duration)
|
|
{
|
|
if (!is_null($duration)) {
|
|
$remaining = clone $duration;
|
|
$remaining->sub($this->getDuration());
|
|
return ($remaining->isNegative() === true) ? new QtiDuration('PT0S') : $remaining;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the time remaining to be spent by the candidate on the source of the time
|
|
* constraint. Please note that this method will never return negative durations.
|
|
*
|
|
* @return Duration A Duration object or null if there is no maxTime constraint running for the source of the time constraint.
|
|
*/
|
|
public function getMaximumRemainingTime()
|
|
{
|
|
if (($maxTime = $this->getAdjustedMaxTime()) !== null) {
|
|
$maximumTime = clone $maxTime;
|
|
|
|
if ($this->getApplyExtraTime() && $this->timer) {
|
|
// take care of the already consumed extra time under the current constraint
|
|
// and append the full remaining extra time
|
|
// the total must correspond to the already elapsed time plus the remaining time
|
|
$maximumTime->add(new QtiDuration('PT' . $this->timer->getExtraTime() . 'S'));
|
|
}
|
|
|
|
return $this->getRemainingTimeFrom($maximumTime);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the time remaining the candidate has to spend by the candidate on the source of the time
|
|
* constraint. Please note that this method will never return negative durations.
|
|
*
|
|
* @return Duration A Duration object or null if there is no minTime constraint running for the source of the time constraint.
|
|
*/
|
|
public function getMinimumRemainingTime()
|
|
{
|
|
if (($timeLimits = $this->getSource()->getTimeLimits()) !== null && ($minTime = $timeLimits->getMinTime()) !== null) {
|
|
return $this->getRemainingTimeFrom($minTime);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Calculates maximum time with applied adjustments
|
|
* @return QtiDuration|null
|
|
*/
|
|
public function getAdjustedMaxTime()
|
|
{
|
|
$timeLimits = $this->getSource()->getTimeLimits();
|
|
if ($timeLimits === null) {
|
|
return null;
|
|
}
|
|
|
|
$maxTime = $timeLimits->getMaxTime();
|
|
if ($maxTime === null) {
|
|
return null;
|
|
}
|
|
|
|
$maximumTime = clone $maxTime;
|
|
if ($this->timer) {
|
|
$adjustmentSeconds = $this->timer->getAdjustmentMap()->get($this->getSource()->getIdentifier());
|
|
if ($adjustmentSeconds > 0) {
|
|
$maximumTime->add(new QtiDuration('PT' . $adjustmentSeconds . 'S'));
|
|
} else {
|
|
$maximumTime->sub(new QtiDuration('PT' . abs($adjustmentSeconds) . 'S'));
|
|
}
|
|
}
|
|
|
|
return $maximumTime;
|
|
}
|
|
|
|
/**
|
|
* Converts a duration to milliseconds
|
|
* @param QtiDuration|null $duration the duration to convert
|
|
* @return int|false the duration in ms or false if none
|
|
*/
|
|
private function durationToMs($duration)
|
|
{
|
|
if (!is_null($duration) && $duration instanceof QtiDuration) {
|
|
return TestRunnerUtils::getDurationWithMicroseconds($duration);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Serialize the constraint the expected way by the TestContext and the TestMap
|
|
* @return array
|
|
*/
|
|
public function jsonSerialize()
|
|
{
|
|
$source = $this->getSource();
|
|
$timeLimits = $source->getTimeLimits();
|
|
if (!is_null($timeLimits)) {
|
|
$identifier = $source->getIdentifier();
|
|
|
|
$maxTime = $this->getAdjustedMaxTime();
|
|
$minTime = $timeLimits->getMinTime();
|
|
$maxTimeRemaining = $this->getMaximumRemainingTime();
|
|
$minTimeRemaining = $this->getMinimumRemainingTime();
|
|
if ($maxTimeRemaining !== false || $minTimeRemaining !== false) {
|
|
$label = method_exists($source, 'getTitle') ? $source->getTitle() : $identifier;
|
|
|
|
$extraTime = [];
|
|
if ($this->getTimer() !== null && $maxTime !== null) {
|
|
$timer = $this->getTimer();
|
|
$maxTimeSeconds = $maxTime->getSeconds(true);
|
|
$extraTime = [
|
|
'total' => $timer->getExtraTime(),
|
|
'consumed' => $timer->getConsumedExtraTime($identifier, $maxTimeSeconds, $this->timerTarget),
|
|
'remaining' => $timer->getRemainingExtraTime($identifier, $maxTimeSeconds, $this->timerTarget),
|
|
];
|
|
}
|
|
|
|
/** @var TimerLabelFormatterService $labelFormatter */
|
|
$labelFormatter = ServiceManager::getServiceManager()->get(TimerLabelFormatterService::SERVICE_ID);
|
|
return [
|
|
'label' => $labelFormatter->format($label),
|
|
'source' => $identifier,
|
|
'qtiClassName' => $source->getQtiClassName(),
|
|
'extraTime' => $extraTime,
|
|
'allowLateSubmission' => $this->allowLateSubmission(),
|
|
'minTime' => $this->durationToMs($minTime),
|
|
'minTimeRemaining' => $this->durationToMs($minTimeRemaining),
|
|
'maxTime' => $this->durationToMs($maxTime),
|
|
'maxTimeRemaining' => $this->durationToMs($maxTimeRemaining),
|
|
];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|