testSession = $testSession; return $this->register(self::INCREASE, $seconds, $type, $source); } /** * {@inheritDoc} * @throws InvalidStorageException */ public function decrease( TestSession $testSession, int $seconds, string $type = self::TYPE_TIME_ADJUSTMENT, QtiIdentifiable $source = null ): bool { $this->testSession = $testSession; $seconds = $this->findMaximumPossibleDecrease($seconds); if ($seconds === 0) { return false; } return $this->register(self::DECREASE, $seconds, $type, $source); } /** * {@inheritDoc} */ public function getAdjustedMaxTime(QtiIdentifiable $source, QtiTimer $timer): ?QtiDuration { $timeLimits = $source->getTimeLimits(); if (!$timeLimits || ($maxTime = $timeLimits->getMaxTime()) === null) { return null; } $maximumTime = clone $maxTime; if ($timer !== null) { $adjustmentSeconds = $this->getAdjustment($source, $timer); if ($adjustmentSeconds > 0) { $maximumTime->add(new QtiDuration('PT' . $adjustmentSeconds . 'S')); } else { $maximumTime->sub(new QtiDuration('PT' . abs($adjustmentSeconds) . 'S')); } } return $maximumTime; } /** * Get adjusted seconds * @param QtiIdentifiable $source * @param QtiTimer $qtiTimer * @return int */ public function getAdjustment(QtiIdentifiable $source, QtiTimer $qtiTimer): int { return $qtiTimer->getAdjustmentMap()->get($source->getIdentifier()); } /** * @inheritDoc */ public function getAdjustmentByType(QtiIdentifiable $source, QtiTimer $timer, ?string $adjustmentType = null): int { return $timer->getAdjustmentMap()->getByType($source->getIdentifier(), $adjustmentType); } /** * @throws InvalidStorageException */ private function register(string $action, int $seconds, string $type, QtiIdentifiable $source = null): bool { if ($source) { $this->putAdjustmentToTheMap($source, $type, $action, $seconds); } else { $this->putAdjustmentToTheMap($this->testSession->getCurrentAssessmentItemRef(), $type, $action, $seconds); $this->putAdjustmentToTheMap($this->testSession->getCurrentAssessmentSection(), $type, $action, $seconds); $this->putAdjustmentToTheMap($this->testSession->getCurrentTestPart(), $type, $action, $seconds); $this->putAdjustmentToTheMap($this->testSession->getAssessmentTest(), $type, $action, $seconds); } $this->getTimer()->save(); $this->getServiceLocator()->get(StorageManager::SERVICE_ID)->persist(); return true; } private function findMaximumPossibleDecrease(int $requestedSeconds): int { $minRemaining = PHP_INT_MAX; foreach ($this->testSession->getTimeConstraints() as $tc) { $maximumRemainingTime = $tc->getMaximumRemainingTime(); if ($maximumRemainingTime === false) { continue; } $maximumRemainingTime = $maximumRemainingTime->getSeconds(true); $minRemaining = min($minRemaining, $maximumRemainingTime); } if ($minRemaining < $requestedSeconds) { return $minRemaining; } return $requestedSeconds; } private function putAdjustmentToTheMap(QtiIdentifiable $element, string $type, string $action, int $seconds) { if ($element === null || !$element->getTimeLimits() || !$element->getTimeLimits()->hasMaxTime()) { return; } if ($action === self::INCREASE) { $this->getTimer()->getAdjustmentMap()->increase($element->getIdentifier(), $type, $seconds); } elseif ($action === self::DECREASE) { $this->getTimer()->getAdjustmentMap()->decrease($element->getIdentifier(), $type, $seconds); } } private function getTimer(): QtiTimer { return $this->testSession->getTimer(); } }