package storage import ( "database/sql" "encoding/json" "errors" "fmt" "github.com/google/uuid" _ "github.com/lib/pq" "github.com/paulmach/orb" "github.com/paulmach/orb/geojson" "github.com/rs/zerolog/log" "github.com/spf13/viper" "solidarity-service/internal" "solidarity-service/utils" "strconv" "strings" "sync" "time" ) type PostgresqlStorage struct { DbConnection *sql.DB Schema string Tables map[string]string } func NewPostgresqlStorage(cfg *viper.Viper) (PostgresqlStorage, error) { var ( host = cfg.GetString("storage.db.psql.host") port = cfg.GetString("storage.db.psql.port") user = cfg.GetString("storage.db.psql.user") password = cfg.GetString("storage.db.psql.password") dbname = cfg.GetString("storage.db.psql.dbname") sslmode = cfg.GetString("storage.db.psql.sslmode") pg_schema = cfg.GetString("storage.db.psql.schema") pgtables_drivers = cfg.GetString("storage.db.psql.tables.drivers") pgtables_passengers = cfg.GetString("storage.db.psql.tables.passengers") pgtables_bookings = cfg.GetString("storage.db.psql.tables.bookings") ) portInt, _ := strconv.Atoi(port) psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", host, portInt, user, password, dbname, sslmode) db, err := sql.Open("postgres", psqlconn) if err != nil { log.Error().Err(err).Msg("opening connection to postgresql failed") return PostgresqlStorage{}, fmt.Errorf("connection to postgresql failed") } err = db.Ping() if err != nil { log.Error().Err(err).Msg("ping to postgresql failed") return PostgresqlStorage{}, fmt.Errorf("connection to postgresql database failed") } return PostgresqlStorage{ DbConnection: db, Schema: pg_schema, Tables: map[string]string{ "drivers": fmt.Sprintf("%s.%s", pg_schema, pgtables_drivers), "bookings": fmt.Sprintf("%s.%s", pg_schema, pgtables_bookings), "passengers": fmt.Sprintf("%s.%s", pg_schema, pgtables_passengers), }, }, nil } func (s PostgresqlStorage) CreatePassenger(passenger internal.Passenger) (err error) { _, err = uuid.Parse(passenger.Passenger.ID) if err != nil { log.Error().Err(err).Msg("Postgresql Storage CreatePassenger invalid ID") return err } if passenger.Passenger.Alias == "" || passenger.Passenger.Operator == "" { errMsg := "Postgresql Storage CreatePassenger empty alias or operator FQDN." log.Error().Msg(errMsg) return errors.New(errMsg) } preferencesJSON, err := json.Marshal(passenger.Preferences) if err != nil { errMsg := "Postgresql Storage CreatePassenger Error encoding Preferences to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } _, err = s.DbConnection.Exec(fmt.Sprintf("INSERT INTO %s (passenger_id, alias, last_name, first_name, grade, picture, verified_identity, operator, preferences) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", s.Tables["passengers"]), passenger.Passenger.ID, passenger.Passenger.Alias, passenger.Passenger.LastName, passenger.Passenger.FirstName, passenger.Passenger.Grade, passenger.Passenger.Picture, passenger.Passenger.VerifiedIdentity, passenger.Passenger.Operator, preferencesJSON) if err != nil { if strings.Contains(err.Error(), utils.SQL_DUPLICATE) { err = s.UpdatePassenger(passenger) if err != nil { return err } return nil } errMsg := "Postgresql Storage CreatePassenger Error inserting data into the database" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) UpdatePassenger(passenger internal.Passenger) (err error) { _, err = uuid.Parse(passenger.Passenger.ID) if err != nil { log.Error().Err(err).Msg("Postgresql Storage UpdatePassenger invalid ID") return err } if passenger.Passenger.Alias == "" || passenger.Passenger.Operator == "" { errMsg := "Postgresql Storage UpdatePassenger empty alias or operator FQDN." log.Error().Msg(errMsg) return errors.New(errMsg) } preferencesJSON, err := json.Marshal(passenger.Preferences) if err != nil { errMsg := "Postgresql Storage UpdatePassenger Error encoding Preferences to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } _, err = s.DbConnection.Exec(fmt.Sprintf(` UPDATE %s SET alias = $2, last_name = $3, first_name = $4, grade = $5, picture = $6, verified_identity = $7, operator = $8, preferences = $9 WHERE passenger_id = $1 `, s.Tables["passengers"]), passenger.Passenger.ID, passenger.Passenger.Alias, passenger.Passenger.LastName, passenger.Passenger.FirstName, passenger.Passenger.Grade, passenger.Passenger.Picture, passenger.Passenger.VerifiedIdentity, passenger.Passenger.Operator, preferencesJSON) if err != nil { errMsg := "Postgresql Storage UpdatePassenger Error updating data in the database" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) GetPassenger(passengerID string) (passenger internal.Passenger, err error) { var preferencesJSON []byte err = s.DbConnection.QueryRow(fmt.Sprintf("SELECT alias, last_name, first_name, grade, picture, verified_identity, operator, preferences, passenger_id FROM %s WHERE passenger_id = $1", s.Tables["passengers"]), passengerID). Scan( &passenger.Passenger.Alias, &passenger.Passenger.LastName, &passenger.Passenger.FirstName, &passenger.Passenger.Grade, &passenger.Passenger.Picture, &passenger.Passenger.VerifiedIdentity, &passenger.Passenger.Operator, &preferencesJSON, &passenger.Passenger.ID, ) if err != nil { errMsg := "Postgresql Storage GetPassenger Error querying data from the database" log.Error().Err(err).Msg(errMsg) return passenger, errors.New(errMsg + err.Error()) } err = json.Unmarshal(preferencesJSON, &passenger.Preferences) if err != nil { errMsg := "Postgresql Storage GetPassenger Error decoding Preferences from JSON" log.Error().Err(err).Msg(errMsg) return passenger, errors.New(errMsg + err.Error()) } return passenger, nil } func (s PostgresqlStorage) CreateDriver(driver internal.Driver) (err error) { var availabilities []byte _, err = uuid.Parse(driver.Driver.ID) if err != nil { log.Error().Err(err).Msg("Postgresql Storage CreateDriver invalid ID") return err } departureJSON, err := json.Marshal(driver.Driver_departure_address) if err != nil { errMsg := "Postgresql Storage CreateDriver Error encoding departure Feature to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } preferencesJSON, err := json.Marshal(driver.Preferences) if err != nil { errMsg := "Postgresql Storage CreateDriver Error encoding Preferences to JSON" log.Error().Err(err).Msg(errMsg + err.Error()) return errors.New(errMsg) } carJSON, err := json.Marshal(driver.Car) if err != nil { errMsg := "Postgresql Storage CreateDriver Error encoding Car to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } if driver.Driver.Alias == "" || driver.Driver.Operator == "" { errMsg := "Postgresql Storage CreateDriver empty alias or operator FQDN." log.Error().Msg(errMsg) return errors.New(errMsg) } if driver.AvailabilitiesType != internal.Punctual && driver.AvailabilitiesType != internal.Regular { errMsg := "Postgresql Storage CreateDriver invalid Availabilities Type" log.Error().Msg(errMsg) return errors.New(errMsg) } if driver.Radius == 0 { errMsg := "Postgresql Storage CreateDriver Radius has to be defined" log.Error().Msg(errMsg) return errors.New(errMsg) } switch driver.AvailabilitiesType { case internal.Punctual: availabilities, err = json.Marshal(driver.PunctualAvailabilities) if err != nil { errMsg := "Postgresql Storage CreateDriver error converting Punctual availabilities" log.Error().Msg(errMsg) return errors.New(errMsg + err.Error()) } case internal.Regular: availabilities, err = json.Marshal(driver.RegularAvailabilities) if err != nil { errMsg := "Postgresql Storage CreateDriver error converting Regular availabilities" log.Error().Msg(errMsg) return errors.New(errMsg + err.Error()) } } _, err = s.DbConnection.Exec(fmt.Sprintf("INSERT INTO %s (driver_id,driver_departure_route,driver_radius,last_name,first_name,grade,alias,picture,verified_identity,preferences,availabilities_type,availabilities,operator , car) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)", s.Tables["drivers"]), driver.Driver.ID, departureJSON, driver.Radius, driver.Driver.LastName, driver.Driver.FirstName, driver.Driver.Grade, driver.Driver.Alias, driver.Driver.Picture, driver.Driver.VerifiedIdentity, preferencesJSON, driver.AvailabilitiesType, string(availabilities), driver.Driver.Operator, carJSON, ) if err != nil { if strings.Contains(err.Error(), utils.SQL_DUPLICATE) { err = s.UpdateDriver(driver) if err != nil { return err } return nil } errMsg := "Postgresql Storage CreateDriver Error inserting data into the database" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) UpdateDriver(driver internal.Driver) (err error) { var availabilities []byte _, err = uuid.Parse(driver.Driver.ID) if err != nil { log.Error().Err(err).Msg("Postgresql Storage UpdateDriver invalid ID") return err } departureJSON, err := json.Marshal(driver.Driver_departure_address) if err != nil { errMsg := "Postgresql Storage UpdateDriver Error encoding departure Feature to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } preferencesJSON, err := json.Marshal(driver.Preferences) if err != nil { errMsg := "Postgresql Storage UpdateDriver Error encoding Preferences to JSON" log.Error().Err(err).Msg(errMsg + err.Error()) return errors.New(errMsg) } carJSON, err := json.Marshal(driver.Car) if err != nil { errMsg := "Postgresql Storage UpdateDriver Error encoding Car to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } if driver.Driver.Alias == "" || driver.Driver.Operator == "" { errMsg := "Postgresql Storage UpdateDriver empty alias or operator FQDN." log.Error().Msg(errMsg) return errors.New(errMsg) } if driver.AvailabilitiesType != internal.Punctual && driver.AvailabilitiesType != internal.Regular { errMsg := "Postgresql Storage UpdateDriver invalid Availabilities Type" log.Error().Msg(errMsg) return errors.New(errMsg) } if driver.Radius == 0 { errMsg := "Postgresql Storage UpdateDriver Radius has to be defined" log.Error().Msg(errMsg) return errors.New(errMsg) } switch driver.AvailabilitiesType { case internal.Punctual: availabilities, err = json.Marshal(driver.PunctualAvailabilities) if err != nil { errMsg := "Postgresql Storage UpdateDriver error converting Punctual availabilities" log.Error().Msg(errMsg) return errors.New(errMsg + err.Error()) } case internal.Regular: availabilities, err = json.Marshal(driver.RegularAvailabilities) if err != nil { errMsg := "Postgresql Storage UpdateDriver error converting Regular availabilities" log.Error().Msg(errMsg) return errors.New(errMsg + err.Error()) } } _, err = s.DbConnection.Exec(fmt.Sprintf(` UPDATE %s SET driver_departure_route = $2, driver_radius = $3, last_name = $4, first_name = $5, grade = $6, alias = $7, picture = $8, verified_identity = $9, preferences = $10, availabilities_type = $11, availabilities = $12, operator = $13, car = $14 WHERE driver_id = $1 `, s.Tables["drivers"]), driver.Driver.ID, departureJSON, driver.Radius, driver.Driver.LastName, driver.Driver.FirstName, driver.Driver.Grade, driver.Driver.Alias, driver.Driver.Picture, driver.Driver.VerifiedIdentity, preferencesJSON, driver.AvailabilitiesType, string(availabilities), driver.Driver.Operator, carJSON, ) if err != nil { errMsg := "Postgresql Storage UpdateDriver Error updating data in the database" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) GetDriver(driverID string) (driver internal.Driver, err error) { var preferencesJSON []byte var departureAddress []byte var availabilitiesJSON []byte var carJSON []byte err = s.DbConnection.QueryRow(fmt.Sprintf("SELECT driver_departure_route, driver_radius, last_name, first_name, grade, alias, picture, verified_identity, preferences, availabilities_type, availabilities, operator , driver_id , car FROM %s WHERE driver_id = $1", s.Tables["drivers"]), driverID). Scan( &departureAddress, &driver.Radius, &driver.Driver.LastName, &driver.Driver.FirstName, &driver.Driver.Grade, &driver.Driver.Alias, &driver.Driver.Picture, &driver.Driver.VerifiedIdentity, &preferencesJSON, &driver.AvailabilitiesType, &availabilitiesJSON, &driver.Driver.Operator, &driver.Driver.ID, &carJSON, ) if err != nil { errMsg := "Postgresql Storage GetDriver Error querying data from the database" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } err = json.Unmarshal(preferencesJSON, &driver.Preferences) if err != nil { errMsg := "Postgresql Storage GetDriver Error decoding Preferences from JSON" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } err = json.Unmarshal(carJSON, &driver.Car) if err != nil { errMsg := "Postgresql Storage GetDriver Error decoding Car from JSON" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } driver.Driver_departure_address, err = geojson.UnmarshalFeature(departureAddress) if err != nil { errMsg := "Postgresql Storage GetDriver Error decoding Driver departure route into GeoJSON" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } switch driver.AvailabilitiesType { case internal.Regular: err = json.Unmarshal(availabilitiesJSON, &driver.RegularAvailabilities) if err != nil { errMsg := "Postgresql Storage GetDriver Error decoding Regular Availabilities from JSON" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } case internal.Punctual: err = json.Unmarshal(availabilitiesJSON, &driver.PunctualAvailabilities) if err != nil { errMsg := "Postgresql Storage GetDriver Error decoding Punctual Availabilities from JSON" log.Error().Err(err).Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } default: errMsg := "Postgresql Storage GetDriver Invalid Availabilities Type" log.Error().Msg(errMsg) return driver, errors.New(errMsg + err.Error()) } return driver, nil } func (s PostgresqlStorage) CreateBooking(booking internal.BookingRequest) (err error) { _, err = uuid.Parse(booking.Driver_id) if err != nil { log.Error().Err(err).Msg("Postgresql Storage CreateBooking invalid Driver ID") return err } _, err = uuid.Parse(booking.Passenger_id) if err != nil { log.Error().Err(err).Msg("Postgresql Storage CreateBooking invalid Passenger ID") return err } _, err = uuid.Parse(booking.ID) if err != nil { log.Error().Err(err).Msg("Postgresql Storage CreateBooking invalid Booking ID") return err } if booking.Operator == "" { log.Error().Err(err).Msg("Postgresql Storage CreateBooking empty operator") return err } departureJSON, err := json.Marshal(booking.Departure_address) if err != nil { errMsg := "Postgresql Storage CreateBooking Error encoding departure Feature to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } destinationJSON, err := json.Marshal(booking.Destination_address) if err != nil { errMsg := "Postgresql Storage CreateBooking Error converting destination Feature to JSON" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } if booking.Pickup_date == 0 { errMsg := "Postgresql Storage CreateBooking empty UNIX pickup Date timestamp" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg) } booking.Distance = utils.Haversine(booking.Departure_address.Point().Lat(), booking.Departure_address.Point().Lon(), booking.Destination_address.Point().Lat(), booking.Destination_address.Point().Lon()) booking.Duration, _ = utils.CalculateDurationBetweenFeatures(booking.Departure_address, booking.Destination_address) _, err = s.DbConnection.Exec(fmt.Sprintf("INSERT INTO %s (booking_id , passenger_id , driver_id , operator, booking_status,departure_address,destination_address,pickup_date,duration,distance) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)", s.Tables["bookings"]), booking.ID, booking.Passenger_id, booking.Driver_id, booking.Operator, booking.Status, departureJSON, destinationJSON, booking.Pickup_date, booking.Duration, booking.Distance, ) if err != nil { errMsg := "Postgresql Storage CreateBooking Error inserting data into the database" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) GetBooking(id string) (booking internal.Booking, err error) { var departureAddress []byte var destinationAddress []byte err = s.DbConnection.QueryRow(fmt.Sprintf("SELECT booking_id , passenger_id , driver_id , booking_status , departure_address,destination_address,pickup_date,duration,distance FROM %s WHERE booking_id = $1", s.Tables["bookings"]), id). Scan( &booking.ID, &booking.Passenger.ID, &booking.Driver.ID, &booking.Status, &departureAddress, &destinationAddress, &booking.Pickup_date, &booking.Duration, &booking.Distance, ) if err != nil { errMsg := "Postgresql Storage GetBooking Error getting booking" log.Error().Err(err).Msg(errMsg) return internal.Booking{}, errors.New(errMsg + err.Error()) } booking.PassengerPickupAddress, err = geojson.UnmarshalFeature(departureAddress) if err != nil { errMsg := "Postgresql Storage GetBooking Error decoding Driver departure route into GeoJSON" log.Error().Err(err).Msg(errMsg) return internal.Booking{}, errors.New(errMsg + err.Error()) } booking.PassengerDropAddress, err = geojson.UnmarshalFeature(destinationAddress) if err != nil { errMsg := "Postgresql Storage GetBooking Error decoding Driver destination route into GeoJSON" log.Error().Err(err).Msg(errMsg) return internal.Booking{}, errors.New(errMsg + err.Error()) } passenger, err := s.GetPassenger(booking.Passenger.ID) if err != nil { errMsg := "Postgresql Storage GetBooking Error getting passenger" log.Error().Err(err).Msg(errMsg) return booking, errors.New(errMsg + err.Error()) } booking.Passenger = passenger.Passenger driver, err := s.GetDriver(booking.Driver.ID) if err != nil { errMsg := "Postgresql Storage GetBooking Error getting driver" log.Error().Err(err).Msg(errMsg) return booking, errors.New(errMsg + err.Error()) } booking.Driver = driver.Driver return booking, nil } func (s PostgresqlStorage) UpdateBookingStatus(id string, status internal.BookingStatus) (err error) { _, err = uuid.Parse(id) if err != nil { log.Error().Err(err).Msg("Postgresql Storage UpdateBookingStatus invalid Booking ID") return err } _, err = s.GetBooking(id) if err != nil { return errors.New(err.Error()) } query := fmt.Sprintf("UPDATE %s SET booking_status = $1 WHERE booking_id = $2", s.Tables["bookings"]) _, err = s.DbConnection.Exec(query, status, id) if err != nil { errMsg := "Postgresql Storage UpdateBookingStatus Error updating booking status" log.Error().Err(err).Msg(errMsg) return errors.New(errMsg + err.Error()) } return nil } func (s PostgresqlStorage) FilterUserBookingsByStatus(userType string, status internal.BookingStatus, userID string) (bookings []internal.Booking, err error) { if err != nil { return nil, errors.New("invalid UUID") } if userType != "driver" && userType != "passenger" { return nil, errors.New("invalid user type") } queryTemplate := fmt.Sprintf("SELECT booking_id, passenger_id, driver_id, booking_status, departure_address, destination_address, pickup_date,duration,distance FROM %s WHERE %s_id = $1 AND booking_status = $2", s.Tables["bookings"], userType) rows, err := s.DbConnection.Query(queryTemplate, userID, status) if err != nil { return nil, err } defer rows.Close() var wg sync.WaitGroup var mu sync.Mutex for rows.Next() { var departureAddress []byte var destinationAddress []byte var booking internal.Booking if err := rows.Scan(&booking.ID, &booking.Passenger.ID, &booking.Driver.ID, &booking.Status, &departureAddress, &destinationAddress, &booking.Pickup_date, &booking.Duration, &booking.Distance); err != nil { return nil, err } wg.Add(1) go func(booking internal.Booking, departureAddress, destinationAddress []byte) { defer wg.Done() // Common logic for both "driver" and "passenger" cases booking, err := s.populateBookingDetails(booking, departureAddress, destinationAddress) if err != nil { mu.Lock() err = fmt.Errorf("Error populating booking details: %w", err) mu.Unlock() return } mu.Lock() bookings = append(bookings, booking) mu.Unlock() }(booking, departureAddress, destinationAddress) } // Wait for all goroutines to finish wg.Wait() if err := rows.Err(); err != nil { return nil, err } return bookings, nil } func (s PostgresqlStorage) populateBookingDetails(booking internal.Booking, departureAddress, destinationAddress []byte) (internal.Booking, error) { var wg sync.WaitGroup var mu sync.Mutex errCh := make(chan error, 4) // Buffered channel to handle potential errors // Concurrently fetch passenger information wg.Add(1) go func() { defer wg.Done() passenger, err := s.GetPassenger(booking.Passenger.ID) if err != nil { errCh <- fmt.Errorf("Postgresql Storage GetBooking Error getting passenger: %w", err) return } mu.Lock() booking.Passenger = passenger.Passenger mu.Unlock() }() // Concurrently fetch driver information wg.Add(1) go func() { defer wg.Done() driver, err := s.GetDriver(booking.Driver.ID) if err != nil { errCh <- fmt.Errorf("Postgresql Storage GetBooking Error getting driver: %w", err) return } mu.Lock() booking.Driver = driver.Driver mu.Unlock() }() // Concurrently decode departureAddress into GeoJSON wg.Add(1) go func() { defer wg.Done() decodedDepartureAddress, err := geojson.UnmarshalFeature(departureAddress) if err != nil { errCh <- fmt.Errorf("Postgresql Storage FilterBookingsByStatus Error decoding Driver departure route into GeoJSON: %w", err) return } mu.Lock() booking.PassengerPickupAddress = decodedDepartureAddress mu.Unlock() }() // Concurrently decode destinationAddress into GeoJSON wg.Add(1) go func() { defer wg.Done() decodedDestinationAddress, err := geojson.UnmarshalFeature(destinationAddress) if err != nil { errCh <- fmt.Errorf("Postgresql Storage FilterBookingsByStatus Error decoding Driver destination route into GeoJSON: %w", err) return } mu.Lock() booking.PassengerDropAddress = decodedDestinationAddress mu.Unlock() }() // Close the error channel after all goroutines are done go func() { wg.Wait() close(errCh) }() // Check for errors using a select statement for err := range errCh { return internal.Booking{}, err // Return the first error encountered } return booking, nil } func (s PostgresqlStorage) GetAllDrivers(date int64) (drivers []internal.Driver, err error) { rows, err := s.DbConnection.Query(fmt.Sprintf("SELECT driver_id, driver_departure_route, driver_radius, last_name, first_name, grade, alias, picture, verified_identity, preferences, availabilities_type, availabilities, operator, car FROM %s", s.Tables["drivers"])) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var driver internal.Driver var departureRoute []byte var preferencesJSON []byte var availabilitiesJSON []byte var carJSON []byte if err := rows.Scan( &driver.Driver.ID, &departureRoute, &driver.Radius, &driver.Driver.LastName, &driver.Driver.FirstName, &driver.Driver.Grade, &driver.Driver.Alias, &driver.Driver.Picture, &driver.Driver.VerifiedIdentity, &preferencesJSON, &driver.AvailabilitiesType, &availabilitiesJSON, &driver.Driver.Operator, &carJSON, ); err != nil { return nil, err } if err := json.Unmarshal(departureRoute, &driver.Driver_departure_address); err != nil { return nil, err } if err := json.Unmarshal(preferencesJSON, &driver.Preferences); err != nil { return nil, err } if err := json.Unmarshal(carJSON, &driver.Car); err != nil { return nil, err } // Filter drivers based on date and time matching punctual or regular availabilities. if driver.AvailabilitiesType == internal.Punctual { if err := json.Unmarshal(availabilitiesJSON, &driver.PunctualAvailabilities); err != nil { return nil, err } year, month, day := time.Unix(date, 0).Date() dateHour := time.Unix(date, 0).UTC().Local() dateHour_string := dateHour.Format("3:04 PM") for _, avail := range driver.PunctualAvailabilities { availyear, availmonth, availday := time.Unix(avail.Date, 0).Date() if availyear == year && availmonth == month && availday == day { startTime, err := time.Parse("3:04 PM", avail.StartTime) if err != nil { return nil, err } endTime, err := time.Parse("3:04 PM", avail.EndTime) if err != nil { return nil, err } // Convert DateHour_string to time.Time dateHourTime, err := time.Parse("3:04 PM", dateHour_string) if err != nil { return nil, err } if dateHourTime.After(startTime) || dateHourTime.Equal(startTime) { if dateHourTime.Before(endTime) || dateHourTime.Equal(endTime) { drivers = append(drivers, driver) } } } // Check if the date and time match the punctual driver's availability. } } else if driver.AvailabilitiesType == internal.Regular { if err := json.Unmarshal(availabilitiesJSON, &driver.RegularAvailabilities); err != nil { return nil, err } dayOfWeek := strings.ToLower(time.Unix(date, 0).Local().Format("Mon")) dateHour := time.Unix(date, 0).UTC().Local() dateHour_string := dateHour.Format("3:04 PM") for _, avail := range driver.RegularAvailabilities { // Check if the day and time match the regular driver's availability. if strings.ToLower(avail.DayOfWeek) == dayOfWeek { startTime, err := time.Parse("3:04 PM", avail.StartTime) if err != nil { return nil, err } endTime, err := time.Parse("3:04 PM", avail.EndTime) if err != nil { return nil, err } // Convert DateHour_string to time.Time dateHourTime, err := time.Parse("3:04 PM", dateHour_string) if err != nil { return nil, err } if dateHourTime.After(startTime) || dateHourTime.Equal(startTime) { if dateHourTime.Before(endTime) || dateHourTime.Equal(endTime) { drivers = append(drivers, driver) } } } } } } if err := rows.Err(); err != nil { return nil, err } return drivers, nil } func (s PostgresqlStorage) DriverJourneys(departure_route *geojson.Feature, departure_date int64) (drivers []internal.Driver, err error) { allDrivers, err := s.GetAllDrivers(departure_date) if err != nil { return nil, err } for _, driver := range allDrivers { // Check if the departure route is the same as the driver's departure route. if departure_route.Point().Y() == driver.Driver_departure_address.Point().Y() && departure_route.Point().X() == driver.Driver_departure_address.Point().X() { drivers = append(drivers, driver) } else { // Calculate the distance between the departure point and the driver's departure route. coords1 := departure_route.Geometry.(orb.Point) coords2 := driver.Driver_departure_address.Geometry.(orb.Point) distance := utils.Haversine(coords1[1], coords1[0], coords2[1], coords2[0]) if int64(distance) <= int64(driver.Radius) { drivers = append(drivers, driver) } } } return drivers, nil }