cron window look-ahead + crontab docs (v1.0)

This commit is contained in:
2026-07-07 23:18:20 -03:00
parent 1fa3acae74
commit d64986d2ee
4 changed files with 45 additions and 10 deletions

View File

@@ -23,6 +23,7 @@ class RecoRecoModel extends Base
public function run($now = null) public function run($now = null)
{ {
$now = $now ?: time(); $now = $now ?: time();
$horizon = $this->horizon($now);
$spawned = 0; $spawned = 0;
$task_ids = $this->db->table('task_has_metadata') $task_ids = $this->db->table('task_has_metadata')
@@ -31,13 +32,31 @@ class RecoRecoModel extends Base
->findAllByColumn('task_id'); ->findAllByColumn('task_id');
foreach ($task_ids as $task_id) { foreach ($task_ids as $task_id) {
$spawned += $this->processTemplate((int) $task_id, $now); $spawned += $this->processTemplate((int) $task_id, $now, $horizon);
} }
return $spawned; return $spawned;
} }
public function processTemplate($task_id, $now) /**
* The look-ahead horizon for this run: fire everything whose fire time is before it. Computed as
* "ceil now to the next 6h boundary, then +12h", which lands on the four daily runs:
* 05:58 -> 18:00, 11:58 -> 00:00, 17:58 -> 06:00, 23:58 -> 12:00. Gives a 12h look-ahead with a
* 6h overlap between runs, so a missed run is covered by its neighbour. Past-due occurrences are
* naturally included (their fire time is < now < horizon) -- that is the catch-up.
*
* @param int $now
* @return int
*/
private function horizon($now)
{
$startOfDay = mktime(0, 0, 0, (int) date('n', $now), (int) date('j', $now), (int) date('Y', $now));
$boundaryHour = (intdiv((int) date('G', $now), 6) + 1) * 6;
return $startOfDay + $boundaryHour * 3600 + 12 * 3600;
}
public function processTemplate($task_id, $now, $horizon)
{ {
$task = $this->taskFinderModel->getById($task_id); $task = $this->taskFinderModel->getById($task_id);
@@ -86,11 +105,12 @@ class RecoRecoModel extends Base
$this->setDue($task_id, $cursor); $this->setDue($task_id, $cursor);
} }
// Fire every occurrence whose fire time has arrived, advancing the cursor each time. // Fire every occurrence whose fire time is before this run's horizon (this includes
// past-due ones -> catch-up), advancing the cursor each time.
$spawned = 0; $spawned = 0;
$fireTime = $cursor - $daysBefore * 86400; $fireTime = $cursor - $daysBefore * 86400;
while ($fireTime <= $now && $spawned < self::CATCHUP_CAP) { while ($fireTime < $horizon && $spawned < self::CATCHUP_CAP) {
$this->spawn($task, $targetColumn); $this->spawn($task, $targetColumn);
$next = $this->calculator()->occurrenceFrom($anchor, $frequency, $lastDay, $cursor, false); $next = $this->calculator()->occurrenceFrom($anchor, $frequency, $lastDay, $cursor, false);

View File

@@ -36,7 +36,7 @@ class Plugin extends Base
public function getPluginVersion() public function getPluginVersion()
{ {
return '0.3.2'; return '1.0.0';
} }
public function getPluginHomepage() public function getPluginHomepage()

View File

@@ -21,21 +21,36 @@ leaves native recurrence untouched (the two are mutually exclusive per card).
- **Lead time:** "create the copy N days before the due date", so the card shows up early enough - **Lead time:** "create the copy N days before the due date", so the card shows up early enough
to act on; the copy is still due on the real day. to act on; the copy is still due on the real day.
## Scheduling (cron)
The engine runs from the CLI command `recoreco:run`. Add it to cron **four times a day** -- at
05:58, 11:58, 17:58 and 23:58:
```
58 5,11,17,23 * * * cd /path/to/kanboard && php cli recoreco:run >> /var/log/recoreco.log 2>&1
```
(Point the path at your install and run it as the user that owns Kanboard's data.) Each run looks
ahead 12 hours; the runs overlap by 6 hours, so a single missed run is covered by the next one, and
any occurrence whose time is already past is still caught up. `days before` shifts a copy earlier
within that lead. You can also run it by hand any time: `php cli recoreco:run`.
## Status ## Status
Early development. This version adds the **Recurring schedule** modal (a task sidebar action) and Working for **monthly by day** (with the last-day rule), driven by cron. The remaining frequencies
stores the settings in task metadata. The scheduling engine and the cron command arrive in the (yearly, weekly, daily, monthly by weekday), the recurrence icons, and the FinanceBuddy
following versions. installment hand-off arrive in the following versions.
## Requirements ## Requirements
- Kanboard >= 1.2.0 - Kanboard >= 1.2.0
- cron (to run `recoreco:run`)
## Installation ## Installation
Copy this folder into your Kanboard installation as `plugins/RecoReco/`. The directory name must be Copy this folder into your Kanboard installation as `plugins/RecoReco/`. The directory name must be
exactly `RecoReco` (Kanboard derives the plugin namespace from the folder name). No build step and exactly `RecoReco` (Kanboard derives the plugin namespace from the folder name). No build step and
no database migration. no database migration. Then add the cron entry above.
## License ## License

View File

@@ -1 +1 @@
RecoReco v0.3.2 RecoReco v1.0