package models import ( "arbeitszeitmessung/helper" "database/sql" "fmt" "log" "net/url" "sort" "strconv" "time" ) type SameBookingError struct{} func (e SameBookingError) Error() string { return "the same booking already exists!" } type Booking struct { CardUID string `json:"card_uid"` GeraetID int16 `json:"geraet_id"` CheckInOut int16 `json:"check_in_out"` Timestamp time.Time `json:"timestamp"` CounterId int `json:"counter_id"` } var DB *sql.DB func (b *Booking) New(card_uid string, geraet_id int16, check_in_out int16) Booking { return Booking{ CardUID: card_uid, GeraetID: geraet_id, CheckInOut: check_in_out, } } func (b *Booking) FromUrlParams(params url.Values) Booking { var booking Booking if _check_in_out, err := strconv.Atoi(params.Get("check_in_out")); err == nil { booking.CheckInOut = int16(_check_in_out) } if _geraet_id, err := strconv.Atoi(params.Get("geraet_id")); err == nil { booking.GeraetID = int16(_geraet_id) } booking.CardUID = params.Get("card_uid") return booking } func (b *Booking) Verify() bool { //check for overlapping time + arbeitszeit verstoß if b.CardUID == "" { //|| b.GeraetID == 0 || b.CheckInOut == 0 { return false } return true } func (b *Booking) Insert() error { if !checkLastBooking(*b) { return SameBookingError{} } stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out) VALUES ($1, $2, $3) RETURNING counter_id, timestamp`)) if err != nil { return err } err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut).Scan(&b.CounterId, &b.Timestamp) if err != nil { return err } return nil } func (b *Booking) InsertWithTimestamp() error { if b.Timestamp.IsZero() { return b.Insert() } stmt, err := DB.Prepare((`INSERT INTO anwesenheit (card_uid, geraet_id, check_in_out, timestamp) VALUES ($1, $2, $3, $4) RETURNING counter_id`)) if err != nil { return err } err = stmt.QueryRow(b.CardUID, b.GeraetID, b.CheckInOut, b.Timestamp).Scan(&b.CounterId) if err != nil { return err } return nil } func (b *Booking) GetBookingById(booking_id int) (Booking, error) { var booking Booking qStr, err := DB.Prepare((`SELECT counter_id, timestamp, card_uid, geraet_id, check_in_out FROM anwesenheit WHERE counter_id = $1`)) if err != nil { return booking, err } err = qStr.QueryRow(booking_id).Scan(&booking.CounterId, &booking.Timestamp, &booking.CardUID, &booking.GeraetID, &booking.CheckInOut) if err != nil { return booking, err } // if !booking.Verify() { // fmt.Printf("Booking verification failed! %d", ) // return booking, nil // } return booking, nil } // Gets all booking based on a card uid // // optional filter parameter func (b *Booking) GetBookingsByCardID(card_uid string, tsFrom time.Time, tsTo time.Time) ([]Booking, error) { qStr, err := DB.Prepare((`SELECT counter_id, timestamp, card_uid, geraet_id, check_in_out FROM anwesenheit WHERE card_uid = $1 AND timestamp BETWEEN $2 AND $3 ORDER BY timestamp`)) if err != nil { return nil, err } defer qStr.Close() var bookings []Booking rows, err := qStr.Query(card_uid, tsFrom, tsTo) if err == sql.ErrNoRows { return bookings, err } if err != nil { return nil, err } defer rows.Close() for rows.Next() { var booking Booking if err := rows.Scan(&booking.CounterId, &booking.Timestamp, &booking.CardUID, &booking.GeraetID, &booking.CheckInOut); err != nil { return bookings, err } bookings = append(bookings, booking) } if err = rows.Err(); err != nil { return bookings, err } return bookings, nil } func (b *Booking) GetBookingsGrouped(card_uid string, tsFrom time.Time, tsTo time.Time) ([]WorkDay, error) { var grouped = make(map[string][]Booking) bookings, err := b.GetBookingsByCardID(card_uid, tsFrom, tsTo) if err != nil { log.Println("Failed to get bookings", err) return []WorkDay{}, nil } for _, booking := range bookings { day := booking.Timestamp.Truncate(24 * time.Hour) key := day.Format("2006-01-02") grouped[key] = append(grouped[key], booking) } var result []WorkDay for key, bookings := range grouped { day, _ := time.Parse("2006-01-02", key) sort.Slice(bookings, func(i, j int) bool { return bookings[i].Timestamp.Before(bookings[j].Timestamp) }) workDay := WorkDay{Day: day, Bookings: bookings} workDay.getWorkTime() result = append(result, workDay) } sort.Slice(result, func(i, j int) bool { return result[i].Day.After(result[j].Day) }) return result, nil } func (b Booking) Save() { qStr, err := DB.Prepare((`UPDATE "anwesenheit" SET "card_uid" = $2, "geraet_id" = $3, "check_in_out" = $4, "timestamp" = $5 WHERE "counter_id" = $1;`)) if err != nil { log.Fatalf("Error preparing query: %v", err) return } _, err = qStr.Query(b.CounterId, b.CardUID, b.GeraetID, b.CheckInOut, b.Timestamp) if err != nil { log.Fatalf("Error executing query: %v", err) return } } func (b *Booking) GetBookingType() string { debug := (helper.GetEnv("GO_ENV", "production") == "debug") switch b.CheckInOut { case 1: //manuelle Änderung return "kommen" case 3: if debug { return "kommen manuell" } return "kommen" case 2: //manuelle Änderung return "gehen" case 4: if debug { return "gehen manuell" } return "gehen" case 254: return "abgemeldet" default: return "Buchungs Typ unbekannt" } } func (b *Booking) Update(nb Booking) { if b.CheckInOut != nb.CheckInOut && nb.CheckInOut != 0 { b.CheckInOut = nb.CheckInOut } if b.CardUID != nb.CardUID && nb.CardUID != "" { b.CardUID = nb.CardUID } if b.GeraetID != nb.GeraetID && nb.GeraetID != 0 { b.GeraetID = nb.GeraetID } if b.Timestamp != nb.Timestamp { b.Timestamp = nb.Timestamp } } func checkLastBooking(b Booking) bool { var check_in_out int stmt, err := DB.Prepare((`SELECT check_in_out FROM "anwesenheit" WHERE "card_uid" = $1 ORDER BY "timestamp" DESC LIMIT 1;`)) if err != nil { log.Fatalf("Error preparing query: %v", err) return false } err = stmt.QueryRow(b.CardUID).Scan(&check_in_out) if err == sql.ErrNoRows { return true } if err != nil { log.Println("Error checking last booking: ", err) return false } if int16(check_in_out)%2 == b.CheckInOut%2 { return false } return true } func (b *Booking) UpdateTime(newTime time.Time) { hour, minute, _ := newTime.Clock() if hour == b.Timestamp.Hour() && minute == b.Timestamp.Minute() { return } // TODO: add check for time overlap var newBooking Booking newBooking.Timestamp = time.Date(b.Timestamp.Year(), b.Timestamp.Month(), b.Timestamp.Day(), hour, minute, 0, 0, b.Timestamp.Location()) if b.CheckInOut < 3 { newBooking.CheckInOut = b.CheckInOut + 2 } if b.CheckInOut == 254 { newBooking.CheckInOut = 4 } log.Println("Updating") b.Update(newBooking) b.Verify() b.Save() } func (b *Booking) ToString() string { return fmt.Sprintf("Booking %d: at: %s, as type: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut) }