146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func buildPlanningTargets(fields []Field) []PlanningTarget {
|
|
groupMap := make(map[string][]Field)
|
|
var out []PlanningTarget
|
|
for _, f := range fields {
|
|
if !f.Owned {
|
|
continue
|
|
}
|
|
if f.GroupKey == "" {
|
|
out = append(out, PlanningTarget{Ref: fmt.Sprintf("f:%d", f.ID), Label: fieldLabel(f)})
|
|
continue
|
|
}
|
|
groupMap[f.GroupKey] = append(groupMap[f.GroupKey], f)
|
|
}
|
|
groupKeys := make([]string, 0, len(groupMap))
|
|
for k := range groupMap {
|
|
groupKeys = append(groupKeys, k)
|
|
}
|
|
sort.Strings(groupKeys)
|
|
for _, k := range groupKeys {
|
|
members := groupMap[k]
|
|
sort.Slice(members, func(i, j int) bool { return members[i].Number < members[j].Number })
|
|
name := members[0].GroupName
|
|
if strings.TrimSpace(name) == "" {
|
|
name = autoGroupName(members)
|
|
}
|
|
out = append(out, PlanningTarget{Ref: "g:" + k, Label: name})
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Label < out[j].Label })
|
|
return out
|
|
}
|
|
|
|
func buildFieldGroups(fields []Field) []FieldGroup {
|
|
groupMap := make(map[string][]Field)
|
|
for _, f := range fields {
|
|
if strings.TrimSpace(f.GroupKey) == "" {
|
|
continue
|
|
}
|
|
groupMap[f.GroupKey] = append(groupMap[f.GroupKey], f)
|
|
}
|
|
|
|
var out []FieldGroup
|
|
for key, members := range groupMap {
|
|
sort.Slice(members, func(i, j int) bool { return members[i].Number < members[j].Number })
|
|
nums := make([]string, 0, len(members))
|
|
for _, m := range members {
|
|
nums = append(nums, strconv.Itoa(m.Number))
|
|
}
|
|
name := members[0].GroupName
|
|
if strings.TrimSpace(name) == "" {
|
|
name = autoGroupName(members)
|
|
}
|
|
out = append(out, FieldGroup{Key: key, Name: name, Numbers: strings.Join(nums, "+")})
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
|
return out
|
|
}
|
|
|
|
func autoGroupName(fields []Field) string {
|
|
sort.Slice(fields, func(i, j int) bool { return fields[i].Number < fields[j].Number })
|
|
parts := make([]string, 0, len(fields))
|
|
for _, f := range fields {
|
|
parts = append(parts, strconv.Itoa(f.Number))
|
|
}
|
|
return "Feld " + strings.Join(parts, "+")
|
|
}
|
|
|
|
func fieldLabel(f Field) string {
|
|
base := fmt.Sprintf("Feld %d", f.Number)
|
|
if strings.TrimSpace(f.Name) != "" {
|
|
base += " (" + f.Name + ")"
|
|
}
|
|
return base
|
|
}
|
|
|
|
func monthOptions() []MonthOption {
|
|
out := make([]MonthOption, 0, 12)
|
|
for i, m := range monthNames {
|
|
out = append(out, MonthOption{Value: i + 1, Label: m})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func cycleOffsetOptions(maxOffset int) []CycleOption {
|
|
if maxOffset < 0 {
|
|
maxOffset = 0
|
|
}
|
|
out := make([]CycleOption, 0, maxOffset+1)
|
|
for i := 0; i <= maxOffset; i++ {
|
|
out = append(out, CycleOption{
|
|
Value: i,
|
|
Label: fmt.Sprintf("+%d Zyklus", i),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func monthInWindow(month, start, end int) bool {
|
|
if start <= end {
|
|
return month >= start && month <= end
|
|
}
|
|
return month >= start || month <= end
|
|
}
|
|
|
|
func wrapMonth(v int) int {
|
|
m := v % 12
|
|
if m == 0 {
|
|
return 12
|
|
}
|
|
return m
|
|
}
|
|
|
|
func periodMonth(period int) int {
|
|
return (period % 12) + 1
|
|
}
|
|
|
|
func periodCycle(period int) int {
|
|
return period / 12
|
|
}
|
|
|
|
func validateCropInput(name string, start, end, grow, regrowCycles int) error {
|
|
if name == "" {
|
|
return errors.New("Name der Feldfrucht fehlt")
|
|
}
|
|
if start < 1 || start > 12 || end < 1 || end > 12 {
|
|
return errors.New("Aussaatmonat muss 1-12 sein")
|
|
}
|
|
if grow < 1 || grow > 24 {
|
|
return errors.New("Wachstumsdauer muss 1-24 sein")
|
|
}
|
|
if regrowCycles < 0 || regrowCycles > 120 {
|
|
return errors.New("Regrow-Zyklen müssen 0-120 sein")
|
|
}
|
|
return nil
|
|
}
|
|
|