build uppon paramParser

This commit is contained in:
2025-10-28 22:59:09 +01:00
parent a634b7a69e
commit 4bc5594dc5
4 changed files with 101 additions and 54 deletions

View File

@@ -2,13 +2,13 @@ package endpoints
import ( import (
"arbeitszeitmessung/helper" "arbeitszeitmessung/helper"
"arbeitszeitmessung/helper/paramParser"
"arbeitszeitmessung/models" "arbeitszeitmessung/models"
"arbeitszeitmessung/templates" "arbeitszeitmessung/templates"
"context" "context"
"errors" "errors"
"log" "log"
"net/http" "net/http"
"strconv"
"time" "time"
) )
@@ -30,17 +30,17 @@ func submitReport(w http.ResponseWriter, r *http.Request) {
log.Println("Error parsing form", err) log.Println("Error parsing form", err)
return return
} }
userPN, _ := strconv.Atoi(r.FormValue("user")) pp := paramParser.New(r.Form)
_weekTs := r.FormValue("week") userPN, err := pp.ParseInt("user")
weekTs, err := time.Parse(time.DateOnly, _weekTs) weekTs := pp.ParseTimestampFallback("week", time.DateOnly, time.Now())
user, err := models.GetUserByPersonalNr(userPN) user, err := models.GetUserByPersonalNr(userPN)
workWeek := models.NewWorkWeek(user, weekTs, true)
if err != nil { if err != nil {
log.Println("Could not get user!") log.Println("Could not get user!")
return return
} }
workWeek := models.NewWorkWeek(user, weekTs, true)
switch r.FormValue("method") { switch r.FormValue("method") {
case "send": case "send":
err = workWeek.SendWeek() err = workWeek.SendWeek()
@@ -62,14 +62,11 @@ func showWeeks(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/user/login", http.StatusSeeOther) http.Redirect(w, r, "/user/login", http.StatusSeeOther)
return return
} }
submissionDate := r.URL.Query().Get("submission_date")
lastSub := user.GetLastWorkWeekSubmission() pp := paramParser.New(r.URL.Query())
if submissionDate != "" { submissionDate := pp.ParseTimestampFallback("submission_date", time.DateOnly, user.GetLastWorkWeekSubmission())
submissionDate, err := time.Parse(time.DateOnly, submissionDate) lastSub := helper.GetMonday(submissionDate)
if err == nil {
lastSub = helper.GetMonday(submissionDate)
}
}
userWeek := models.NewWorkWeek(user, lastSub, true) userWeek := models.NewWorkWeek(user, lastSub, true)
var workWeeks []models.WorkWeek var workWeeks []models.WorkWeek

View File

@@ -2,14 +2,15 @@ package endpoints
import ( import (
"arbeitszeitmessung/helper" "arbeitszeitmessung/helper"
"arbeitszeitmessung/helper/paramParser"
"arbeitszeitmessung/models" "arbeitszeitmessung/models"
"arbeitszeitmessung/templates" "arbeitszeitmessung/templates"
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"log" "log"
"log/slog"
"net/http" "net/http"
"sort"
"strconv" "strconv"
"time" "time"
) )
@@ -67,26 +68,15 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/user/login", http.StatusSeeOther) http.Redirect(w, r, "/user/login", http.StatusSeeOther)
return return
} }
pp := paramParser.New(r.URL.Query())
// TODO add config for timeoffset // TODO add config for timeoffset
tsFrom, err := parseTimestamp(r, "time_from", time.Now().AddDate(0, -1, 0).Format(time.DateOnly)) tsFrom := pp.ParseTimestampFallback("time_from", time.DateOnly, time.Now().AddDate(0, -1, 0))
if err != nil { tsTo := pp.ParseTimestampFallback("time_to", time.DateOnly, time.Now())
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
}
tsTo = tsTo.AddDate(0, 0, 1) // so that today is inside tsTo = tsTo.AddDate(0, 0, 1) // so that today is inside
days := models.GetDays(user, tsFrom, tsTo, true) 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() lastSub := user.GetLastWorkWeekSubmission()
var aggregatedOvertime time.Duration var aggregatedOvertime time.Duration
@@ -116,6 +106,7 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
func updateBooking(w http.ResponseWriter, r *http.Request) { func updateBooking(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
pp := paramParser.New(r.Form)
var loc *time.Location var loc *time.Location
loc, err := time.LoadLocation(helper.GetEnv("TZ", "Europe/Berlin")) loc, err := time.LoadLocation(helper.GetEnv("TZ", "Europe/Berlin"))
if err != nil { if err != nil {
@@ -136,10 +127,9 @@ func updateBooking(w http.ResponseWriter, r *http.Request) {
return return
} }
var check_in_out int check_in_out, err := pp.ParseInt("check_in_out")
check_in_out, err = strconv.Atoi(r.FormValue("check_in_out"))
if err != nil { if err != nil {
log.Println("Error parsing check_in_out", err) slog.Warn("Error parsing check_in_out")
return return
} }

View File

@@ -1,8 +1,10 @@
package paramParser package paramParser
import ( import (
"fmt"
"log/slog" "log/slog"
"net/url" "net/url"
"strconv"
"time" "time"
) )
@@ -10,22 +12,82 @@ type ParamsParser struct {
urlParams url.Values 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 { func New(_urlParams url.Values) ParamsParser {
return ParamsParser{ return ParamsParser{
urlParams: _urlParams, urlParams: _urlParams,
} }
} }
func (p *ParamsParser) ParseTimestamp(key string, format string, fallback time.Time) time.Time { func (p *ParamsParser) ParseTimestampFallback(key string, format string, fallback time.Time) time.Time {
paramTimestamp := p.urlParams.Get(key) if !p.urlParams.Has(key) {
if paramTimestamp == "" {
return fallback return fallback
} }
paramTimestamp := p.urlParams.Get(key)
if timestamp, err := time.Parse(format, paramTimestamp); err == nil { if timestamp, err := time.Parse(format, paramTimestamp); err == nil {
return timestamp return timestamp
} else { } 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 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
}
} }

30
db.sql
View File

@@ -176,15 +176,15 @@ sample_bookings AS (
(d.work_date + make_time(16, floor(random()*50)::int, 0))::timestamptz AS ts, (d.work_date + make_time(16, floor(random()*50)::int, 0))::timestamptz AS ts,
1 AS anwesenheit_typ 1 AS anwesenheit_typ
FROM days d 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) -- now insert absences (uses the same days CTE)
INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum) INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum)
SELECT SELECT
@@ -247,15 +247,13 @@ all_bookings AS (
SELECT * FROM base_bookings SELECT * FROM base_bookings
UNION ALL UNION ALL
SELECT * FROM pause_bookings 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) INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum)
SELECT SELECT
d.card_uid, d.card_uid,