params = $params; // Should we make a wet run? if (isset($this->params[0])) { $this->wetRun = (boolval($this->params[0]) === true); // Should we limit the number of tests being terminated? if (isset($this->params[1])) { $this->maxTerminate = intval($this->params[1]); } } $deliveryExecutionStateService = $this->getServiceLocator()->get(DeliveryExecutionStateService::SERVICE_ID); if(!$deliveryExecutionStateService->hasOption(DeliveryExecutionStateService::OPTION_TERMINATION_DELAY_AFTER_PAUSE)) { return new Report( Report::TYPE_WARNING, 'Option `termination_delay_after_pause` not configured. `TerminatePausedAssessment` execution terminated' ); } $wetInfo = ($this->wetRun === false) ? 'dry' : 'wet'; $this->report = new Report( Report::TYPE_INFO, "Automatic termination of expired executions (${wetInfo} run)..." ); common_Logger::d('Termination expired paused execution started at ' . date(DATE_RFC3339)); \common_ext_ExtensionsManager::singleton()->getExtensionById('taoDeliveryRdf'); \common_ext_ExtensionsManager::singleton()->getExtensionById('taoQtiTest'); $deliveryExecutionService = ServiceProxy::singleton(); $count = 0; /** @var DeliveryMonitoringService $deliveryMonitoringService */ $deliveryMonitoringService = ServiceManager::getServiceManager()->get(DeliveryMonitoringService::CONFIG_ID); $delay = $deliveryExecutionStateService->getOption(DeliveryExecutionStateService::OPTION_TERMINATION_DELAY_AFTER_PAUSE); $startTimeFilter = (new DateTimeImmutable('now'))->sub(new DateInterval($delay))->getTimestamp(); $deliveryExecutionsData = $deliveryMonitoringService->find([ [DeliveryMonitoringService::STATUS => [ DeliveryExecution::STATE_ACTIVE, DeliveryExecution::STATE_PAUSED ]], 'AND', [DeliveryMonitoringService::START_TIME => '<' . $startTimeFilter] ]); foreach ($deliveryExecutionsData as $deliveryExecutionData) { $data = $deliveryExecutionData->get(); $deliveryExecution = $deliveryExecutionService->getDeliveryExecution( $data[DeliveryMonitoringService::DELIVERY_EXECUTION_ID] ); try { if ($this->isExpired($deliveryExecution)) { $this->terminateExecution($deliveryExecution); $count++; } } catch (\common_exception_NotFound $e) { //Delivery execution entry missed. $deliveryExecutionData->update( DeliveryMonitoringService::STATUS, DeliveryExecution::STATE_TERMINATED ); $deliveryMonitoringService->partialSave($deliveryExecutionData); common_Logger::w( 'Delivery execution ' . $deliveryExecution->getIdentifier() . ' is missed. Set it\'s state in delivery monitoring to Terminated' ); $this->addReport( Report::TYPE_WARNING, 'Delivery execution {$deliveryExecution->getIdentifier()} state in delivery monitoring was set to `terminated` ' . 'due to missed delivery execution entry.' ); } catch (\Exception $e) { $this->addReport(Report::TYPE_ERROR, $e->getMessage()); } catch (\Throwable $e) { common_Logger::f($e->getMessage()); } // Should we stop terminating assessments? if ($this->maxTerminate > 0 && $count >= $this->maxTerminate) { break; } } $msg = $count > 0 ? "{$count} executions has been terminated." : "Expired executions not found."; $this->addReport(Report::TYPE_INFO, $msg); common_Logger::d('Termination expired paused execution finished at ' . date(DATE_RFC3339)); return $this->report; } /** * $terminate delivery execution * @param DeliveryExecution $deliveryExecution */ protected function terminateExecution(DeliveryExecution $deliveryExecution) { if ($this->wetRun === true) { $deliveryExecutionStateService = ServiceManager::getServiceManager()->get(DeliveryExecutionStateService::SERVICE_ID); $deliveryExecutionStateService->terminateExecution( $deliveryExecution, [ 'reasons' => $this->getTerminationReasons(), 'comment' => __('The assessment was automatically terminated by the system due to inactivity.'), ] ); $this->addReport(Report::TYPE_INFO, "Delivery execution {$deliveryExecution->getIdentifier()} has been terminated."); } else { $this->addReport(Report::TYPE_INFO, "Delivery execution {$deliveryExecution->getIdentifier()} should be terminated."); } } /** * Return Termination Reasons. * * Provides the 'reasons' information array with keys 'category' and 'subCategory'. * This method may be overriden by subclasses to provide customer specific information. * * @return array. */ protected function getTerminationReasons() { // @fixme remove customer specific information. return ['category' => 'Technical', 'subCategory' => 'ACT']; } /** * Checks if delivery execution was expired after pausing * * @param DeliveryExecution $deliveryExecution * @return bool * @throws */ protected function isExpired(DeliveryExecution $deliveryExecution) { $result = false; $executionState = $deliveryExecution->getState()->getUri(); if (in_array($executionState, [ DeliveryExecutionState::STATE_PAUSED, DeliveryExecutionState::STATE_ACTIVE, ], true) ) { /** @var \oat\taoProctoring\model\implementation\DeliveryExecutionStateService $deliveryExecutionStateService */ $deliveryExecutionStateService = $this->getServiceLocator()->get(DeliveryExecutionStateService::SERVICE_ID); if ($executionState === DeliveryExecutionState::STATE_ACTIVE) { $lastTestTakersEvent = $this->getLastTestTakersEvent($deliveryExecution); $lastEventTime = (new DateTimeImmutable())->setTimestamp($lastTestTakersEvent['created_at']); } else { $lastEventTime = $this->getLastPause($deliveryExecution); } if ($lastEventTime) { $delay = $deliveryExecutionStateService->getOption(DeliveryExecutionStateService::OPTION_TERMINATION_DELAY_AFTER_PAUSE); $result = ($lastEventTime->add(new DateInterval($delay)) < (new DateTimeImmutable('now'))); } } return $result; } /** * Get time of the last pause * @param DeliveryExecution $deliveryExecution * @return DateTimeImmutable * @throws \Exception */ private function getLastPause(DeliveryExecution $deliveryExecution) { $deliveryLogService = $this->getServiceLocator()->get(DeliveryLog::SERVICE_ID); $pauses = array_reverse($deliveryLogService->get($deliveryExecution->getIdentifier(), DeliveryLogEvent::EVENT_ID_TEST_PAUSE)); if (!empty($pauses)) { $lastPause = $pauses[0]['created_at']; } else { $lastPause = $this->getLastTestTakersEvent($deliveryExecution)['created_at']; } return (new DateTimeImmutable())->setTimestamp($lastPause); } }