From 68000a0f0a5ca2e04ca7cfc0c8050bbd4c89fa3b Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 17 Jul 2025 19:28:21 +0200 Subject: [PATCH 01/52] cleanup/small refactor + first tests --- Backend/endpoints/team.go | 49 +---------------------------- Backend/endpoints/time-create.go | 13 ++++---- Backend/endpoints/time.go | 12 ++++---- Backend/helper/system_test.go | 53 ++++++++++++++++++++++++++++++++ Backend/helper/time.go | 16 ++++++++++ Backend/helper/time_test.go | 17 ++++++++++ 6 files changed, 99 insertions(+), 61 deletions(-) create mode 100644 Backend/helper/system_test.go create mode 100644 Backend/helper/time.go create mode 100644 Backend/helper/time_test.go diff --git a/Backend/endpoints/team.go b/Backend/endpoints/team.go index c28cc72..9b214ea 100644 --- a/Backend/endpoints/team.go +++ b/Backend/endpoints/team.go @@ -25,18 +25,6 @@ func TeamHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed) break } - // user, err := (*models.User).GetUserFromSession(nil, Session, r.Context()) - // if err != nil { - // log.Println("No user found with the given personal number!") - // http.Redirect(w, r, "/user/login", http.StatusSeeOther) - // return - // } - // var userWorkDays []models.WorkDay - // userWorkDays = (*models.WorkDay).GetWorkDays(nil, user.CardUID, time.Date(2025, time.February, 24, 0, 0, 0, 0, time.Local), time.Date(2025, time.February, 24+7, 0, 0, 0, 0, time.Local)) - // log.Println("User:", user) - // teamMembers, err := user.GetTeamMembers() - // getWeeksTillNow(time.Now().AddDate(0, 0, -14)) - // templates.TeamPage(teamMembers, userWorkDays).Render(r.Context(), w) } func submitReport(w http.ResponseWriter, r *http.Request) { @@ -84,7 +72,7 @@ func showWeeks(w http.ResponseWriter, r *http.Request) { if submissionDate != "" { submissionDate, err := time.Parse("2006-01-02", submissionDate) if err == nil { - lastSub = getMonday(submissionDate) + lastSub = helper.GetMonday(submissionDate) } } userWeek := (*models.WorkWeek).GetWeek(nil, user, lastSub, true) @@ -98,38 +86,3 @@ func showWeeks(w http.ResponseWriter, r *http.Request) { // isRunningWeek := time.Since(lastSub) < 24*5*time.Hour //the last submission is this week and cannot be send yet templates.TeamPage(workWeeks, userWeek).Render(r.Context(), w) } - -func getWeeksTillNow(lastWeek time.Time) []time.Time { - var weeks []time.Time - if lastWeek.After(time.Now()) { - log.Println("Timestamp is after today, no weeks till now!") - return weeks - } - if lastWeek.Weekday() != time.Monday { - if lastWeek.Weekday() == time.Sunday { - lastWeek = lastWeek.AddDate(0, 0, -6) - } else { - lastWeek = lastWeek.AddDate(0, 0, -int(lastWeek.Weekday()-1)) - } - } - if time.Since(lastWeek) < 24*5*time.Hour { - log.Println("Timestamp in running week, cannot split!") - } - - for t := lastWeek; t.Before(time.Now()); t = t.Add(7 * 24 * time.Hour) { - weeks = append(weeks, t) - } - log.Println(weeks) - return weeks -} - -func getMonday(ts time.Time) time.Time { - if ts.Weekday() != time.Monday { - if ts.Weekday() == time.Sunday { - ts = ts.AddDate(0, 0, -6) - } else { - ts = ts.AddDate(0, 0, -int(ts.Weekday()-1)) - } - } - return ts -} diff --git a/Backend/endpoints/time-create.go b/Backend/endpoints/time-create.go index ddeb0a4..7dd0e29 100644 --- a/Backend/endpoints/time-create.go +++ b/Backend/endpoints/time-create.go @@ -33,7 +33,7 @@ func TimeCreateHandler(w http.ResponseWriter, r *http.Request) { // Creates a booking from the http query params -> no body needed // after that entry wi'll be written to database and the booking is returned as json func createBooking(w http.ResponseWriter, r *http.Request) { - if !checkPassword(r) { + if !verifyToken(r) { log.Println("Wrong or no API key provided!") http.Error(w, "Wrong or no API key provided", http.StatusUnauthorized) return @@ -58,16 +58,15 @@ func createBooking(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) } -func checkPassword(r *http.Request) bool { +func verifyToken(r *http.Request) bool { authToken := helper.GetEnv("API_TOKEN", "dont_access") authHeaders := r.Header.Get("Authorization") - _authStart := len("Bearer ") - if len(authHeaders) <= _authStart { + if len(authHeaders) <= 7 { //len "Bearer " authHeaders = r.URL.Query().Get("api_key") - _authStart = 0 - if len(authHeaders) <= _authStart { + if len(authHeaders) <= 0 { return false } + return authToken == authHeaders } - return authToken == authHeaders[_authStart:] + return authToken == authHeaders[7:] } diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go index 36abc91..3179995 100644 --- a/Backend/endpoints/time.go +++ b/Backend/endpoints/time.go @@ -34,16 +34,16 @@ func TimeHandler(w http.ResponseWriter, r *http.Request) { } } -func parseTimestamp(r *http.Request, get_key string, fallback string) (time.Time, error) { - _timestamp_get := r.URL.Query().Get(get_key) - if _timestamp_get == "" { - _timestamp_get = fallback +func parseTimestamp(r *http.Request, getKey string, fallback string) (time.Time, error) { + getTimestamp := r.URL.Query().Get(getKey) + if getTimestamp == "" { + getTimestamp = fallback } - timestamp_get, err := time.Parse("2006-01-02", _timestamp_get) + Timestamp, err := time.Parse("2006-01-02", getTimestamp) if err != nil { return time.Now(), err } - return timestamp_get, nil + return Timestamp, nil } // Returns bookings from DB with similar card uid -> checks for card uid in http query params diff --git a/Backend/helper/system_test.go b/Backend/helper/system_test.go new file mode 100644 index 0000000..dc944d9 --- /dev/null +++ b/Backend/helper/system_test.go @@ -0,0 +1,53 @@ +package helper + +import ( + "os" + "testing" + "time" +) + +func TestGetEnv(t *testing.T) { + os.Setenv("GO_TEST_VALUE", "123") + env := GetEnv("GO_TEST_VALUE", "") + if env != "123" { + t.Error("GetEnv() cannot find value") + } +} + +func TestGetEnvEmpty(t *testing.T) { + env := GetEnv("GO_TEST_NOVALUE", "123") + if env != "123" { + t.Errorf("GetEnv() did not use default value: want=%s got=%s", "123", env) + } +} + +func TestCacheCreate(t *testing.T) { + cacheFetch := func(key string) (any, error) { + return "123", nil + } + + cache := NewCache(1*time.Second, cacheFetch) + if cache.ttl != 1*time.Second { + t.Error("Error creating cache") + } +} + +func TestCacheFunction(t *testing.T) { + counter := 1 + cacheFetch := func(key string) (any, error) { + counter += 1 + return counter, nil + } + + cache := NewCache(1*time.Millisecond, cacheFetch) + valInit, err := cache.Get("TEST") + valCache, err := cache.Get("TEST") + time.Sleep(1 * time.Millisecond) + valNoCache, err := cache.Get("TEST") + if err != nil { + t.Errorf("Error getting key from Cache: %e", err) + } + if valInit != valCache || valCache != 2 || valNoCache != 3 { + t.Error("Caching does not resprect ttl.") + } +} diff --git a/Backend/helper/time.go b/Backend/helper/time.go new file mode 100644 index 0000000..5abd82c --- /dev/null +++ b/Backend/helper/time.go @@ -0,0 +1,16 @@ +package helper + +import ( + "time" +) + +func GetMonday(ts time.Time) time.Time { + if ts.Weekday() != time.Monday { + if ts.Weekday() == time.Sunday { + return ts.AddDate(0, 0, -6) + } else { + return ts.AddDate(0, 0, -int(ts.Weekday()-1)) + } + } + return ts +} diff --git a/Backend/helper/time_test.go b/Backend/helper/time_test.go new file mode 100644 index 0000000..c82c4c4 --- /dev/null +++ b/Backend/helper/time_test.go @@ -0,0 +1,17 @@ +package helper + +import ( + "testing" + "time" +) + +func TestGetMonday(t *testing.T) { + isMonday, err := time.Parse("2006-01-02", "2025-07-14") + notMonday, err := time.Parse("2006-01-02", "2025-07-16") + if err != nil || isMonday == notMonday { + t.Errorf("U stupid? %e", err) + } + if GetMonday(isMonday) != isMonday || GetMonday(notMonday) != isMonday { + t.Error("Wrong date conversion!") + } +} From 4201ed7b1c5ec81edc427b0bbe409796fc239110 Mon Sep 17 00:00:00 2001 From: tom Date: Sun, 20 Jul 2025 19:34:16 +0200 Subject: [PATCH 02/52] removed Formatduration and fixed rounding error, working on overtime calculation --- Backend/endpoints/time.go | 2 +- Backend/helper/time.go | 21 ++ Backend/helper/time_test.go | 19 ++ Backend/models/booking.go | 2 +- Backend/models/workDay.go | 26 +- Backend/models/workWeek.go | 3 +- Backend/templates/timeComponents.templ | 4 + Backend/templates/timeComponents_templ.go | 332 ++++++++++++---------- 8 files changed, 230 insertions(+), 179 deletions(-) diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go index 3179995..477ad96 100644 --- a/Backend/endpoints/time.go +++ b/Backend/endpoints/time.go @@ -116,7 +116,7 @@ func updateBooking(w http.ResponseWriter, r *http.Request) { newBooking := (*models.Booking).New(nil, user.CardUID, 0, int16(check_in_out)) newBooking.Timestamp = timestamp - err = newBooking.InsertTimestamp() + err = newBooking.InsertWithTimestamp() if err != nil { log.Println("Error inserting booking", err) } diff --git a/Backend/helper/time.go b/Backend/helper/time.go index 5abd82c..70ace8a 100644 --- a/Backend/helper/time.go +++ b/Backend/helper/time.go @@ -1,6 +1,7 @@ package helper import ( + "fmt" "time" ) @@ -14,3 +15,23 @@ func GetMonday(ts time.Time) time.Time { } return ts } + +// Converts duration to string +func FormatDuration(d time.Duration) string { + hours := int(d.Abs().Hours()) + minutes := int(d.Abs().Minutes()) % 60 + sign := "" + if d < 0 { + sign = "-" + } + switch { + case hours > 0 && minutes == 0: + return fmt.Sprintf("%s%dh", sign, hours) + case hours > 0: + return fmt.Sprintf("%s%dh %dmin", sign, hours, minutes) + case minutes > 0: + return fmt.Sprintf("%s%dmin", sign, minutes) + default: + return "" + } +} diff --git a/Backend/helper/time_test.go b/Backend/helper/time_test.go index c82c4c4..d106e7f 100644 --- a/Backend/helper/time_test.go +++ b/Backend/helper/time_test.go @@ -15,3 +15,22 @@ func TestGetMonday(t *testing.T) { t.Error("Wrong date conversion!") } } + +func TestFormatDuration(t *testing.T) { + durations := []struct { + name string + duration time.Duration + }{ + {"2h", time.Duration(120 * time.Minute)}, + {"30min", time.Duration(30 * time.Minute)}, + {"1h 30min", time.Duration(90 * time.Minute)}, + {"-1h 30min", time.Duration(-90 * time.Minute)}, + } + for _, d := range durations { + t.Run(d.name, func(t *testing.T) { + if FormatDuration(d.duration) != d.name { + t.Error("Format missmatch in Formatduration.") + } + }) + } +} diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 47c2c47..24533c2 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -70,7 +70,7 @@ func (b *Booking) Insert() error { return nil } -func (b *Booking) InsertTimestamp() error { +func (b *Booking) InsertWithTimestamp() error { if b.Timestamp.IsZero() { return b.Insert() } diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index d6d637a..6585742 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -4,7 +4,6 @@ import ( "arbeitszeitmessung/helper" "database/sql" "encoding/json" - "fmt" "log" "strconv" "time" @@ -180,23 +179,9 @@ func (d *WorkDay) getWorkTime() { d.calcPauseTime() } -// Converts duration to string -func formatDuration(d time.Duration) string { - hours := int(d.Abs().Hours()) - minutes := int(d.Abs().Minutes()) % 60 - switch { - case hours > 0: - return fmt.Sprintf("%dh %dmin", hours, minutes) - case minutes > 0: - return fmt.Sprintf("%dmin", minutes) - default: - return "" - } -} - func (d *WorkDay) GetWorkTimeString() (string, string) { - workString := formatDuration(d.workTime) - pauseString := formatDuration(d.pauseTime) + workString := helper.FormatDuration(d.workTime) + pauseString := helper.FormatDuration(d.pauseTime) return workString, pauseString } @@ -210,7 +195,12 @@ func (d *WorkDay) RequiresAction() bool { // returns a integer percentage of how much day has been worked of func (d *WorkDay) GetWorkDayProgress(user User) uint8 { - defaultWorkTime := time.Duration(user.ArbeitszeitPerTag * float32(time.Hour)) + defaultWorkTime := time.Duration(user.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute) progress := (d.workTime.Seconds() / defaultWorkTime.Seconds()) * 100 return uint8(progress) } + +func (d *WorkDay) CalcOvertime(user User) time.Duration { + overtime := d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute) + return overtime +} diff --git a/Backend/models/workWeek.go b/Backend/models/workWeek.go index 1ce0a30..7bd4beb 100644 --- a/Backend/models/workWeek.go +++ b/Backend/models/workWeek.go @@ -1,6 +1,7 @@ package models import ( + "arbeitszeitmessung/helper" "database/sql" "errors" "log" @@ -60,7 +61,7 @@ func (w *WorkWeek) CheckStatus() WeekStatus { } func (w *WorkWeek) GetWorkHourString() string { - return formatDuration(w.WorkHours) + return helper.FormatDuration(w.WorkHours) } func aggregateWorkTime(days []WorkDay) time.Duration { diff --git a/Backend/templates/timeComponents.templ b/Backend/templates/timeComponents.templ index 9a7b68a..576ff9e 100644 --- a/Backend/templates/timeComponents.templ +++ b/Backend/templates/timeComponents.templ @@ -1,6 +1,7 @@ package templates import ( + "arbeitszeitmessung/helper" "arbeitszeitmessung/models" "fmt" "net/url" @@ -39,6 +40,8 @@ templ inputForm() { templ dayComponent(workDay models.WorkDay) { {{ work, pause := workDay.GetWorkTimeString() + user := ctx.Value("user").(models.User) + overtime := helper.FormatDuration(workDay.CalcOvertime(user)) justify := "" if len(workDay.Bookings) <= 1 { justify = "justify-content: center" @@ -57,6 +60,7 @@ templ dayComponent(workDay models.WorkDay) {

