Implement opal-task Phase 3: Filter and Modifier Parsing

- Add filter.go: Parse filters (+tag, -tag, attribute:value, IDs)
- Implement Filter.ToSQL() for WHERE clause generation
- Add modifier.go: Parse modifiers (set/clear attributes, add/remove tags)
- Implement Modifier.Apply() to update existing tasks
- Add dateparse.go: Smart date parsing (ISO, today, tomorrow, weekdays)
- Implement nextWeekday logic (smart Sunday interpretation)
- Update GetTasks() to accept Filter parameter
- Add CreateTaskWithModifier() for task creation with modifiers
- Add comprehensive test suite (13 new tests, all passing)
- Support filtering by status, project, priority, tags, UUIDs, display IDs
- Support modifying priority, project, dates, recurrence, tags
This commit is contained in:
2026-01-04 14:48:43 +01:00
parent 7c6ec97c62
commit c99a4a2d95
7 changed files with 999 additions and 7 deletions
+38 -6
View File
@@ -139,6 +139,11 @@ func sqlToUUIDPtr(v interface{}) *uuid.UUID {
// CreateTask creates a new task with the given description
func CreateTask(description string) (*Task, error) {
return CreateTaskWithModifier(description, nil)
}
// CreateTaskWithModifier creates a new task with the given description and applies modifiers
func CreateTaskWithModifier(description string, mod *Modifier) (*Task, error) {
now := timeNow()
task := &Task{
UUID: uuid.New(),
@@ -150,10 +155,26 @@ func CreateTask(description string) (*Task, error) {
Tags: []string{},
}
// Apply modifiers before saving (for attributes)
if mod != nil {
if err := mod.ApplyToNew(task); err != nil {
return nil, err
}
}
if err := task.Save(); err != nil {
return nil, err
}
// Apply tags after saving (requires task.ID)
if mod != nil {
for _, tag := range mod.AddTags {
if err := task.AddTag(tag); err != nil {
return nil, err
}
}
}
return task, nil
}
@@ -242,22 +263,33 @@ func GetTask(taskUUID uuid.UUID) (*Task, error) {
return task, nil
}
// GetTasks retrieves all tasks (filtering will be added later)
func GetTasks() ([]*Task, error) {
// GetTasks retrieves all tasks with optional filtering
func GetTasks(filter *Filter) ([]*Task, error) {
db := GetDB()
if db == nil {
return nil, fmt.Errorf("database not initialized")
}
query := `
// Build WHERE clause from filter
whereClause := "1=1"
var args []interface{}
if filter != nil {
whereClause, args = filter.ToSQL()
}
query := fmt.Sprintf(`
SELECT id, uuid, status, description, project, priority,
created, modified, start, end, due, scheduled, wait, until_date,
recurrence_duration, parent_uuid
FROM tasks
ORDER BY due ASC, priority DESC
`
WHERE %s
ORDER BY
CASE WHEN due IS NULL THEN 1 ELSE 0 END,
due ASC,
priority DESC
`, whereClause)
rows, err := db.Query(query)
rows, err := db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf("failed to query tasks: %w", err)
}