Skip to content

Commit 882d471

Browse files
committed
feat(Assignments): Implement recurrence rule checking
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent f62c60a commit 882d471

5 files changed

Lines changed: 326 additions & 6 deletions

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"html2text/html2text": "^4.3",
1818
"phpoffice/phpword": "^1.2",
1919
"ralouphie/mimey": "^1.0",
20+
"simshaun/recurr": "^5.0",
2021
"smalot/pdfparser": "^2.11"
2122
},
2223
"scripts": {

composer.lock

Lines changed: 274 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/Controller/AssignmentsApiController.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public function getUserAssignment(int $id): DataResponse {
132132
*
133133
* 200: User tasks returned
134134
* 403: User not logged in
135+
* 400: Malformed recurrence rule
135136
*/
136137
#[NoAdminRequired]
137138
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT, tags: ['assignments'])]
@@ -144,7 +145,11 @@ public function updateUserAssignment(int $id, ?string $prompt, ?string $recurren
144145
$assignment->setPrompt($prompt);
145146
}
146147
if ($recurrence !== null) {
147-
$assignment->setRecurrence($recurrence);
148+
try {
149+
$assignment->setRecurrence($recurrence);
150+
} catch (\InvalidArgumentException $e) {
151+
return new DataResponse('', HTTP::STATUS_BAD_REQUEST);
152+
}
148153
}
149154
if ($startsAt !== null) {
150155
$assignment->setStartsAt($startsAt);

lib/Db/Assignment.php

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@
1111

1212
use OCP\AppFramework\Db\Entity;
1313
use OCP\DB\Types;
14+
use Psr\Container\ContainerExceptionInterface;
15+
use Psr\Container\NotFoundExceptionInterface;
16+
use Recurr\Exception\InvalidRRule;
17+
use Recurr\RecurrenceCollection;
18+
use Recurr\Rule;
19+
use Recurr\Transformer\Constraint\AfterConstraint;
20+
use function OCP\Log\logger;
1421

1522
/**
1623
* @method \string getUserId()
1724
* @method \void setUserId(string $userId)
1825
* @method \string getPrompt()
1926
* @method \void setPrompt(string $prompt)
2027
* @method \string getRecurrence()
21-
* @method \void setRecurrence(string $recurrence)
2228
* @method \int getStartsAt()
2329
* @method \void setStartsAt(int $startsAt)
2430
* @method \int getCreatedAt()
@@ -89,11 +95,41 @@ public function jsonSerialize() {
8995
];
9096
}
9197

98+
/**
99+
* @throws \InvalidArgumentException
100+
*/
101+
public function setRecurrence(string $recurrence): void {
102+
try {
103+
new Rule($recurrence);
104+
} catch (InvalidRRule $e) {
105+
throw new \InvalidArgumentException('Invalid recurrence rule: ' . $recurrence, previous: $e);
106+
}
107+
$this->setter('recurrence', [$recurrence]);
108+
}
109+
92110
/**
93111
* Evaluates the recurrence rule and checks if a run is due
94112
*/
95113
public function isDueToRun(\DateTimeImmutable $now): bool {
96-
// TODO: Use an actual algorithm here
97-
return true;
114+
try {
115+
$startsAt = new \DateTime('@' . $this->getStartsAt());
116+
// Find recurrences after the last run or after the current time if this assignment has never run
117+
$rule = new Rule($this->getRecurrence(), $startsAt);
118+
$transformer = new \Recurr\Transformer\ArrayTransformer();
119+
$constraint = new AfterConstraint($this->getLastRunAt() !== 0 ? new \DateTime('@' . $this->getLastRunAt()) : $startsAt, true);
120+
/** @var RecurrenceCollection $collection */
121+
$collection = $transformer->transform($rule, $constraint);
122+
if ($collection->isEmpty()) {
123+
return false;
124+
}
125+
$nextRecurrence = $collection->first();
126+
if ($nextRecurrence->getStart()->getTimestamp() <= $now->getTimestamp() && $nextRecurrence->getStart()->getTimestamp() > $this->getLastRunAt()) {
127+
return true;
128+
}
129+
} catch (InvalidRRule|\Exception|NotFoundExceptionInterface|ContainerExceptionInterface $e) {
130+
// this should not happen, as we validate the rule on setRecurrence, but just in case, we catch the exception and log it
131+
logger('assistant')->error($e->getMessage(), ['exception' => $e]);
132+
}
133+
return false;
98134
}
99135
}

0 commit comments

Comments
 (0)