diff --git a/Backend/endpoints/team.go b/Backend/endpoints/team.go index 467b4bf..efc1c7b 100644 --- a/Backend/endpoints/team.go +++ b/Backend/endpoints/team.go @@ -2,13 +2,13 @@ package endpoints import ( "arbeitszeitmessung/helper" + "arbeitszeitmessung/helper/paramParser" "arbeitszeitmessung/models" "arbeitszeitmessung/templates" "context" "errors" "log" "net/http" - "strconv" "time" ) @@ -30,17 +30,17 @@ func submitReport(w http.ResponseWriter, r *http.Request) { log.Println("Error parsing form", err) return } - userPN, _ := strconv.Atoi(r.FormValue("user")) - _weekTs := r.FormValue("week") - weekTs, err := time.Parse(time.DateOnly, _weekTs) + pp := paramParser.New(r.Form) + userPN, err := pp.ParseInt("user") + weekTs := pp.ParseTimestampFallback("week", time.DateOnly, time.Now()) user, err := models.GetUserByPersonalNr(userPN) - workWeek := models.NewWorkWeek(user, weekTs, true) - if err != nil { log.Println("Could not get user!") return } + workWeek := models.NewWorkWeek(user, weekTs, true) + switch r.FormValue("method") { case "send": err = workWeek.SendWeek() @@ -62,14 +62,11 @@ func showWeeks(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/user/login", http.StatusSeeOther) return } - submissionDate := r.URL.Query().Get("submission_date") - lastSub := user.GetLastWorkWeekSubmission() - if submissionDate != "" { - submissionDate, err := time.Parse(time.DateOnly, submissionDate) - if err == nil { - lastSub = helper.GetMonday(submissionDate) - } - } + + pp := paramParser.New(r.URL.Query()) + submissionDate := pp.ParseTimestampFallback("submission_date", time.DateOnly, user.GetLastWorkWeekSubmission()) + lastSub := helper.GetMonday(submissionDate) + userWeek := models.NewWorkWeek(user, lastSub, true) var workWeeks []models.WorkWeek diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go index 5e0f6f7..d8bb4c3 100644 --- a/Backend/endpoints/time.go +++ b/Backend/endpoints/time.go @@ -2,14 +2,15 @@ package endpoints import ( "arbeitszeitmessung/helper" + "arbeitszeitmessung/helper/paramParser" "arbeitszeitmessung/models" "arbeitszeitmessung/templates" "context" "database/sql" "encoding/json" "log" + "log/slog" "net/http" - "sort" "strconv" "time" ) @@ -67,26 +68,15 @@ func getBookings(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/user/login", http.StatusSeeOther) return } + pp := paramParser.New(r.URL.Query()) // TODO add config for timeoffset - tsFrom, err := parseTimestamp(r, "time_from", time.Now().AddDate(0, -1, 0).Format(time.DateOnly)) - if err != nil { - log.Println("Error parsing 'from' time", err) - http.Error(w, "Timestamp 'from' cannot be parsed!", http.StatusBadRequest) - return - } - tsTo, err := parseTimestamp(r, "time_to", time.Now().Format(time.DateOnly)) - if err != nil { - log.Println("Error parsing 'to' time", err) - http.Error(w, "Timestamp 'to' cannot be parsed!", http.StatusBadRequest) - return - } + tsFrom := pp.ParseTimestampFallback("time_from", time.DateOnly, time.Now().AddDate(0, -1, 0)) + tsTo := pp.ParseTimestampFallback("time_to", time.DateOnly, time.Now()) + tsTo = tsTo.AddDate(0, 0, 1) // so that today is inside days := models.GetDays(user, tsFrom, tsTo, true) - sort.Slice(days, func(i, j int) bool { - return days[i].Date().After(days[j].Date()) - }) lastSub := user.GetLastWorkWeekSubmission() var aggregatedOvertime time.Duration @@ -116,6 +106,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) { func updateBooking(w http.ResponseWriter, r *http.Request) { r.ParseForm() + pp := paramParser.New(r.Form) var loc *time.Location loc, err := time.LoadLocation(helper.GetEnv("TZ", "Europe/Berlin")) if err != nil { @@ -136,10 +127,9 @@ func updateBooking(w http.ResponseWriter, r *http.Request) { return } - var check_in_out int - check_in_out, err = strconv.Atoi(r.FormValue("check_in_out")) + check_in_out, err := pp.ParseInt("check_in_out") if err != nil { - log.Println("Error parsing check_in_out", err) + slog.Warn("Error parsing check_in_out") return } diff --git a/Backend/helper/paramParser/main.go b/Backend/helper/paramParser/main.go index 0b08c2d..472ed58 100644 --- a/Backend/helper/paramParser/main.go +++ b/Backend/helper/paramParser/main.go @@ -1,8 +1,10 @@ package paramParser import ( + "fmt" "log/slog" "net/url" + "strconv" "time" ) @@ -10,22 +12,82 @@ type ParamsParser struct { urlParams url.Values } +type NoValueError struct { + Key string +} + +func (e *NoValueError) Error() string { + return fmt.Sprintf("No value found for key %s", e.Key) +} + func New(_urlParams url.Values) ParamsParser { return ParamsParser{ urlParams: _urlParams, } } -func (p *ParamsParser) ParseTimestamp(key string, format string, fallback time.Time) time.Time { - paramTimestamp := p.urlParams.Get(key) - if paramTimestamp == "" { +func (p *ParamsParser) ParseTimestampFallback(key string, format string, fallback time.Time) time.Time { + if !p.urlParams.Has(key) { return fallback } + paramTimestamp := p.urlParams.Get(key) if timestamp, err := time.Parse(format, paramTimestamp); err == nil { return timestamp } else { - slog.Warn("Error parsing HTTP Params to timestamp", slog.Any("key", key), slog.Any("error", err)) + slog.Warn("Error parsing HTTP Params to time.Time", slog.Any("key", key), slog.Any("error", err)) return fallback } - +} + +func (p *ParamsParser) ParseTimestamp(key string, format string) (time.Time, error) { + if !p.urlParams.Has(key) { + return time.Time{}, &NoValueError{Key: key} + } + paramTimestamp := p.urlParams.Get(key) + if timestamp, err := time.Parse(format, paramTimestamp); err == nil { + return timestamp, nil + } else { + slog.Debug("Error parsing HTTP Params to time.Time", slog.Any("key", key), slog.Any("error", err)) + return timestamp, err + } +} + +func (p *ParamsParser) ParseStringFallback(key string, fallback string) string { + if !p.urlParams.Has(key) { + return fallback + } + return p.urlParams.Get(key) +} + +func (p *ParamsParser) ParseString(key string, fallback string) (string, error) { + if !p.urlParams.Has(key) { + return "", &NoValueError{Key: key} + } + return p.urlParams.Get(key), nil +} + +func (p *ParamsParser) ParseIntFallback(key string, fallback int) int { + if !p.urlParams.Has(key) { + return fallback + } + paramInt := p.urlParams.Get(key) + if result, err := strconv.Atoi(paramInt); err == nil { + return result + } else { + slog.Warn("Error parsing HTTP Params to Int", slog.Any("key", key), slog.Any("error", err)) + return fallback + } +} + +func (p *ParamsParser) ParseInt(key string) (int, error) { + if !p.urlParams.Has(key) { + return 0, &NoValueError{Key: key} + } + paramInt := p.urlParams.Get(key) + if result, err := strconv.Atoi(paramInt); err == nil { + return result, nil + } else { + slog.Debug("Error parsing HTTP Params to Int", slog.Any("key", key), slog.Any("error", err)) + return 0, err + } } diff --git a/db.sql b/db.sql index cdc1beb..7f2dfff 100644 --- a/db.sql +++ b/db.sql @@ -176,15 +176,15 @@ sample_bookings AS ( (d.work_date + make_time(16, floor(random()*50)::int, 0))::timestamptz AS ts, 1 AS anwesenheit_typ FROM days d -), -ins_anw AS ( - -- insert only bookings up to now (prevents future times on today) - INSERT INTO anwesenheit ("timestamp", card_uid, check_in_out, geraet_id) - SELECT ts, card_uid, check_in_out, geraet_id - FROM sample_bookings - WHERE ts <= NOW() - RETURNING 1 ) + +-- insert only bookings up to now (prevents future times on today) +INSERT INTO anwesenheit ("timestamp", card_uid, check_in_out, geraet_id) +SELECT ts, card_uid, check_in_out, geraet_id +FROM sample_bookings +WHERE ts <= NOW() +RETURNING 1; + -- now insert absences (uses the same days CTE) INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum) SELECT @@ -247,15 +247,13 @@ all_bookings AS ( SELECT * FROM base_bookings UNION ALL SELECT * FROM pause_bookings -), -ins_anw AS ( - INSERT INTO anwesenheit ("timestamp", "card_uid", "check_in_out", "geraet_id", "anwesenheit_typ") - SELECT ts, card_uid, check_in_out, geraet_id, 1 as anwesenheit_typ - FROM all_bookings - WHERE ts <= NOW() - ORDER BY work_date, ts - RETURNING 1 ) +INSERT INTO anwesenheit ("timestamp", "card_uid", "check_in_out", "geraet_id", "anwesenheit_typ") +SELECT ts, card_uid, check_in_out, geraet_id, 1 as anwesenheit_typ +FROM all_bookings +WHERE ts <= NOW() +ORDER BY work_date, ts; + INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum) SELECT d.card_uid,