Implement opal-task Phase 5: Recurrence Implementation
- Complete SpawnNextInstance() for creating recurring task instances - Implement automatic next instance spawning on task completion - Add support for 'until' date to expire recurrences - Copy tags from template to new instances - Add comprehensive recurrence tests (6 tests, all passing) - Test pattern parsing, formatting, next due calculation - Test end-to-end recurring task workflow - Test expiration with until dates
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ParseRecurrencePattern converts "1w", "2d", "1m" to time.Duration
|
||||
@@ -58,12 +60,67 @@ func CalculateNextDue(currentDue time.Time, recurrence time.Duration) time.Time
|
||||
}
|
||||
|
||||
// SpawnNextInstance creates a new task instance from completed recurring task
|
||||
// This will be implemented after we have the CRUD operations
|
||||
func SpawnNextInstance(completedInstance *Task) error {
|
||||
if completedInstance.ParentUUID == nil {
|
||||
return fmt.Errorf("task is not a recurring instance")
|
||||
}
|
||||
|
||||
// TODO: Implement after GetTask is available
|
||||
return fmt.Errorf("not implemented yet")
|
||||
// Load template
|
||||
template, err := GetTask(*completedInstance.ParentUUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load template: %w", err)
|
||||
}
|
||||
|
||||
if template.RecurrenceDuration == nil {
|
||||
return fmt.Errorf("template has no recurrence duration")
|
||||
}
|
||||
|
||||
// Calculate next due date
|
||||
var nextDue *time.Time
|
||||
if completedInstance.Due != nil {
|
||||
next := CalculateNextDue(*completedInstance.Due, *template.RecurrenceDuration)
|
||||
nextDue = &next
|
||||
}
|
||||
|
||||
// Check if we're past 'until' date
|
||||
if template.Until != nil && nextDue != nil && nextDue.After(*template.Until) {
|
||||
// Don't spawn, recurrence has expired
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create new instance
|
||||
now := time.Now()
|
||||
newInstance := &Task{
|
||||
UUID: uuid.New(),
|
||||
Status: StatusPending,
|
||||
Description: template.Description,
|
||||
Project: template.Project,
|
||||
Priority: template.Priority,
|
||||
Created: now,
|
||||
Modified: now,
|
||||
Due: nextDue,
|
||||
Scheduled: template.Scheduled,
|
||||
Wait: template.Wait,
|
||||
Until: template.Until,
|
||||
ParentUUID: &template.UUID,
|
||||
Tags: []string{},
|
||||
}
|
||||
|
||||
if err := newInstance.Save(); err != nil {
|
||||
return fmt.Errorf("failed to save new instance: %w", err)
|
||||
}
|
||||
|
||||
// Copy tags from template
|
||||
templateTags, err := template.GetTags()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get template tags: %w", err)
|
||||
}
|
||||
|
||||
for _, tag := range templateTags {
|
||||
if err := newInstance.AddTag(tag); err != nil {
|
||||
return fmt.Errorf("failed to add tag: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user