{ work }

}

{ pause }

+

{ overtime }

} diff --git a/Backend/templates/timeComponents_templ.go b/Backend/templates/timeComponents_templ.go index c109fb7..75776c4 100644 --- a/Backend/templates/timeComponents_templ.go +++ b/Backend/templates/timeComponents_templ.go @@ -9,6 +9,7 @@ import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" import ( + "arbeitszeitmessung/helper" "arbeitszeitmessung/models" "fmt" "net/url" @@ -47,7 +48,7 @@ func inputForm() templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname + " " + user.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 18, Col: 66} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 19, Col: 66} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -68,7 +69,7 @@ func inputForm() templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 27, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -81,7 +82,7 @@ func inputForm() templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 55} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 29, Col: 55} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -118,6 +119,8 @@ func dayComponent(workDay models.WorkDay) templ.Component { ctx = templ.ClearChildren(ctx) work, pause := workDay.GetWorkTimeString() + user := ctx.Value("user").(models.User) + overtime := helper.FormatDuration(workDay.CalcOvertime(user)) justify := "" if len(workDay.Bookings) <= 1 { justify = "justify-content: center" @@ -137,7 +140,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("Mon")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 51, Col: 94} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 94} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -150,7 +153,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("02.01.2006")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 51, Col: 139} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 54, Col: 139} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -178,7 +181,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(work) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 57, Col: 36} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 60, Col: 36} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -196,18 +199,31 @@ func dayComponent(workDay models.WorkDay) templ.Component { var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(pause) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 59, Col: 40} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 62, Col: 40} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var10 string + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(overtime) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 63, Col: 43} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -215,57 +231,57 @@ func dayComponent(workDay models.WorkDay) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" class=\"flex flex-col gap-2 group w-full justify-between\" style=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(justify) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 69, Col: 131} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" method=\"post\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if (workDay.Absence != models.Absence{}) { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.GetStringType()) + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.GetStringType()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 67, Col: 41} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 41} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if len(workDay.Bookings) < 1 && (workDay.Absence == models.Absence{}) { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -273,7 +289,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -292,7 +308,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -301,7 +317,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -309,7 +325,7 @@ func dayComponent(workDay models.WorkDay) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -333,25 +349,25 @@ func changeButtonComponent(id string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templ.GetChildren(ctx) - if templ_7745c5c3_Var13 == nil { - templ_7745c5c3_Var13 = templ.NopComponent + templ_7745c5c3_Var14 := templ.GetChildren(ctx) + if templ_7745c5c3_Var14 == nil { + templ_7745c5c3_Var14 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.JSFuncCall("editDay", templ.JSExpression("this"), templ.JSExpression("event"), id)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\">

