89 lines
4.2 KiB
Go
89 lines
4.2 KiB
Go
package storage
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"gardomatic.kleiax.de/internal/platform/validate"
|
|
)
|
|
|
|
// TaskRecurrence identifies the calendar interval of a manual task.
|
|
type TaskRecurrence string
|
|
|
|
// Supported task recurrence values. An empty value disables recurrence.
|
|
const (
|
|
TaskRecurrenceNone TaskRecurrence = ""
|
|
TaskRecurrenceDaily TaskRecurrence = "daily"
|
|
TaskRecurrenceWeekly TaskRecurrence = "weekly"
|
|
TaskRecurrenceMonthly TaskRecurrence = "monthly"
|
|
TaskRecurrenceYearly TaskRecurrence = "yearly"
|
|
)
|
|
|
|
// TaskModelInterface persists garden work items and generated task instances.
|
|
type TaskModelInterface interface {
|
|
Insert(task Task) (Task, error)
|
|
Get(gardenID, id int) (Task, error)
|
|
GetAllForGarden(gardenID int) ([]Task, error)
|
|
Update(gardenID int, task Task) (Task, error)
|
|
Delete(gardenID, id int) error
|
|
}
|
|
|
|
// ValidateTask applies persistence-independent rules for manual garden tasks.
|
|
func ValidateTask(v *validate.Validator, task Task) {
|
|
v.Check(strings.TrimSpace(task.Title) != "", "title", "must be provided")
|
|
v.Check(len(task.Title) <= 500, "title", "must not be more than 500 bytes long")
|
|
v.Check(len(task.Description) <= 10_000, "description", "must not be more than 10000 bytes long")
|
|
v.Check(task.Priority >= -100 && task.Priority <= 100, "priority", "must be between -100 and 100")
|
|
if task.PlantID != nil {
|
|
v.Check(*task.PlantID > 0, "plant_id", "must be a positive integer")
|
|
}
|
|
if task.LocationID != nil {
|
|
v.Check(*task.LocationID > 0, "location_id", "must be a positive integer")
|
|
}
|
|
if task.DueAtStart != nil && task.DueAtEnd != nil {
|
|
v.Check(!task.DueAtStart.After(*task.DueAtEnd), "due_at_end", "must not be before due_at_start")
|
|
}
|
|
v.Check(task.Recurrence == TaskRecurrenceNone || task.Recurrence == TaskRecurrenceDaily || task.Recurrence == TaskRecurrenceWeekly || task.Recurrence == TaskRecurrenceMonthly || task.Recurrence == TaskRecurrenceYearly, "recurrence", "is invalid")
|
|
if task.Recurrence != TaskRecurrenceNone {
|
|
v.Check(task.DueAtStart != nil || task.DueAtEnd != nil, "recurrence", "requires a due date")
|
|
v.Check(task.RecurrenceInterval > 0, "recurrence_interval", "must be greater than zero")
|
|
}
|
|
validateOptionalEnum(v, "plant_status_on_completion", task.PlantStatusOnCompletion, "alive", "dead", "removed", "infested", "harvested")
|
|
if task.PlantStatusOnCompletion != nil {
|
|
v.Check(task.PlantID != nil, "plant_status_on_completion", "requires a plant")
|
|
}
|
|
if task.CompletedAt != nil {
|
|
v.Check(task.CompletedBy != nil && *task.CompletedBy > 0, "completed_by", "must be set for a completed task")
|
|
}
|
|
if task.CompletedBy != nil {
|
|
v.Check(task.CompletedAt != nil, "completed_at", "must be set when completed_by is set")
|
|
}
|
|
}
|
|
|
|
// Task represents a manual or template-generated garden work item.
|
|
type Task struct {
|
|
ID int `json:"id"`
|
|
GardenID int `json:"garden_id"`
|
|
PlantID *int `json:"plant_id,omitempty"`
|
|
LocationID *int `json:"location_id,omitempty"`
|
|
TemplateID *int `json:"template_id,omitempty"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
DueAtStart *time.Time `json:"due_at_start,omitempty"`
|
|
DueAtEnd *time.Time `json:"due_at_end,omitempty"`
|
|
GeneratedFor *time.Time `json:"generated_for,omitempty"`
|
|
Recurrence TaskRecurrence `json:"recurrence,omitempty"`
|
|
RecurrenceInterval int `json:"recurrence_interval"`
|
|
RepeatFromID *int `json:"repeat_from_id,omitempty"`
|
|
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
|
CompletedBy *int `json:"completed_by,omitempty"`
|
|
Priority int `json:"priority"`
|
|
Active bool `json:"active"`
|
|
CreatedBy int `json:"created_by"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
Version int `json:"version"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
PlantStatusOnCompletion *string `json:"plant_status_on_completion,omitempty"`
|
|
}
|