118 lines
6.1 KiB
Go
118 lines
6.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"gardomatic.kleiax.de/internal/platform/validate"
|
|
)
|
|
|
|
// TaskTriggerType identifies how a species task template calculates its due window.
|
|
type TaskTriggerType string
|
|
|
|
// TaskDurationUnit is the calendar unit used for offsets and durations.
|
|
type TaskDurationUnit string
|
|
|
|
// TaskTemplateOrigin identifies whether a template was entered manually or derived from species data.
|
|
type TaskTemplateOrigin string
|
|
|
|
// Supported task duration units.
|
|
const (
|
|
TaskDurationDay TaskDurationUnit = "day"
|
|
TaskDurationWeek TaskDurationUnit = "week"
|
|
TaskDurationMonth TaskDurationUnit = "month"
|
|
)
|
|
|
|
// Supported task trigger types.
|
|
const (
|
|
// TaskTriggerMonthOfYear repeats within a calendar-year date window.
|
|
TaskTriggerMonthOfYear TaskTriggerType = "month_of_year"
|
|
// TaskTriggerRelativeToPlanting is offset from the planting date.
|
|
TaskTriggerRelativeToPlanting TaskTriggerType = "relative_to_planting"
|
|
// TaskTriggerRelativeToLastTask is offset from the last completion.
|
|
TaskTriggerRelativeToLastTask TaskTriggerType = "relative_to_last_task"
|
|
TaskTriggerRelativeToSowing TaskTriggerType = "relative_to_sowing"
|
|
TaskTriggerRelativeToHarvest TaskTriggerType = "relative_to_harvest"
|
|
TaskTriggerRelativeToSpeciesPlanting TaskTriggerType = "relative_to_species_planting"
|
|
)
|
|
|
|
// Supported template origins.
|
|
const (
|
|
TaskTemplateOriginManual TaskTemplateOrigin = "manual"
|
|
TaskTemplateOriginSeasonSowing TaskTemplateOrigin = "season_sowing"
|
|
TaskTemplateOriginSeasonPlanting TaskTemplateOrigin = "season_planting"
|
|
TaskTemplateOriginSeasonHarvest TaskTemplateOrigin = "season_harvest"
|
|
)
|
|
|
|
// SpeciesTaskTemplateModelInterface persists recurring rules for species tasks.
|
|
type SpeciesTaskTemplateModelInterface interface {
|
|
Insert(template SpeciesTaskTemplate) (SpeciesTaskTemplate, error)
|
|
Get(gardenID, id int) (SpeciesTaskTemplate, error)
|
|
GetAllForSpecies(gardenID, speciesID int) ([]SpeciesTaskTemplate, error)
|
|
Update(gardenID int, template SpeciesTaskTemplate) (SpeciesTaskTemplate, error)
|
|
Delete(gardenID, id int) error
|
|
}
|
|
|
|
// ValidateSpeciesTaskTemplate applies scheduling and recurrence rules to a
|
|
// species task template.
|
|
func ValidateSpeciesTaskTemplate(v *validate.Validator, template SpeciesTaskTemplate) {
|
|
v.Check(strings.TrimSpace(template.Title) != "", "title", "must be provided")
|
|
v.Check(len(template.Title) <= 500, "title", "must not be more than 500 bytes long")
|
|
v.Check(len(template.Description) <= 10_000, "description", "must not be more than 10000 bytes long")
|
|
v.Check(template.Priority >= -100 && template.Priority <= 100, "priority", "must be between -100 and 100")
|
|
v.Check(template.TriggerType == TaskTriggerMonthOfYear || template.TriggerType == TaskTriggerRelativeToPlanting || template.TriggerType == TaskTriggerRelativeToLastTask || template.TriggerType == TaskTriggerRelativeToSowing || template.TriggerType == TaskTriggerRelativeToHarvest || template.TriggerType == TaskTriggerRelativeToSpeciesPlanting, "trigger_type", "is invalid")
|
|
v.Check(template.Origin == TaskTemplateOriginManual || template.Origin == TaskTemplateOriginSeasonSowing || template.Origin == TaskTemplateOriginSeasonPlanting || template.Origin == TaskTemplateOriginSeasonHarvest, "origin", "is invalid")
|
|
if template.TriggerType == TaskTriggerMonthOfYear {
|
|
v.Check(template.MonthFrom != nil && *template.MonthFrom >= 1 && *template.MonthFrom <= 12, "month_from", "must be between 1 and 12")
|
|
validateTemplateDay(v, "day_from", template.DayFrom)
|
|
v.Check(template.DayFrom != nil, "day_from", "must be provided")
|
|
} else {
|
|
v.Check(template.TriggerOffset >= 0, "trigger_offset", "must not be negative")
|
|
}
|
|
v.Check(template.Duration >= 0, "duration", "must not be negative")
|
|
v.Check(validTaskDurationUnit(template.DurationUnit), "duration_unit", "is invalid")
|
|
v.Check(validTaskDurationUnit(template.TriggerOffsetUnit), "trigger_offset_unit", "is invalid")
|
|
v.Check(template.Recurrence == TaskRecurrenceNone || template.Recurrence == TaskRecurrenceDaily || template.Recurrence == TaskRecurrenceWeekly || template.Recurrence == TaskRecurrenceMonthly || template.Recurrence == TaskRecurrenceYearly, "recurrence", "is invalid")
|
|
if template.Recurrence != TaskRecurrenceNone {
|
|
v.Check(template.RecurrenceInterval > 0, "recurrence_interval", "must be greater than zero")
|
|
}
|
|
}
|
|
|
|
func validTaskDurationUnit(unit TaskDurationUnit) bool {
|
|
return unit == TaskDurationDay || unit == TaskDurationWeek || unit == TaskDurationMonth
|
|
}
|
|
|
|
func validateTemplateDay(v *validate.Validator, field string, value *int) {
|
|
if value != nil {
|
|
v.Check(*value >= 1 && *value <= 31, field, "must be between 1 and 31")
|
|
}
|
|
}
|
|
|
|
// SpeciesTaskTemplate defines a recurring task rule for a species.
|
|
type SpeciesTaskTemplate struct {
|
|
ID int `json:"id"`
|
|
SpeciesID int `json:"species_id"`
|
|
Origin TaskTemplateOrigin `json:"origin"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
TriggerType TaskTriggerType `json:"trigger_type"`
|
|
MonthFrom *int `json:"month_from,omitempty"`
|
|
DayFrom *int `json:"day_from,omitempty"`
|
|
MonthTo *int `json:"month_to,omitempty"`
|
|
DayTo *int `json:"day_to,omitempty"`
|
|
OffsetDaysFrom *int `json:"offset_days_from,omitempty"`
|
|
OffsetDaysTo *int `json:"offset_days_to,omitempty"`
|
|
IntervalDays *int `json:"interval_days,omitempty"`
|
|
TriggerOffset int `json:"trigger_offset"`
|
|
TriggerOffsetUnit TaskDurationUnit `json:"trigger_offset_unit"`
|
|
Duration int `json:"duration"`
|
|
DurationUnit TaskDurationUnit `json:"duration_unit"`
|
|
Recurrence TaskRecurrence `json:"recurrence,omitempty"`
|
|
RecurrenceInterval int `json:"recurrence_interval"`
|
|
Priority int `json:"priority"`
|
|
Active bool `json:"active"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
Version int `json:"version"`
|
|
}
|