Ändern

Absenden

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -375,9 +391,9 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var15 := templ.GetChildren(ctx) - if templ_7745c5c3_Var15 == nil { - templ_7745c5c3_Var15 = templ.NopComponent + templ_7745c5c3_Var16 := templ.GetChildren(ctx) + if templ_7745c5c3_Var16 == nil { + templ_7745c5c3_Var16 = templ.NopComponent } ctx = templ.ClearChildren(ctx) @@ -400,65 +416,65 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen break } if today { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var16 = []any{"flex w-full items-center justify-center overflow-hidden rounded-full", bgColor} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...) + var templ_7745c5c3_Var17 = []any{"flex w-full items-center justify-center overflow-hidden rounded-full", bgColor} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" style=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var19 string + templ_7745c5c3_Var19, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("height: %d%%", int(progress))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 127, Col: 149} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - var templ_7745c5c3_Var19 = []any{"w-2 h-full bg-accent rounded-md", bgColor} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var19...) + var templ_7745c5c3_Var20 = []any{"w-2 h-full bg-accent rounded-md", bgColor} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -483,12 +499,12 @@ func lineComponent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var21 := templ.GetChildren(ctx) - if templ_7745c5c3_Var21 == nil { - templ_7745c5c3_Var21 = templ.NopComponent + templ_7745c5c3_Var22 := templ.GetChildren(ctx) + if templ_7745c5c3_Var22 == nil { + templ_7745c5c3_Var22 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -512,12 +528,12 @@ func absenceComponent(d models.WorkDay) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var22 := templ.GetChildren(ctx) - if templ_7745c5c3_Var22 == nil { - templ_7745c5c3_Var22 = templ.NopComponent + templ_7745c5c3_Var23 := templ.GetChildren(ctx) + if templ_7745c5c3_Var23 == nil { + templ_7745c5c3_Var23 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -594,58 +610,58 @@ func newBookingComponent(d models.WorkDay) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var26 := templ.GetChildren(ctx) - if templ_7745c5c3_Var26 == nil { - templ_7745c5c3_Var26 = templ.NopComponent + templ_7745c5c3_Var27 := templ.GetChildren(ctx) + if templ_7745c5c3_Var27 == nil { + templ_7745c5c3_Var27 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, ">Gehen") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -669,64 +685,64 @@ func bookingComponent(booking models.Booking) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var29 := templ.GetChildren(ctx) - if templ_7745c5c3_Var29 == nil { - templ_7745c5c3_Var29 = templ.NopComponent + templ_7745c5c3_Var30 := templ.GetChildren(ctx) + if templ_7745c5c3_Var30 == nil { + templ_7745c5c3_Var30 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var30 string - templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 174, Col: 97} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var31 string - templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId)) + templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 175, Col: 70} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 178, Col: 97} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" type=\"time\" value=\"") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" type=\"time\" value=\"") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var33 string - templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType()) + templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 176, Col: 29} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 179, Col: 126} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\" class=\"text-neutral-700 group-[.edit]:inline hidden bg-neutral-100 text-sm border border-neutral-200 rounded-md px-3 py-2 transition duration-300 ease focus:outline-none focus:border-neutral-400 hover:border-neutral-300\"> ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var34 string + templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 180, Col: 29} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -750,12 +766,12 @@ func LegendComponent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var34 := templ.GetChildren(ctx) - if templ_7745c5c3_Var34 == nil { - templ_7745c5c3_Var34 = templ.NopComponent + templ_7745c5c3_Var35 := templ.GetChildren(ctx) + if templ_7745c5c3_Var35 == nil { + templ_7745c5c3_Var35 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
Fehler
Arbeitszeit unter regulär
Arbeitszeit vollständig
Überstunden
Keine Buchungen
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "
Fehler
Arbeitszeit unter regulär
Arbeitszeit vollständig
Überstunden
Keine Buchungen
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } From 3e0f84f618251b89b39230567134c9b1b39aee4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Tr=C3=B6ger?= Date: Fri, 1 Aug 2025 18:21:39 +0200 Subject: [PATCH 03/52] trying atlas --- atlas.hcl | 10 ++++++++++ schema.sql | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 atlas.hcl create mode 100644 schema.sql diff --git a/atlas.hcl b/atlas.hcl new file mode 100644 index 0000000..ebc39a6 --- /dev/null +++ b/atlas.hcl @@ -0,0 +1,10 @@ +# The "dev" environment represents our local testings. +env "local" { + url = "postgres://root:very_secure@:5432/arbeitszeitmessung?search_path=public&sslmode=disable" + migration { + dir = "file://migrations" + format = golang-migrate + } + dev = "docker://postgres/16/dev?search_path=public" + src = "file://schema.sql" +} diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..55dcecd --- /dev/null +++ b/schema.sql @@ -0,0 +1,58 @@ +-- Create "abwesenheit" table +CREATE TABLE "abwesenheit" ( + "counter_id" bigserial NOT NULL, + "card_uid" character varying(255) NULL, + "abwesenheit_typ" smallint NULL, + "datum" timestamptz NULL DEFAULT (now())::date, + PRIMARY KEY ("counter_id") +); +-- Create "anwesenheit" table +CREATE TABLE "anwesenheit" ( + "counter_id" bigserial NOT NULL, + "timestamp" timestamptz NULL DEFAULT CURRENT_TIMESTAMP, + "card_uid" character varying(255) NULL, + "check_in_out" smallint NULL, + "anwesenheit_type" smallint NULL, + "geraet_id" smallint NULL, + PRIMARY KEY ("counter_id") +); +-- Set comment to column: "check_in_out" on table: "anwesenheit" +COMMENT ON COLUMN "anwesenheit"."check_in_out" IS '1=Check In 2=Check Out , 3=Check in Manuell, 4=Check out manuell255=Automatic Check Out'; +-- Set comment to column: "geraet_id" on table: "anwesenheit" +COMMENT ON COLUMN "anwesenheit"."geraet_id" IS 'ID des Lesegerätes'; +-- Create "personal_daten" table +CREATE TABLE "personal_daten" ( + "personal_nummer" integer NOT NULL, + "aktiv_beschaeftigt" boolean NULL, + "vorname" character varying(255) NULL, + "nachname" character varying(255) NULL, + "geburtsdatum" date NULL, + "plz" character varying(255) NULL, + "adresse" character varying(255) NULL, + "geschlecht" smallint NULL, + "card_uid" character varying(255) NULL, + "hauptbeschaeftigungs_ort" smallint NULL, + "arbeitszeit_per_tag" real NULL, + "arbeitszeit_min_start" time NULL, + "arbeitszeit_max_ende" time NULL, + "vorgesetzter_pers_nr" integer NULL, + PRIMARY KEY ("personal_nummer") +); +-- Set comment to column: "geschlecht" on table: "personal_daten" +COMMENT ON COLUMN "personal_daten"."geschlecht" IS '1==weiblich, 2==maennlich, 3==divers'; +-- Create "user_password" table +CREATE TABLE "user_password" ( + "personal_nummer" integer NOT NULL, + "pass_hash" text NULL, + "zuletzt_geandert" timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("personal_nummer") +); +-- Create "wochen_report" table +CREATE TABLE "wochen_report" ( + "id" serial NOT NULL, + "personal_nummer" integer NULL, + "woche_start" date NULL, + "bestaetigt" boolean NULL DEFAULT false, + PRIMARY KEY ("id"), + CONSTRAINT "wochen_report_personal_nummer_woche_start_key" UNIQUE ("personal_nummer", "woche_start") +); From 7670efa99b48782a54b1c7c65fc6ee0326af26f0 Mon Sep 17 00:00:00 2001 From: tom Date: Sat, 2 Aug 2025 08:55:40 +0200 Subject: [PATCH 04/52] added control tables (s_*) + working on implementing absence and booking types --- Backend/endpoints/time.go | 7 +- Backend/models/absence.go | 102 ++++++++++++++++------ Backend/models/booking.go | 36 ++++++++ Backend/models/controlTables.go | 15 ++++ Backend/models/user.go | 8 +- Backend/models/workDay.go | 4 +- Backend/templates/timeComponents.templ | 6 +- Backend/templates/timeComponents_templ.go | 14 +-- DB/initdb/01_create_tables.sql | 35 ++++++-- DB/initdb/02_create_user.sh | 3 +- 10 files changed, 176 insertions(+), 54 deletions(-) create mode 100644 Backend/models/controlTables.go diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go index 477ad96..9dae1a3 100644 --- a/Backend/endpoints/time.go +++ b/Backend/endpoints/time.go @@ -163,7 +163,12 @@ func createAbsence(absenceType int, user models.User, loc *time.Location, r *htt log.Println("Cannot get date from input! Skipping absence creation", err) return } - absence := models.NewAbsence(user.CardUID, int8(absenceType), absenceDate) + + absence, err := models.NewAbsence(user.CardUID, absenceType, absenceDate) + if err != nil { + log.Println("Error creating absence!", err) + return + } err = absence.Insert() if err != nil { log.Println("Error inserting absence!", err) diff --git a/Backend/models/absence.go b/Backend/models/absence.go index 2f352c6..a1f1b9e 100644 --- a/Backend/models/absence.go +++ b/Backend/models/absence.go @@ -1,52 +1,65 @@ package models import ( + "errors" "log" "time" ) type AbsenceType struct { - Value int8 - Label string + Id int8 + Name string } -const ( - AbsenceNone int8 = iota - AbsenceUrlaub - AbsenceKurzarbeit - AbsenceKrank - AbsenceKindkrank -) +// const ( +// AbsenceNone int8 = iota +// AbsenceUrlaub +// AbsenceKurzarbeit +// AbsenceKrank +// AbsenceKindkrank +// ) -var AbsenceTypes = []AbsenceType{ - // {Value: AbsenceNone, Label: "Abwesenheit"}, - {Value: AbsenceUrlaub, Label: "Urlaub"}, - {Value: AbsenceKurzarbeit, Label: "Kurzarbeit"}, - {Value: AbsenceKrank, Label: "Krank"}, - {Value: AbsenceKindkrank, Label: "Kindkrank"}, -} +// var AbsenceTypes = []AbsenceType{ +// // {Value: AbsenceNone, Label: "Abwesenheit"}, +// {Id: AbsenceUrlaub, Name: "Urlaub"}, +// {Id: AbsenceKurzarbeit, Name: "Kurzarbeit"}, +// {Id: AbsenceKrank, Name: "Krank"}, +// {Id: AbsenceKindkrank, Name: "Kindkrank"}, +// } -var AbsenceTypesLabel = map[int8]string{ - 0: "None", - AbsenceUrlaub: "Urlaub", - AbsenceKurzarbeit: "Kurzarbeit", - AbsenceKrank: "Krank", - AbsenceKindkrank: "Kindkrank", -} +// var AbsenceTypesLabel = map[int8]string{ +// 0: "None", +// AbsenceUrlaub: "Urlaub", +// AbsenceKurzarbeit: "Kurzarbeit", +// AbsenceKrank: "Krank", +// AbsenceKindkrank: "Kindkrank", +// } type Absence struct { CounterId int CardUID string - AbwesenheitTyp int8 + AbwesenheitTyp AbsenceType Datum time.Time + // Comment string } -func NewAbsence(card_uid string, abwesenheit_typ int8, datum time.Time) Absence { +func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence, error) { + if abwesenheit_typ < 0 { + return Absence{ + CardUID: card_uid, + AbwesenheitTyp: AbsenceType{0, "Custom absence"}, + Datum: datum, + }, nil + } + _absenceType, ok := GetAbsenceTypesCached()[int8(abwesenheit_typ)] + if !ok { + return Absence{}, errors.New("Invalid absencetype") + } return Absence{ CardUID: card_uid, - AbwesenheitTyp: abwesenheit_typ, + AbwesenheitTyp: _absenceType, Datum: datum, - } + }, nil } func (a *Absence) Insert() error { @@ -63,6 +76,37 @@ func (a *Absence) Insert() error { return nil } -func (a *Absence) GetStringType() string { - return AbsenceTypesLabel[a.AbwesenheitTyp] +// func (a *Absence) GetStringType() string { +// return AbsenceTypesLabel[a.AbwesenheitTyp] +// } + +func GetAbsenceTypes() (map[int8]AbsenceType, error) { + var types = make(map[int8]AbsenceType) + qStr, err := DB.Prepare("SELECT abwesenheit_id, abwesenheit_name FROM s_abwesenheit_typen;") + if err != nil { + return types, err + } + defer qStr.Close() + rows, err := qStr.Query() + if err != nil { + log.Println("Error getting abwesenheit rows!", err) + return types, err + } + defer rows.Close() + for rows.Next() { + var absenceType AbsenceType + if err := rows.Scan(&absenceType.Id, &absenceType.Name); err != nil { + log.Println("Error scanning absence row!", err) + } + types[absenceType.Id] = absenceType + } + return types, nil +} + +func GetAbsenceTypesCached() map[int8]AbsenceType { + types, err := definedTypes.Get("s_abwesenheit_typen") + if err != nil { + return map[int8]AbsenceType{} + } + return types.(map[int8]AbsenceType) } diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 24533c2..9265808 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -13,6 +13,11 @@ import ( type SameBookingError struct{} +type BookingType struct { + Id int8 + Name string +} + func (e SameBookingError) Error() string { return "the same booking already exists!" } @@ -261,3 +266,34 @@ func (b *Booking) UpdateTime(newTime time.Time) { func (b *Booking) ToString() string { return fmt.Sprintf("Booking %d: at: %s, as type: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut) } + +func GetBokkingTypes() ([]BookingType, error) { + var types []BookingType + qStr, err := DB.Prepare("SELECT anwesenheit_id, anwesenheit_name FROM s_anwesenheit_typen;") + if err != nil { + return types, err + } + defer qStr.Close() + rows, err := qStr.Query() + if err != nil { + log.Println("Error getting anwesenheit rows!", err) + return types, err + } + defer rows.Close() + for rows.Next() { + var bookingType BookingType + if err := rows.Scan(&bookingType.Id, &bookingType.Name); err != nil { + log.Println("Error scanning row!", err) + } + types = append(types, bookingType) + } + return types, nil +} + +func GetBookingTypesCached() []BookingType { + types, err := definedTypes.Get("s_anwesenheit_typen") + if err != nil { + return []BookingType{} + } + return types.([]BookingType) +} diff --git a/Backend/models/controlTables.go b/Backend/models/controlTables.go new file mode 100644 index 0000000..e943837 --- /dev/null +++ b/Backend/models/controlTables.go @@ -0,0 +1,15 @@ +package models + +import ( + "arbeitszeitmessung/helper" +) + +var definedTypes = helper.NewCache(3600, func(key string) (any, error) { + switch key { + case "s_abwesenheit_typen": + return GetAbsenceTypes() + case "s_anwesenheit_typen": + return GetBokkingTypes() + } + return nil, nil +}) diff --git a/Backend/models/user.go b/Backend/models/user.go index 7c45242..2f181a2 100644 --- a/Backend/models/user.go +++ b/Backend/models/user.go @@ -40,7 +40,7 @@ func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Conte } func (u *User) GetAll() ([]User, error) { - qStr, err := DB.Prepare((`SELECT card_uid, vorname, nachname FROM personal_daten;`)) + qStr, err := DB.Prepare((`SELECT card_uid, vorname, nachname FROM s_personal_daten;`)) var users []User if err != nil { fmt.Printf("Error preparing query statement %v\n", err) @@ -98,7 +98,7 @@ func (u *User) CheckOut() error { func (u *User) GetByPersonalNummer(personalNummer int) (User, error) { var user User - qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM personal_daten WHERE personal_nummer = $1;`)) + qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE personal_nummer = $1;`)) if err != nil { return user, err } @@ -146,7 +146,7 @@ func (u *User) ChangePass(password, newPassword string) (bool, error) { func (u *User) GetTeamMembers() ([]User, error) { var teamMembers []User - qStr, err := DB.Prepare(`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM personal_daten WHERE vorgesetzter_pers_nr = $1`) + qStr, err := DB.Prepare(`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE vorgesetzter_pers_nr = $1`) if err != nil { return teamMembers, err } @@ -234,7 +234,7 @@ func (u *User) GetFromCardUID(card_uid string) (User, error) { user := User{} var err error - qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM personal_daten WHERE card_uid = $1;`)) + qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE card_uid = $1;`)) if err != nil { return user, err } diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index 6585742..13a0166 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -117,8 +117,8 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay } if absenceType.Valid { - workDay.Absence = NewAbsence(card_uid, int8(absenceType.Int16), workDay.Day) - log.Println("Found absence", workDay.Absence) + workDay.Absence, err = NewAbsence(card_uid, int(absenceType.Int16), workDay.Day) + // log.Println("Found absence", workDay.Absence) } if workDay.Day.Equal(time.Now().Truncate(24 * time.Hour)) { diff --git a/Backend/templates/timeComponents.templ b/Backend/templates/timeComponents.templ index 576ff9e..bdd7c10 100644 --- a/Backend/templates/timeComponents.templ +++ b/Backend/templates/timeComponents.templ @@ -68,7 +68,7 @@ templ dayComponent(workDay models.WorkDay) { @lineComponent()
if (workDay.Absence != models.Absence{}) { -

{ workDay.Absence.GetStringType() }

+

{ workDay.Absence.AbwesenheitTyp.Name }

} if len(workDay.Bookings) < 1 && (workDay.Absence == models.Absence{}) {

Keine Buchung gefunden. Bitte Arbeitsstunden oder Grund der Abwesenheit eingeben!

@@ -147,8 +147,8 @@ templ absenceComponent(d models.WorkDay) { diff --git a/Backend/templates/timeComponents_templ.go b/Backend/templates/timeComponents_templ.go index 75776c4..c560ce9 100644 --- a/Backend/templates/timeComponents_templ.go +++ b/Backend/templates/timeComponents_templ.go @@ -267,9 +267,9 @@ func dayComponent(workDay models.WorkDay) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.GetStringType()) + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Absence.AbwesenheitTyp.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 41} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 71, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { @@ -554,15 +554,15 @@ func absenceComponent(d models.WorkDay) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - for _, absence := range models.AbsenceTypes { + for _, absence := range models.GetAbsenceTypesCached() { templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "