From cbb0eaa288dae2de2cd7ff0201061035bf936b00 Mon Sep 17 00:00:00 2001 From: kluge Date: Mon, 10 Aug 2026 16:41:08 +0200 Subject: [PATCH 01/14] added clean command --- Classes/Command/TaskCommandController.php | 299 +++++++++--------- .../Repository/TaskExecutionRepository.php | 39 +++ 2 files changed, 187 insertions(+), 151 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index d3b75dd..a553b2e 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -1,186 +1,183 @@ 'error', - TaskStatus::COMPLETED => 'success', - TaskStatus::RUNNING => 'em', - TaskStatus::ABORTED => 'strike' - ]; - - /** - * @throws \Exception - */ - public function runCommand(): void + public function findPending(Task $task): QueryResultInterface { - $this->scheduler->scheduleTasks(); - $this->taskRunner->runTasks(); - $this->taskExecutionHistory->cleanup(); + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('taskIdentifier', $task->getIdentifier()), + $query->logicalOr( + $query->equals('status', TaskStatus::PLANNED), + $query->equals('status', TaskStatus::RUNNING), + ) + ) + ); + return $query->execute(); } - /** - * Run a task directly - * - * @param string $taskIdentifier - * @throws \Exception - */ - public function runSingleCommand(string $taskIdentifier): void + public function findByTask(Task $task): QueryResultInterface { - $task = $this->getTaskByIdentifier($taskIdentifier); - $this->scheduler->scheduleTask($task); - $this->taskRunner->runTasks(); - $this->scheduler->scheduleTasks(); - $this->taskExecutionHistory->cleanup(); + $query = $this->createQuery(); + $query->matching( + $query->equals('taskIdentifier', $task->getIdentifier()), + ); + return $query->execute(); } - /** - * Lists all defined tasks - * @throws \Exception - */ - public function listCommand(): void + public function removePlannedTask(Task $task): void { - $tasks = $this->taskCollectionFactory->buildTasksFromConfiguration()->toArray(); - if ($tasks === []) { - $this->outputLine('No tasks configured yet'); - return; - } - $this->scheduler->scheduleTasks(); - - $this->output->outputTable(array_map(function (TaskInterface $task) { - /** @var TaskExecution $latestExecution */ - $latestExecution = $this->taskExecutionRepository->findLatestExecution($task, 1)->getFirst(); - return [ - $task->getIdentifier(), - $task->getLabel(), - $task->getCronExpression(), - $task->getHandlerClass(), - $latestExecution === null || $latestExecution->getEndTime() === null ? '-' : $latestExecution->getEndTime()->format('Y-m-d H:i:s') ?? $latestExecution->getStartTime()->format('Y-m-d H:i:s'), - $latestExecution === null ? '-' : sprintf('<%s>%s', $this->lastExecutionStatusMapping[$latestExecution->getStatus()], $latestExecution->getStatus(), $this->lastExecutionStatusMapping[$latestExecution->getStatus()]), - $latestExecution === null || $latestExecution->getDuration() === null ? '-' : number_format($latestExecution->getDuration(), 2) . ' s', - $this->getNextExecutionInfo($task), - ]; - }, $tasks), - ['Identifier', 'Label', 'Cron Expression', 'Handler Class', 'Previous Run Date', 'Previous Run Status', 'Previous Run Duration', 'Next Run'] + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('taskIdentifier', $task->getIdentifier()), + $query->equals('status', TaskStatus::PLANNED) + ) ); + + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } } - /** - * @param string $taskIdentifier - * @throws \JsonException|StopCommandException - */ - public function showCommand(string $taskIdentifier): void + public function removeByOptions($taskIdentifier, $status): int { - $task = $this->getTaskByIdentifier($taskIdentifier); - $this->outputLine(sprintf('%s (%s)', $task->getLabel(), $taskIdentifier)); - $this->outputLine(PHP_EOL . $task->getDescription() . PHP_EOL); - - $this->outputLine('Task Info'); - $this->output->outputTable( - [ - ['Cron Expression', $task->getCronExpression()], - ['First Execution', $task->getFirstExecution() === null ? '-' : $task->getFirstExecution()->format('Y-m-d H:i:s')], - ['Last Execution', $task->getLastExecution() === null ? '-' : $task->getLastExecution()->format('Y-m-d H:i:s')], - ['Handler Class', $task->getHandlerClass()], - ['Workload', $task->getWorkload() !== null ? json_encode($task->getWorkload()->getData(), JSON_THROW_ON_ERROR + JSON_PRETTY_PRINT) : '-'], - ['Next Run', $this->getNextExecutionInfo($task)], - ] + $query = $this->createQuery(); + $constraints = []; + if ($status) $constraints[] = $query->equals('status', $status); + if ($taskIdentifier) $query->equals('taskIdentifier', $taskIdentifier); + $query->matching( + $query->logicalAnd( + $constraints + ) ); - $this->outputLine(PHP_EOL . 'Task Executions'); - $taskExecutions = $this->taskExecutionRepository->findLatestExecution($task); + $removed = 0; + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + $removed++; + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } + return $removed; + } + + public function removeByIdentifier(string $identifier): void + { + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('taskIdentifier', $identifier) + ) + ); - if ($taskExecutions->count() === 0) { - $this->outputLine('This task has not yet been executed.'); - return; + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } } + } - $this->output->outputTable( - array_map(function (TaskExecution $execution) { - return [ - sprintf('%s', $execution->getScheduleTime()->format('Y-m-d H:i:s')), - number_format($execution->getDuration(), 2) . ' s', - sprintf('<%s>%s %s %s', $this->lastExecutionStatusMapping[$execution->getStatus()], $execution->getStatus(), $this->lastExecutionStatusMapping[$execution->getStatus()], $execution->getResult(), $execution->getException()), - ]; - }, $taskExecutions->toArray()), - ['Date','Run Duration', 'Status'] + public function removeByStatus(string $status): int + { + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('status', $status) + ) ); + + $removed = 0; + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + $removed++; + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } + return $removed; } - /** - * @param TaskInterface $task - * @return string - */ - private function getNextExecutionInfo(TaskInterface $task): string + public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface { - $nextExecution = $this->taskExecutionRepository->findNextScheduled((new \DateTime())->add(new \DateInterval('P10Y')), [], $task); - $nextExecutionInfo = 'Not Scheduled'; - if ($nextExecution instanceof TaskExecution) { - $nextExecutionDate = $nextExecution->getScheduleTime()->format('Y-m-d H:i:s'); - $nextExecutionInfo = $nextExecution->getScheduleTime() < (new \DateTime()) ? sprintf('%s (delayed)', $nextExecutionDate) : $nextExecutionDate; + $query = $this->createQuery(); + + $query->matching( + $query->logicalAnd( + $query->equals('taskIdentifier', $task->getIdentifier()), + $query->logicalNot( + $query->equals('status', TaskStatus::PLANNED) + ) + ) + ) + ->setOrderings(['scheduleTime' => QueryInterface::ORDER_DESCENDING]); + + if ($limit > 0) { + $query->setLimit($limit); + } + + if ($offset > 0) { + $query->setOffset($offset); } - return $nextExecutionInfo; + + return $query->execute(); } - /** - * @param string $taskIdentifier - * @return TaskInterface - * @throws StopCommandException - */ - private function getTaskByIdentifier(string $taskIdentifier): TaskInterface + public function findNextScheduled(DateTime $runTime, array $skippedExecutions = [], Task $task = null): ?TaskExecution { - try { - return $this->taskCollectionFactory->buildTasksFromConfiguration()->getTask($taskIdentifier); - } catch (\InvalidArgumentException $exception) { - $this->outputLine('No task with id "%s" is configured', [$taskIdentifier]); - $this->quit(1); + $queryBuilder = $this->createQueryBuilder('taskExecution'); + + $queryBuilder + ->where($queryBuilder->expr()->lte('taskExecution.scheduleTime', ':scheduleTime')) + ->andWhere($queryBuilder->expr()->eq('taskExecution.status', ':status')) + ->orderBy('taskExecution.scheduleTime', QueryInterface::ORDER_DESCENDING) + ->setMaxResults(1) + ->setParameter('scheduleTime', $runTime, Types::DATETIME_MUTABLE) + ->setParameter('status', TaskStatus::PLANNED); + + if (!empty($skippedExecutions)) { + $queryBuilder->andWhere( + $queryBuilder->expr()->not($queryBuilder->expr()->in('taskExecution.Persistence_Object_Identifier', ':skippedExecutions')) + )->setParameter('skippedExecutions', $skippedExecutions); + } + + if ($task !== null) { + $queryBuilder->andWhere( + $queryBuilder->expr()->eq('taskExecution.taskIdentifier', ':taskIdentifier')) + ->setParameter('taskIdentifier', $task->getIdentifier()); } + + return $queryBuilder->getQuery()->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT); } + + } diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 8ab0fa3..ef54e1b 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,6 +64,45 @@ public function removePlannedTask(Task $task): void } } + public function removeByIdentifier(string $identifier): void + { + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('taskIdentifier', $identifier) + ) + ); + + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } + } + + public function removeByStatus(string $status): int + { + $query = $this->createQuery(); + $query->matching( + $query->logicalAnd( + $query->equals('status', $status) + ) + ); + + $removed = 0; + foreach ($query->execute() as $scheduledTask) { + try { + $this->remove($scheduledTask); + $removed++; + } catch (ORMException|IllegalObjectTypeException $e) { + throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); + } + } + return $removed; + } + public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface { $query = $this->createQuery(); From 629a52ab736efd0b96d3d09295ec34566dc1caee Mon Sep 17 00:00:00 2001 From: kluge Date: Mon, 10 Aug 2026 16:48:44 +0200 Subject: [PATCH 02/14] edit reverted erroneous commit --- Classes/Command/TaskCommandController.php | 307 +++++++++--------- .../Repository/TaskExecutionRepository.php | 24 +- 2 files changed, 165 insertions(+), 166 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index a553b2e..0b5875f 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -1,183 +1,196 @@ 'error', + TaskStatus::COMPLETED => 'success', + TaskStatus::RUNNING => 'em', + TaskStatus::ABORTED => 'strike' + ]; + + /** + * @throws \Exception + */ + public function runCommand(): void { - $query = $this->createQuery(); - $query->matching( - $query->logicalAnd( - $query->equals('taskIdentifier', $task->getIdentifier()), - $query->logicalOr( - $query->equals('status', TaskStatus::PLANNED), - $query->equals('status', TaskStatus::RUNNING), - ) - ) - ); - return $query->execute(); + $this->scheduler->scheduleTasks(); + $this->taskRunner->runTasks(); + $this->taskExecutionHistory->cleanup(); } - public function findByTask(Task $task): QueryResultInterface + /** + * Run a task directly + * + * @param string $taskIdentifier + * @throws \Exception + */ + public function runSingleCommand(string $taskIdentifier): void { - $query = $this->createQuery(); - $query->matching( - $query->equals('taskIdentifier', $task->getIdentifier()), - ); - return $query->execute(); + $task = $this->getTaskByIdentifier($taskIdentifier); + $this->scheduler->scheduleTask($task); + $this->taskRunner->runTasks(); + $this->scheduler->scheduleTasks(); + $this->taskExecutionHistory->cleanup(); } - public function removePlannedTask(Task $task): void + /** + * Lists all defined tasks + * @throws \Exception + */ + public function listCommand(): void { - $query = $this->createQuery(); - $query->matching( - $query->logicalAnd( - $query->equals('taskIdentifier', $task->getIdentifier()), - $query->equals('status', TaskStatus::PLANNED) - ) - ); - - foreach ($query->execute() as $scheduledTask) { - try { - $this->remove($scheduledTask); - } catch (ORMException|IllegalObjectTypeException $e) { - throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); - } + $tasks = $this->taskCollectionFactory->buildTasksFromConfiguration()->toArray(); + if ($tasks === []) { + $this->outputLine('No tasks configured yet'); + return; } + $this->scheduler->scheduleTasks(); + + $this->output->outputTable(array_map(function (TaskInterface $task) { + /** @var TaskExecution $latestExecution */ + $latestExecution = $this->taskExecutionRepository->findLatestExecution($task, 1)->getFirst(); + return [ + $task->getIdentifier(), + $task->getLabel(), + $task->getCronExpression(), + $task->getHandlerClass(), + $latestExecution === null || $latestExecution->getEndTime() === null ? '-' : $latestExecution->getEndTime()->format('Y-m-d H:i:s') ?? $latestExecution->getStartTime()->format('Y-m-d H:i:s'), + $latestExecution === null ? '-' : sprintf('<%s>%s', $this->lastExecutionStatusMapping[$latestExecution->getStatus()], $latestExecution->getStatus(), $this->lastExecutionStatusMapping[$latestExecution->getStatus()]), + $latestExecution === null || $latestExecution->getDuration() === null ? '-' : number_format($latestExecution->getDuration(), 2) . ' s', + $this->getNextExecutionInfo($task), + ]; + }, $tasks), + ['Identifier', 'Label', 'Cron Expression', 'Handler Class', 'Previous Run Date', 'Previous Run Status', 'Previous Run Duration', 'Next Run'] + ); } - public function removeByOptions($taskIdentifier, $status): int + /** + * @param string $taskIdentifier + * @throws \JsonException|StopCommandException + */ + public function showCommand(string $taskIdentifier): void { - $query = $this->createQuery(); - $constraints = []; - if ($status) $constraints[] = $query->equals('status', $status); - if ($taskIdentifier) $query->equals('taskIdentifier', $taskIdentifier); - $query->matching( - $query->logicalAnd( - $constraints - ) + $task = $this->getTaskByIdentifier($taskIdentifier); + $this->outputLine(sprintf('%s (%s)', $task->getLabel(), $taskIdentifier)); + $this->outputLine(PHP_EOL . $task->getDescription() . PHP_EOL); + + $this->outputLine('Task Info'); + $this->output->outputTable( + [ + ['Cron Expression', $task->getCronExpression()], + ['First Execution', $task->getFirstExecution() === null ? '-' : $task->getFirstExecution()->format('Y-m-d H:i:s')], + ['Last Execution', $task->getLastExecution() === null ? '-' : $task->getLastExecution()->format('Y-m-d H:i:s')], + ['Handler Class', $task->getHandlerClass()], + ['Workload', $task->getWorkload() !== null ? json_encode($task->getWorkload()->getData(), JSON_THROW_ON_ERROR + JSON_PRETTY_PRINT) : '-'], + ['Next Run', $this->getNextExecutionInfo($task)], + ] ); - $removed = 0; - foreach ($query->execute() as $scheduledTask) { - try { - $this->remove($scheduledTask); - $removed++; - } catch (ORMException|IllegalObjectTypeException $e) { - throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); - } + $this->outputLine(PHP_EOL . 'Task Executions'); + $taskExecutions = $this->taskExecutionRepository->findLatestExecution($task); + + if ($taskExecutions->count() === 0) { + $this->outputLine('This task has not yet been executed.'); + return; } - return $removed; - } - public function removeByIdentifier(string $identifier): void - { - $query = $this->createQuery(); - $query->matching( - $query->logicalAnd( - $query->equals('taskIdentifier', $identifier) - ) + $this->output->outputTable( + array_map(function (TaskExecution $execution) { + return [ + sprintf('%s', $execution->getScheduleTime()->format('Y-m-d H:i:s')), + number_format($execution->getDuration(), 2) . ' s', + sprintf('<%s>%s %s %s', $this->lastExecutionStatusMapping[$execution->getStatus()], $execution->getStatus(), $this->lastExecutionStatusMapping[$execution->getStatus()], $execution->getResult(), $execution->getException()), + ]; + }, $taskExecutions->toArray()), + ['Date','Run Duration', 'Status'] ); - - foreach ($query->execute() as $scheduledTask) { - try { - $this->remove($scheduledTask); - } catch (ORMException|IllegalObjectTypeException $e) { - throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); - } - } } - public function removeByStatus(string $status): int + /** + * @param string|null $task Task Identifier + * @param string|null $status Status + */ + public function cleanCommand(string $task = null, string $status = null): void { - $query = $this->createQuery(); - $query->matching( - $query->logicalAnd( - $query->equals('status', $status) - ) - ); - - $removed = 0; - foreach ($query->execute() as $scheduledTask) { - try { - $this->remove($scheduledTask); - $removed++; - } catch (ORMException|IllegalObjectTypeException $e) { - throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); - } - } - return $removed; + $removed = $this->taskExecutionRepository->removeByOptions($task, $status); + $this->outputLine("Removed "+$removed+" entries"); } - public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface + /** + * @param TaskInterface $task + * @return string + */ + private function getNextExecutionInfo(TaskInterface $task): string { - $query = $this->createQuery(); - - $query->matching( - $query->logicalAnd( - $query->equals('taskIdentifier', $task->getIdentifier()), - $query->logicalNot( - $query->equals('status', TaskStatus::PLANNED) - ) - ) - ) - ->setOrderings(['scheduleTime' => QueryInterface::ORDER_DESCENDING]); - - if ($limit > 0) { - $query->setLimit($limit); - } - - if ($offset > 0) { - $query->setOffset($offset); + $nextExecution = $this->taskExecutionRepository->findNextScheduled((new \DateTime())->add(new \DateInterval('P10Y')), [], $task); + $nextExecutionInfo = 'Not Scheduled'; + if ($nextExecution instanceof TaskExecution) { + $nextExecutionDate = $nextExecution->getScheduleTime()->format('Y-m-d H:i:s'); + $nextExecutionInfo = $nextExecution->getScheduleTime() < (new \DateTime()) ? sprintf('%s (delayed)', $nextExecutionDate) : $nextExecutionDate; } - - return $query->execute(); + return $nextExecutionInfo; } - public function findNextScheduled(DateTime $runTime, array $skippedExecutions = [], Task $task = null): ?TaskExecution + /** + * @param string $taskIdentifier + * @return TaskInterface + * @throws StopCommandException + */ + private function getTaskByIdentifier(string $taskIdentifier): TaskInterface { - $queryBuilder = $this->createQueryBuilder('taskExecution'); - - $queryBuilder - ->where($queryBuilder->expr()->lte('taskExecution.scheduleTime', ':scheduleTime')) - ->andWhere($queryBuilder->expr()->eq('taskExecution.status', ':status')) - ->orderBy('taskExecution.scheduleTime', QueryInterface::ORDER_DESCENDING) - ->setMaxResults(1) - ->setParameter('scheduleTime', $runTime, Types::DATETIME_MUTABLE) - ->setParameter('status', TaskStatus::PLANNED); - - if (!empty($skippedExecutions)) { - $queryBuilder->andWhere( - $queryBuilder->expr()->not($queryBuilder->expr()->in('taskExecution.Persistence_Object_Identifier', ':skippedExecutions')) - )->setParameter('skippedExecutions', $skippedExecutions); - } - - if ($task !== null) { - $queryBuilder->andWhere( - $queryBuilder->expr()->eq('taskExecution.taskIdentifier', ':taskIdentifier')) - ->setParameter('taskIdentifier', $task->getIdentifier()); + try { + return $this->taskCollectionFactory->buildTasksFromConfiguration()->getTask($taskIdentifier); + } catch (\InvalidArgumentException $exception) { + $this->outputLine('No task with id "%s" is configured', [$taskIdentifier]); + $this->quit(1); } - - return $queryBuilder->getQuery()->getOneOrNullResult(AbstractQuery::HYDRATE_OBJECT); } - - -} +} \ No newline at end of file diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index ef54e1b..41f3278 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,30 +64,16 @@ public function removePlannedTask(Task $task): void } } - public function removeByIdentifier(string $identifier): void + public function removeByOptions(string $taskIdentifier, string $status): int { $query = $this->createQuery(); - $query->matching( - $query->logicalAnd( - $query->equals('taskIdentifier', $identifier) - ) - ); - foreach ($query->execute() as $scheduledTask) { - try { - $this->remove($scheduledTask); - } catch (ORMException|IllegalObjectTypeException $e) { - throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); - } - } - } - - public function removeByStatus(string $status): int - { - $query = $this->createQuery(); + $constraints = []; + if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); + if ($status) $constraints[] = $query->equals('status', $status); $query->matching( $query->logicalAnd( - $query->equals('status', $status) + $constraints ) ); From 3af7bb0e682c6721522e847cd9fe237b8b39d59d Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 08:36:24 +0200 Subject: [PATCH 03/14] edit added new command to Readme.md --- Readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Readme.md b/Readme.md index cfbe096..f396651 100644 --- a/Readme.md +++ b/Readme.md @@ -79,3 +79,9 @@ Show details about a specific task: ```bash ./flow task:show ``` + +Truncate task history database: + +```bash +./flow task:clean [--task=] [--status=] [--before=] +``` From 47b3311dddcbbb1a30d7b59e7df10bfc9889afdd Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 08:40:23 +0200 Subject: [PATCH 04/14] edit added date as filter option --- Classes/Command/TaskCommandController.php | 13 ++++++++++--- .../Domain/Repository/TaskExecutionRepository.php | 11 +++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index 0b5875f..02c672d 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -155,13 +155,20 @@ public function showCommand(string $taskIdentifier): void } /** + * Clean task execution history database. + * + * Removes specified entries from the task execution history database. + * + * Can filter by task, task status or date. + * * @param string|null $task Task Identifier * @param string|null $status Status + * @param string|null $before Date */ - public function cleanCommand(string $task = null, string $status = null): void + public function cleanCommand(string $task = null, string $status = null, string $before = null): void { - $removed = $this->taskExecutionRepository->removeByOptions($task, $status); - $this->outputLine("Removed "+$removed+" entries"); + $removed = $this->taskExecutionRepository->removeByOptions($task, $status, $before); + $this->outputLine("Removed ".$removed." entries"); } /** diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 41f3278..98ad491 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,13 +64,20 @@ public function removePlannedTask(Task $task): void } } - public function removeByOptions(string $taskIdentifier, string $status): int + public function removeByOptions($taskIdentifier, $status, $before): int { $query = $this->createQuery(); $constraints = []; if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); - if ($status) $constraints[] = $query->equals('status', $status); + if ($status) { + if (substr($status, 0, 1)==="!") { + $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); + } else { + $constraints[] = $query->equals('status', $status); + } + } + if ($before) $constraints[] = $query->lessThan('endtime', $before); $query->matching( $query->logicalAnd( $constraints From 4768603f8cb44bad3ed74ecf36b36541756dcf71 Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 13:36:04 +0200 Subject: [PATCH 05/14] edit reflect new command options --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index f396651..4eba727 100644 --- a/Readme.md +++ b/Readme.md @@ -4,7 +4,7 @@ This package provides a simple to use task scheduler for Neos Flow. Tasks are configured via settings, recurring tasks can be configured using cron syntax. Detailed options configure the first and last executions as well as options for the class handling the task. -Scheduling and running tasks are decoupled: The `Scheduler` schedules tasks whcih the are executed by the `TaskRunner`. This architecture allows receiving and displaying metrics of already executed tasks. +Scheduling and running tasks are decoupled: The `Scheduler` schedules tasks which then are executed by the `TaskRunner`. This architecture allows receiving and displaying metrics of already executed tasks. Most of the architectural ideas behind the package are taken from [php-task](https://github.com/php-task/php-task), and reimplemented for Neos Flow. @@ -83,5 +83,5 @@ Show details about a specific task: Truncate task history database: ```bash -./flow task:clean [--task=] [--status=] [--before=] +./flow task:clean [--task=] [--status=] [--before=] [--dry] [--verbose] ``` From 93604e6573ae89498772b0345ef2f6d2dc9181fa Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 13:36:45 +0200 Subject: [PATCH 06/14] edit added option for verbose output and multiple statuses for filter --- Classes/Command/TaskCommandController.php | 35 +++++++++++++--- .../Repository/TaskExecutionRepository.php | 41 ++++++++++++------- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index 02c672d..721206c 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -162,13 +162,38 @@ public function showCommand(string $taskIdentifier): void * Can filter by task, task status or date. * * @param string|null $task Task Identifier - * @param string|null $status Status - * @param string|null $before Date + * @param string|null $status Status, use ',' to seperate multiple, '~' to invert + * @param string|null $before Datetime + * @param bool $dry Enable dryrun, does not delete entries + * @param bool $verbose Enable Verbose output */ - public function cleanCommand(string $task = null, string $status = null, string $before = null): void + public function cleanCommand(string $task = null, string $status = null, string $before = null, bool $verbose = false, bool $dry=false): void { - $removed = $this->taskExecutionRepository->removeByOptions($task, $status, $before); - $this->outputLine("Removed ".$removed." entries"); + $confirm = true; + if (!$task && !$status && !$before && !$dry) { + $confirm = $this->output->askConfirmation("Do you want to delete all entries? (y/n)", false); + } + if ($confirm) { + $output = $this->taskExecutionRepository->removeByOptions($task, $status, $before, $dry, $verbose); + if ($verbose) { + $this->output->outputTable(array_map(function (TaskExecution $task) { + /** @var TaskExecution $latestExecution */ + return [ + $task->getTaskIdentifier(), + $this->getTaskByIdentifier($task->getTaskIdentifier())->getLabel(), + $task->getHandlerClass(), + $task->getStatus(), + $task->getScheduleTime()->format('Y-m-d H:i:s'), + $task->getStatus()!==TaskStatus::PLANNED ? $task->getStartTime()->format('Y-m-d H:i:s') : 'null', + $task->getStatus()!==TaskStatus::PLANNED ? $task->getEndTime()->format('Y-m-d H:i:s') : 'null', + ]; + }, $output), + ['Identifier', 'Label', 'Handler Class', 'Status', 'Scheduled Time', 'Start Time', 'End Time'] + ); + + } + $this->outputLine(($dry ? "Targets " : "Removed ").count($output)." entries"); + } } /** diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 98ad491..87d0dfe 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,36 +64,47 @@ public function removePlannedTask(Task $task): void } } - public function removeByOptions($taskIdentifier, $status, $before): int + public function removeByOptions($taskIdentifier, $stati, $before, bool $dry = true): array { $query = $this->createQuery(); $constraints = []; if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); - if ($status) { - if (substr($status, 0, 1)==="!") { - $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); + if ($stati) { + if (str_contains($stati, ",")) { + $statusConstraints = []; + foreach (explode(',', $stati) as $status) { + if (substr($status, 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); + else $statusConstraints[] = $query->equals('status', $status); + } + $constraints[] = $query->logicalOr($statusConstraints); } else { - $constraints[] = $query->equals('status', $status); + if (substr($stati, 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($stati, 1))); + else $constraints[] = $query->equals('status', $stati); } } - if ($before) $constraints[] = $query->lessThan('endtime', $before); - $query->matching( - $query->logicalAnd( - $constraints - ) + if ($before) $constraints[] = $query->logicalOr( + $query->lessThan('endTime', $before), + $query->lessThan('scheduleTime', $before), ); + if ($constraints) { + $query->matching( + $query->logicalAnd( + $constraints + ) + ); + } - $removed = 0; - foreach ($query->execute() as $scheduledTask) { + $targets = []; + foreach ($query->execute() as $taskExecution) { try { - $this->remove($scheduledTask); - $removed++; + if (!$dry) $this->remove($taskExecution); + $targets[] = $taskExecution; } catch (ORMException|IllegalObjectTypeException $e) { throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); } } - return $removed; + return $targets; } public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface From 7da51890a9a781cc2c6eb73d62ff6a495e87e492 Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 13:40:16 +0200 Subject: [PATCH 07/14] edit replaced str_contains with strpos --- Classes/Domain/Repository/TaskExecutionRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 87d0dfe..37a0c61 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -71,7 +71,7 @@ public function removeByOptions($taskIdentifier, $stati, $before, bool $dry = tr $constraints = []; if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); if ($stati) { - if (str_contains($stati, ",")) { + if (strpos($stati, ",")!==false) { $statusConstraints = []; foreach (explode(',', $stati) as $status) { if (substr($status, 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); From 90da2fdf7fc7f914926b43a17dc387cb42e66c49 Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 13:47:06 +0200 Subject: [PATCH 08/14] edit catch possible error --- Classes/Command/TaskCommandController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index 721206c..bd599b8 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -178,9 +178,13 @@ public function cleanCommand(string $task = null, string $status = null, string if ($verbose) { $this->output->outputTable(array_map(function (TaskExecution $task) { /** @var TaskExecution $latestExecution */ + $label = ''; + try { + $label = $this->getTaskByIdentifier($task->getTaskIdentifier())->getLabel(); + } catch (StopCommandException $exception) {} return [ $task->getTaskIdentifier(), - $this->getTaskByIdentifier($task->getTaskIdentifier())->getLabel(), + $label, $task->getHandlerClass(), $task->getStatus(), $task->getScheduleTime()->format('Y-m-d H:i:s'), From fd1cdfc317c903804c7abbf9825d04583ef7720a Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 13:58:13 +0200 Subject: [PATCH 09/14] edit clean up code --- Classes/Command/TaskCommandController.php | 6 ++--- .../Repository/TaskExecutionRepository.php | 22 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index bd599b8..b0b978f 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -7,7 +7,6 @@ use Flowpack\Task\Domain\Repository\TaskExecutionRepository; use Flowpack\Task\Domain\Runner\TaskRunner; use Flowpack\Task\Domain\Scheduler\Scheduler; -use Flowpack\Task\Domain\Task\Task; use Flowpack\Task\Domain\Task\TaskCollectionFactory; use Flowpack\Task\Domain\Task\TaskExecutionHistory; use Flowpack\Task\Domain\Task\TaskInterface; @@ -171,13 +170,14 @@ public function cleanCommand(string $task = null, string $status = null, string { $confirm = true; if (!$task && !$status && !$before && !$dry) { - $confirm = $this->output->askConfirmation("Do you want to delete all entries? (y/n)", false); + $confirm = $this->output->askConfirmation("Do you want to delete all entries? [y/N]", false); } + if ($confirm) { $output = $this->taskExecutionRepository->removeByOptions($task, $status, $before, $dry, $verbose); + if ($verbose) { $this->output->outputTable(array_map(function (TaskExecution $task) { - /** @var TaskExecution $latestExecution */ $label = ''; try { $label = $this->getTaskByIdentifier($task->getTaskIdentifier())->getLabel(); diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 37a0c61..4ab5949 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,29 +64,32 @@ public function removePlannedTask(Task $task): void } } - public function removeByOptions($taskIdentifier, $stati, $before, bool $dry = true): array + public function removeByOptions($taskIdentifier, $statuses, $before, bool $dry = true): array { $query = $this->createQuery(); $constraints = []; if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); - if ($stati) { - if (strpos($stati, ",")!==false) { + if ($statuses) { + if (strpos($statuses, ",")!==false) { $statusConstraints = []; - foreach (explode(',', $stati) as $status) { - if (substr($status, 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); + + foreach (explode(',', $statuses) as $status) { + if (substr(trim($status), 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); else $statusConstraints[] = $query->equals('status', $status); } + $constraints[] = $query->logicalOr($statusConstraints); } else { - if (substr($stati, 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($stati, 1))); - else $constraints[] = $query->equals('status', $stati); + if (substr(trim($statuses), 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($statuses, 1))); + else $constraints[] = $query->equals('status', $statuses); } } if ($before) $constraints[] = $query->logicalOr( $query->lessThan('endTime', $before), $query->lessThan('scheduleTime', $before), ); + if ($constraints) { $query->matching( $query->logicalAnd( @@ -95,11 +98,10 @@ public function removeByOptions($taskIdentifier, $stati, $before, bool $dry = tr ); } - $targets = []; - foreach ($query->execute() as $taskExecution) { + $targets = $query->execute()->toArray(); + foreach ($targets as $taskExecution) { try { if (!$dry) $this->remove($taskExecution); - $targets[] = $taskExecution; } catch (ORMException|IllegalObjectTypeException $e) { throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); } From 1c6f748f50abead4bda4f4bc575e448c247e0e33 Mon Sep 17 00:00:00 2001 From: kluge Date: Tue, 11 Aug 2026 14:19:43 +0200 Subject: [PATCH 10/14] edit changed confirmation to always be required, cleaned up code --- Classes/Command/TaskCommandController.php | 12 ++++++------ .../Domain/Repository/TaskExecutionRepository.php | 13 ++++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index b0b978f..c7400e8 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -168,13 +168,13 @@ public function showCommand(string $taskIdentifier): void */ public function cleanCommand(string $task = null, string $status = null, string $before = null, bool $verbose = false, bool $dry=false): void { + $targets = $this->taskExecutionRepository->findByOptions($task, $status, $before); + $confirm = true; - if (!$task && !$status && !$before && !$dry) { - $confirm = $this->output->askConfirmation("Do you want to delete all entries? [y/N]", false); - } + if (!$dry) $confirm = $this->output->askConfirmation("Do you want to delete ".(count($targets))." entries? [Y/n]"); if ($confirm) { - $output = $this->taskExecutionRepository->removeByOptions($task, $status, $before, $dry, $verbose); + if (!$dry) $this->taskExecutionRepository->removeEntries($targets); if ($verbose) { $this->output->outputTable(array_map(function (TaskExecution $task) { @@ -191,12 +191,12 @@ public function cleanCommand(string $task = null, string $status = null, string $task->getStatus()!==TaskStatus::PLANNED ? $task->getStartTime()->format('Y-m-d H:i:s') : 'null', $task->getStatus()!==TaskStatus::PLANNED ? $task->getEndTime()->format('Y-m-d H:i:s') : 'null', ]; - }, $output), + }, $targets), ['Identifier', 'Label', 'Handler Class', 'Status', 'Scheduled Time', 'Start Time', 'End Time'] ); } - $this->outputLine(($dry ? "Targets " : "Removed ").count($output)." entries"); + $this->outputLine(($dry ? "Targets " : "Removed ").count($targets)." entries"); } } diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 4ab5949..1a7dc46 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,7 +64,7 @@ public function removePlannedTask(Task $task): void } } - public function removeByOptions($taskIdentifier, $statuses, $before, bool $dry = true): array + public function findByOptions($taskIdentifier, $statuses, $before): array { $query = $this->createQuery(); @@ -98,15 +98,18 @@ public function removeByOptions($taskIdentifier, $statuses, $before, bool $dry = ); } - $targets = $query->execute()->toArray(); - foreach ($targets as $taskExecution) { + return $query->execute()->toArray(); + } + + public function removeEntries($entries) + { + foreach ($entries as $entry) { try { - if (!$dry) $this->remove($taskExecution); + $this->remove($entry); } catch (ORMException|IllegalObjectTypeException $e) { throw new \RuntimeException('Failed to remove task from execution repository', 1645610863, $e); } } - return $targets; } public function findLatestExecution(Task $task, int $limit = 5, int $offset = 0): QueryResultInterface From a4671abdb43ae414789478d41f1478832df89e6d Mon Sep 17 00:00:00 2001 From: kluge Date: Thu, 13 Aug 2026 08:27:50 +0200 Subject: [PATCH 11/14] edit simplified code, fixed code style --- .../Repository/TaskExecutionRepository.php | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 1a7dc46..68a885d 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -69,20 +69,26 @@ public function findByOptions($taskIdentifier, $statuses, $before): array $query = $this->createQuery(); $constraints = []; - if ($taskIdentifier) $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); + if ($taskIdentifier) { + $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); + } if ($statuses) { - if (strpos($statuses, ",")!==false) { - $statusConstraints = []; - - foreach (explode(',', $statuses) as $status) { - if (substr(trim($status), 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($status, 1))); - else $statusConstraints[] = $query->equals('status', $status); + $statusConstraints = []; + + foreach (explode(',', $statuses) as $status) { + if (substr(trim($status), 0, 1)==="~") { + $constraints[] = $query->logicalNot( + $query->equals('status', substr($status, 1)) + ); + } else { + $statusConstraints[] = $query->equals('status', $status); } + } + if (count($statusConstraints)==1) { + $constraints[] = $statusConstraints[0]; + } elseif (count($statusConstraints)>1) { $constraints[] = $query->logicalOr($statusConstraints); - } else { - if (substr(trim($statuses), 0, 1)==="~") $constraints[] = $query->logicalNot($query->equals('status', substr($statuses, 1))); - else $constraints[] = $query->equals('status', $statuses); } } if ($before) $constraints[] = $query->logicalOr( From cd40e0a9ee55975d12f912f6529095acbd98623d Mon Sep 17 00:00:00 2001 From: kluge Date: Thu, 13 Aug 2026 11:03:02 +0200 Subject: [PATCH 12/14] edit added type hints changed parameter order moved string ops to command added method documentation fixed code style --- Classes/Command/TaskCommandController.php | 23 +++++++--- .../Repository/TaskExecutionRepository.php | 44 ++++++++++++------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index c7400e8..bbdcdc9 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -161,17 +161,30 @@ public function showCommand(string $taskIdentifier): void * Can filter by task, task status or date. * * @param string|null $task Task Identifier - * @param string|null $status Status, use ',' to seperate multiple, '~' to invert * @param string|null $before Datetime + * @param string|null $status Status, use ',' to seperate multiple, '~' to invert * @param bool $dry Enable dryrun, does not delete entries * @param bool $verbose Enable Verbose output */ - public function cleanCommand(string $task = null, string $status = null, string $before = null, bool $verbose = false, bool $dry=false): void + public function cleanCommand(?string $task = null, ?string $before = null, ?string $status = null, bool $verbose = false, bool $dry=false): void { - $targets = $this->taskExecutionRepository->findByOptions($task, $status, $before); + $posStatuses = []; + $negStatuses = []; + if ($status !== null) { + foreach (explode(',', $status) as $s) { + $s = trim($s); + if (substr($s, 0, 1) === "~") { + $posStatuses[] = substr($s, 1); + } else { + $negStatuses[] = $s; + } + } + } + + $targets = $this->taskExecutionRepository->findByOptions($task, $before, $posStatuses, $negStatuses); $confirm = true; - if (!$dry) $confirm = $this->output->askConfirmation("Do you want to delete ".(count($targets))." entries? [Y/n]"); + if (!$dry) $confirm = $this->output->askConfirmation("Do you want to delete " . (count($targets)) . " entries? [Y/n]"); if ($confirm) { if (!$dry) $this->taskExecutionRepository->removeEntries($targets); @@ -196,7 +209,7 @@ public function cleanCommand(string $task = null, string $status = null, string ); } - $this->outputLine(($dry ? "Targets " : "Removed ").count($targets)." entries"); + $this->outputLine(($dry ? "Targets " : "Removed ") . count($targets) . " entries"); } } diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 68a885d..8ef4eb8 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -64,37 +64,47 @@ public function removePlannedTask(Task $task): void } } - public function findByOptions($taskIdentifier, $statuses, $before): array + /** + * Queries task executions by parameters. + * + * @param string|null $taskIdentifier + * @param string|null $before + * @param array $posStatuses + * @param array $negStatuses + * @return array + */ + public function findByOptions(?string $taskIdentifier, ?string $before, array $posStatuses, array $negStatuses): array { $query = $this->createQuery(); $constraints = []; - if ($taskIdentifier) { + if ($taskIdentifier !== null) { $constraints[] = $query->equals('taskIdentifier', $taskIdentifier); } - if ($statuses) { + if ($before !== null) $constraints[] = $query->logicalOr( + $query->lessThan('endTime', $before), + $query->lessThan('scheduleTime', $before), + ); + if ($posStatuses) { $statusConstraints = []; - foreach (explode(',', $statuses) as $status) { - if (substr(trim($status), 0, 1)==="~") { - $constraints[] = $query->logicalNot( - $query->equals('status', substr($status, 1)) - ); - } else { - $statusConstraints[] = $query->equals('status', $status); - } + foreach ($posStatuses as $posStatus) { + $statusConstraints[] = $query->equals('status', $posStatus); } - if (count($statusConstraints)==1) { + if (count($statusConstraints) === 1) { $constraints[] = $statusConstraints[0]; - } elseif (count($statusConstraints)>1) { + } elseif (count($statusConstraints) > 1) { $constraints[] = $query->logicalOr($statusConstraints); } } - if ($before) $constraints[] = $query->logicalOr( - $query->lessThan('endTime', $before), - $query->lessThan('scheduleTime', $before), - ); + if ($negStatuses) { + foreach ($negStatuses as $negStatus) { + $constraints[] = $query->logicalNot( + $query->equals('status', $negStatus) + ); + } + } if ($constraints) { $query->matching( From 97c2eb1b0813bbab9c2d7f912591c7b5b518aaf3 Mon Sep 17 00:00:00 2001 From: kluge Date: Thu, 13 Aug 2026 14:41:18 +0200 Subject: [PATCH 13/14] edit remove status negation --- Classes/Command/TaskCommandController.php | 14 ++++---------- .../Repository/TaskExecutionRepository.php | 18 +++++------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/Classes/Command/TaskCommandController.php b/Classes/Command/TaskCommandController.php index bbdcdc9..d0ed684 100644 --- a/Classes/Command/TaskCommandController.php +++ b/Classes/Command/TaskCommandController.php @@ -162,26 +162,20 @@ public function showCommand(string $taskIdentifier): void * * @param string|null $task Task Identifier * @param string|null $before Datetime - * @param string|null $status Status, use ',' to seperate multiple, '~' to invert + * @param string|null $status Status, use ',' to separate multiple * @param bool $dry Enable dryrun, does not delete entries * @param bool $verbose Enable Verbose output */ public function cleanCommand(?string $task = null, ?string $before = null, ?string $status = null, bool $verbose = false, bool $dry=false): void { - $posStatuses = []; - $negStatuses = []; + $statusArray = []; if ($status !== null) { foreach (explode(',', $status) as $s) { - $s = trim($s); - if (substr($s, 0, 1) === "~") { - $posStatuses[] = substr($s, 1); - } else { - $negStatuses[] = $s; - } + $statusArray[] = trim($s); } } - $targets = $this->taskExecutionRepository->findByOptions($task, $before, $posStatuses, $negStatuses); + $targets = $this->taskExecutionRepository->findByOptions($task, $before, $statusArray); $confirm = true; if (!$dry) $confirm = $this->output->askConfirmation("Do you want to delete " . (count($targets)) . " entries? [Y/n]"); diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 8ef4eb8..3a5e394 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -69,11 +69,10 @@ public function removePlannedTask(Task $task): void * * @param string|null $taskIdentifier * @param string|null $before - * @param array $posStatuses - * @param array $negStatuses + * @param array $statusArray * @return array */ - public function findByOptions(?string $taskIdentifier, ?string $before, array $posStatuses, array $negStatuses): array + public function findByOptions(?string $taskIdentifier, ?string $before, array $statusArray): array { $query = $this->createQuery(); @@ -85,11 +84,11 @@ public function findByOptions(?string $taskIdentifier, ?string $before, array $p $query->lessThan('endTime', $before), $query->lessThan('scheduleTime', $before), ); - if ($posStatuses) { + if ($statusArray) { $statusConstraints = []; - foreach ($posStatuses as $posStatus) { - $statusConstraints[] = $query->equals('status', $posStatus); + foreach ($statusArray as $status) { + $statusConstraints[] = $query->equals('status', $status); } if (count($statusConstraints) === 1) { @@ -98,13 +97,6 @@ public function findByOptions(?string $taskIdentifier, ?string $before, array $p $constraints[] = $query->logicalOr($statusConstraints); } } - if ($negStatuses) { - foreach ($negStatuses as $negStatus) { - $constraints[] = $query->logicalNot( - $query->equals('status', $negStatus) - ); - } - } if ($constraints) { $query->matching( From 164b6fcf17abb5b441f29351d401414ccaa78da6 Mon Sep 17 00:00:00 2001 From: kluge Date: Thu, 13 Aug 2026 14:42:50 +0200 Subject: [PATCH 14/14] edit add parameter and return type --- Classes/Domain/Repository/TaskExecutionRepository.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Classes/Domain/Repository/TaskExecutionRepository.php b/Classes/Domain/Repository/TaskExecutionRepository.php index 3a5e394..13d35e5 100644 --- a/Classes/Domain/Repository/TaskExecutionRepository.php +++ b/Classes/Domain/Repository/TaskExecutionRepository.php @@ -109,7 +109,12 @@ public function findByOptions(?string $taskIdentifier, ?string $before, array $s return $query->execute()->toArray(); } - public function removeEntries($entries) + /** + * @param array $entries + * @return void + * @throws \RuntimeException + */ + public function removeEntries(array $entries): void { foreach ($entries as $entry) { try {