From 1a9629e670f7e4c038d8e2f0655c866e4326a14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orlando=20Tom=C3=A1s?= Date: Thu, 2 Jan 2025 15:51:07 +0100 Subject: [PATCH] Simplify handling with custom JSON time --- .golangci.yml | 2 +- internal/shiftplan/default_rules.go | 2 +- internal/shiftplan/default_rules_test.go | 4 +- internal/shiftplan/planner_test.go | 27 ++++++++----- pkg/apis/team_methods.go | 18 +++++++++ pkg/apis/team_types.go | 51 +++--------------------- 6 files changed, 46 insertions(+), 58 deletions(-) create mode 100644 pkg/apis/team_methods.go diff --git a/.golangci.yml b/.golangci.yml index 1d5b0eb..3794790 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -137,6 +137,6 @@ issues: linters: - funlen - dupl - - path: pkg/apis/team_types.go + - path: pkg/apis/team_methods.go linters: - wrapcheck diff --git a/internal/shiftplan/default_rules.go b/internal/shiftplan/default_rules.go index 01a8c8e..3801e7f 100644 --- a/internal/shiftplan/default_rules.go +++ b/internal/shiftplan/default_rules.go @@ -21,7 +21,7 @@ func (d *DefaultRule) Match(employee apis.Employee, shifts []apis.Shift, start t func VacationConflict() *DefaultRule { return &DefaultRule{ fn: func(e apis.Employee, _ []apis.Shift, start time.Time, end time.Time) bool { - days := e.Vacations() + days := e.VacationDays for vIdx := range days { day := days[vIdx] if (day.After(start) && day.Before(end)) || day.Equal(start) || day.Equal(end) { diff --git a/internal/shiftplan/default_rules_test.go b/internal/shiftplan/default_rules_test.go index 8869e4f..adbb305 100644 --- a/internal/shiftplan/default_rules_test.go +++ b/internal/shiftplan/default_rules_test.go @@ -21,7 +21,7 @@ func TestVacationConflict(t *testing.T) { { name: "Should detect conflict if employee has holiday between scheduled shift", args: args{ - employee: apis.Employee{ID: "a", Name: "a", VacationDays: []string{"2024-01-01"}}, + employee: apis.Employee{ID: "a", Name: "a", VacationDays: vacationDays("2024-01-01")}, start: date("2024-01-01"), end: date("2024-01-02"), }, @@ -39,7 +39,7 @@ func TestVacationConflict(t *testing.T) { { name: "Should not detect conflict if employee has holiday outside scheduled shift", args: args{ - employee: apis.Employee{ID: "a", Name: "a", VacationDays: []string{"2024-01-03"}}, + employee: apis.Employee{ID: "a", Name: "a", VacationDays: vacationDays("2024-01-03")}, start: date("2024-01-01"), end: date("2024-01-02"), }, diff --git a/internal/shiftplan/planner_test.go b/internal/shiftplan/planner_test.go index 89d1e25..b59ad18 100644 --- a/internal/shiftplan/planner_test.go +++ b/internal/shiftplan/planner_test.go @@ -57,7 +57,7 @@ func TestPlanner_Plan(t *testing.T) { { name: "Should not schedule Primary on holiday days", employees: []apis.Employee{ - {ID: "a@test.ch", Name: "a", VacationDays: []string{"2020-04-01"}}, + {ID: "a@test.ch", Name: "a", VacationDays: vacationDays("2020-04-01")}, {ID: "b@test.ch", Name: "b", VacationDays: nil}, {ID: "c@test.ch", Name: "c", VacationDays: nil}, {ID: "d@test.ch", Name: "d", VacationDays: nil}, @@ -99,7 +99,7 @@ func TestPlanner_Plan(t *testing.T) { name: "Should not schedule Secondary on holiday days", employees: []apis.Employee{ {ID: "a@test.ch", Name: "a", VacationDays: nil}, - {ID: "b@test.ch", Name: "b", VacationDays: []string{"2020-04-01"}}, + {ID: "b@test.ch", Name: "b", VacationDays: vacationDays("2020-04-01")}, {ID: "c@test.ch", Name: "c", VacationDays: nil}, {ID: "d@test.ch", Name: "d", VacationDays: nil}, }, @@ -139,10 +139,10 @@ func TestPlanner_Plan(t *testing.T) { { name: "Should return an error if can not find next available duty", employees: []apis.Employee{ - {ID: "a@test.ch", Name: "a", VacationDays: []string{"2020-04-01"}}, - {ID: "b@test.ch", Name: "b", VacationDays: []string{"2020-04-01"}}, - {ID: "c@test.ch", Name: "c", VacationDays: []string{"2020-04-01"}}, - {ID: "d@test.ch", Name: "d", VacationDays: []string{"2020-04-01"}}, + {ID: "a@test.ch", Name: "a", VacationDays: vacationDays("2020-04-01")}, + {ID: "b@test.ch", Name: "b", VacationDays: vacationDays("2020-04-01")}, + {ID: "c@test.ch", Name: "c", VacationDays: vacationDays("2020-04-01")}, + {ID: "d@test.ch", Name: "d", VacationDays: vacationDays("2020-04-01")}, }, args: args{ start: date("2020-04-01"), @@ -156,9 +156,9 @@ func TestPlanner_Plan(t *testing.T) { name: "Should return an error if can not find next secondary", employees: []apis.Employee{ {ID: "a@test.ch", Name: "a", VacationDays: nil}, - {ID: "b@test.ch", Name: "b", VacationDays: []string{"2020-04-01"}}, - {ID: "c@test.ch", Name: "c", VacationDays: []string{"2020-04-01"}}, - {ID: "d@test.ch", Name: "d", VacationDays: []string{"2020-04-01"}}, + {ID: "b@test.ch", Name: "b", VacationDays: vacationDays("2020-04-01")}, + {ID: "c@test.ch", Name: "c", VacationDays: vacationDays("2020-04-01")}, + {ID: "d@test.ch", Name: "d", VacationDays: vacationDays("2020-04-01")}, }, args: args{ start: date("2020-04-01"), @@ -191,3 +191,12 @@ func date(s string) time.Time { return t } + +func vacationDays(days ...string) []apis.VacationDay { + var tmp = make([]apis.VacationDay, len(days)) + for _, day := range days { + tmp = append(tmp, apis.VacationDay{Time: date(day)}) + } + + return tmp +} diff --git a/pkg/apis/team_methods.go b/pkg/apis/team_methods.go new file mode 100644 index 0000000..679fede --- /dev/null +++ b/pkg/apis/team_methods.go @@ -0,0 +1,18 @@ +package apis + +import ( + "time" +) + +func (v *VacationDay) UnmarshalJSON(data []byte) error { + if string(data) == "null" || string(data) == `""` { + return nil + } + + t, err := time.Parse(time.DateOnly, string(data)) + if err != nil { + return err + } + *v = VacationDay{t} + return nil +} diff --git a/pkg/apis/team_types.go b/pkg/apis/team_types.go index 3032d99..af14742 100644 --- a/pkg/apis/team_types.go +++ b/pkg/apis/team_types.go @@ -1,8 +1,6 @@ package apis import ( - "encoding/json" - "fmt" "time" ) @@ -12,49 +10,12 @@ type Team struct { Employees []Employee `json:"employees"` } -type Employee struct { - ID EmployeeID `json:"id"` - Name string `json:"name"` - VacationDays []string `json:"vacationDays,omitempty"` - parsedVacations []time.Time +type VacationDay struct { + time.Time } -func (e *Employee) UnmarshalJSON(data []byte) error { - type Alias Employee - temp := &struct { - *Alias - }{ - Alias: (*Alias)(e), - } - - if err := json.Unmarshal(data, &temp); err != nil { - return err - } - - parsedDay, err := parseDateOnly(e.VacationDays) - if err != nil { - return err - } - e.parsedVacations = parsedDay - - return nil -} - -func (e *Employee) Vacations() []time.Time { - if len(e.VacationDays) != len(e.parsedVacations) { - e.parsedVacations, _ = parseDateOnly(e.VacationDays) - } - return e.parsedVacations -} - -func parseDateOnly(dateStr []string) ([]time.Time, error) { - days := make([]time.Time, len(dateStr)) - for idx, day := range dateStr { - t, err := time.Parse(time.DateOnly, day) - if err != nil { - return nil, fmt.Errorf("failed to parse date '%s'", day) - } - days[idx] = t - } - return days, nil +type Employee struct { + ID EmployeeID `json:"id"` + Name string `json:"name"` + VacationDays []VacationDay `json:"vacationDays,omitempty"` }