tao-test/app/taoProctoring/model/execution/DeliveryExecutionsUpdater.php

336 lines
11 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) 2018 (original work) Open Assessment Technologies SA;
*/
namespace oat\taoProctoring\model\execution;
use common_report_Report as Report;
use DateInterval;
use oat\generis\model\OntologyAwareTrait;
use oat\oatbox\service\ConfigurableService;
use oat\taoDelivery\model\execution\ServiceProxy;
use oat\taoDeliveryRdf\model\DeliveryAssemblyService;
use oat\taoProctoring\model\deliveryLog\DeliveryLog;
use oat\taoProctoring\model\implementation\DeliveryExecutionStateService;
use oat\taoProctoring\model\monitorCache\DeliveryMonitoringService;
use oat\taoDelivery\model\execution\DeliveryExecution;
use DateTimeImmutable;
/**
* Abstract service which allows us to update delivery executions
*
* Class UpdateDeliveryExecutionsService
* @package oat\taoProctoring\model\execution
*/
abstract class DeliveryExecutionsUpdater extends ConfigurableService
{
use OntologyAwareTrait;
/**
* Action based on the time of the last delivery execution activity
*/
const OPTION_TTL_AS_ACTIVE = 'ttlAsActive';
/**
* Action based on the delivery end time
*/
const OPTION_USE_DELIVERY_END_TIME = 'useDeliveryEndTime';
/**
* Statuses of the DE which will be updated
*/
const OPTION_UPDATEABLE_STATUSES = 'updateableExecutionStatuses';
/** @var Report */
protected $report;
/**
* @var \core_kernel_classes_Property
*/
private $propertyDeliveryEndDate;
/**
* terminate, delete, finish etc...
*
* @param $deliveryExecution
* @param $executionId
* @param bool $isEndDate
* @return mixed
*/
abstract protected function action($deliveryExecution, $executionId, $isEndDate = false);
/**
* @return Report
* @throws \common_exception_Error
*/
public function execute()
{
$deliveryMonitoringService = $this->getDeliveryMonitoringService();
$executionsMonitoringData = $deliveryMonitoringService->find([
DeliveryMonitoringService::STATUS => $this->getUpdateableStatuses()
]);
$this->report = Report::createInfo('Updating executions...');
$count = 0;
foreach ($executionsMonitoringData as $datum) {
try{
$data = $datum->get();
$executionId = $data[DeliveryMonitoringService::DELIVERY_EXECUTION_ID];
if ($this->isBasedOnEndDateTime()){
$deliveryID = $data[DeliveryMonitoringService::DELIVERY_ID];
$endDateTime = $this->getDeliveryEndDateTime($deliveryID);
//if no end date time found, fallback to normal updating execution based on TTL.
if (!is_null($endDateTime)){
$updated = $this->actionBasedOnEndDate($endDateTime, $executionId);
if($updated){
$count++;
}
continue;
}
}
$updated = $this->actionBasedOnTTL($executionId);
if($updated){
$count++;
}
}catch (\Exception $exception){
$this->report->add(Report::createFailure($exception->getMessage()));
}
}
$this->report->add(Report::createInfo('Number of executions updated: ' . $count));
return $this->report;
}
/**
* Run an action if the end date matches the condition
*
* @param DateTimeImmutable $endDateTime
* @param $executionId
* @return bool
* @throws \common_exception_Error
* @throws \Exception
*/
protected function actionBasedOnEndDate(DateTimeImmutable $endDateTime, $executionId)
{
try {
$deliveryExecution = $this->getServiceProxy()->getDeliveryExecution($executionId);
$lastInteraction = $this->getLastInteractionDateTime($deliveryExecution);
if ($lastInteraction === null){
$this->report->add(Report::createFailure('Execution last interaction cannot be found: '. $executionId));
return false;
}
if (new DateTimeImmutable('now') >= $endDateTime) {
$this->action($deliveryExecution, $executionId, true);
return true;
}
$this->report->add(Report::createInfo('Execution not expired yet:'. $executionId .
' Last Interaction:'.$lastInteraction->format('Y-m-d H:i:s') .
' Time when will expire:'.$endDateTime->format('Y-m-d H:i:s')
));
} catch (\common_exception_NotFound $e) {
$this->report->add(Report::createFailure('Execution cannot be found: '. $executionId));
$this->report->add(Report::createFailure($e->getMessage()));
}
return false;
}
/**
* Run an action if TTL matches the condition
*
* @param $executionId
* @return bool
* @throws \common_exception_Error
* @throws \Exception
*/
protected function actionBasedOnTTL($executionId)
{
try {
$deliveryExecution = $this->getServiceProxy()->getDeliveryExecution($executionId);
$lastInteraction = $this->getLastInteractionDateTime($deliveryExecution);
if ($lastInteraction === null){
$this->report->add(Report::createFailure('Execution last interaction cannot be found: '. $executionId));
return false;
}
$ttl = $this->getTtlAsActive();
if ($ttl === null) {
$this->report->add(Report::createFailure('Execution ttl not set: '. $executionId));
return false;
}
$timeUntilToLive = clone $lastInteraction;
$timeUntilToLive = $timeUntilToLive->add(new DateInterval($ttl));
if ((new DateTimeImmutable('now')) >= $timeUntilToLive) {
$this->action($deliveryExecution, $executionId);
return true;
} else {
$this->report->add(Report::createInfo('Execution not expired yet:'. $executionId .
' Last Interaction: '.$lastInteraction->format('Y-m-d H:i:s') .
' Time when will expire: '.$timeUntilToLive->format('Y-m-d H:i:s')
));
}
} catch (\common_exception_NotFound $e) {
$this->report->add(Report::createFailure('Execution cannot be found: '. $executionId));
$this->report->add(Report::createFailure($e->getMessage()));
}
return false;
}
/**
* Getting statuses of the delivery executions which needs to be updated
* by default used 'active' and 'paused' deliveryExecutions (for backwards compatibility)
* @return array|mixed
*/
protected function getUpdateableStatuses()
{
return $this->hasOption(self::OPTION_UPDATEABLE_STATUSES)
? $this->getOption(self::OPTION_UPDATEABLE_STATUSES)
: [
DeliveryExecution::STATE_ACTIVE,
DeliveryExecution::STATE_PAUSED,
];
}
/**
* @return array|DeliveryExecutionStateService|object
*/
protected function getDeliveryStateService()
{
/** @var DeliveryExecutionStateService $service */
$service = $this->getServiceLocator()->get(DeliveryExecutionStateService::SERVICE_ID);
return $service;
}
/**
* @return array|DeliveryMonitoringService|object
*/
protected function getDeliveryMonitoringService()
{
/** @var DeliveryMonitoringService $service */
$service = $this->getServiceLocator()->get(DeliveryMonitoringService::SERVICE_ID);
return $service;
}
/**
* @return array|ServiceProxy|object
*/
protected function getServiceProxy()
{
/** @var ServiceProxy $service */
$service = $this->getServiceLocator()->get(ServiceProxy::SERVICE_ID);
return $service;
}
/**
* @return array|DeliveryLog|object
*/
protected function getDeliveryLog()
{
/** @var DeliveryLog $deliveryLogService */
$deliveryLogService = $this->getServiceLocator()->get(DeliveryLog::SERVICE_ID);
return $deliveryLogService;
}
/**
* @param DeliveryExecution $deliveryExecution
* @return bool|DateTimeImmutable|null
* @throws \common_exception_NotFound
* @throws \Exception
*/
protected function getLastInteractionDateTime(DeliveryExecution $deliveryExecution)
{
$deliveryLogService = $this->getDeliveryLog();
$testTakerIdentifier = $deliveryExecution->getUserIdentifier();
$events = array_reverse($deliveryLogService->get($deliveryExecution->getIdentifier()));
$lastTestTakersEvent = null;
foreach ($events as $event) {
if ($event[DeliveryLog::CREATED_BY] === $testTakerIdentifier) {
$lastTestTakersEvent = $event;
break;
}
}
if (!is_null($lastTestTakersEvent)){
$lastEventTime = (new DateTimeImmutable())->setTimestamp($lastTestTakersEvent[DeliveryLog::CREATED_AT]);
} else {
return null;
}
return $lastEventTime;
}
/**
* @return string|null
*/
protected function getTtlAsActive()
{
return $this->getOption(static::OPTION_TTL_AS_ACTIVE);
}
/**
* @return string|null
*/
protected function isBasedOnEndDateTime()
{
return (bool)$this->getOption(static::OPTION_USE_DELIVERY_END_TIME);
}
/**
* @param $deliveryId
* @return null|DateTimeImmutable
*/
protected function getDeliveryEndDateTime($deliveryId)
{
$delivery = $this->getResource($deliveryId);
try {
$endDate = (string)$delivery->getUniquePropertyValue($this->getDeliveryEndDateProperty());
return (new DateTimeImmutable)->setTimestamp($endDate);
} catch (\Exception $e) {
return null;
}
}
/**
* Caching property to avoid multiple generations
* @return \core_kernel_classes_Property
*/
private function getDeliveryEndDateProperty() {
$this->propertyDeliveryEndDate = $this->propertyDeliveryEndDate ?: $this->getProperty(DeliveryAssemblyService::PROPERTY_END);
return $this->propertyDeliveryEndDate;
}
}