multimodal-routing/libs/carpool/blablacardaily.go

228 lines
6.5 KiB
Go

package carpool
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"time"
"git.coopgo.io/coopgo-platform/carpool-service/interoperability/ocss"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
type BBCDailyCarpoolAPI struct {
OperatorId string
OperatorName string
APIKey string
BaseURL string
}
func NewBBCDailyCarpoolAPI(operatorId string, operatorName string, api_key string, baseURL string) (*BBCDailyCarpoolAPI, error) {
return &BBCDailyCarpoolAPI{
OperatorId: operatorId,
OperatorName: operatorName,
APIKey: api_key,
BaseURL: baseURL,
}, nil
}
type BBCDailyResult struct {
ID *string `json:"id"`
Duration *time.Duration `json:"duration"`
Distance *int64 `json:"distance"`
PickupLatitude *float64 `json:"pickup_latitude"`
PickupLongitude *float64 `json:"pickup_longitude"`
PickupDatetime *string `json:"pickup_datetime"`
DropoffLatitude *float64 `json:"dropoff_latitude"`
DropoffLongitude *float64 `json:"dropoff_longitude"`
DropoffDatetime *string `json:"dropoff_datetime"`
WebURL *string `json:"web_url"`
DepartureToPickupWalkingTime *time.Duration `json:"departure_to_pickup_walking_time"`
DropoffToArrivalWalkingTime *time.Duration `json:"dropoff_to_arrival_walking_time"`
JourneyPolyline *string `json:"journey_polyline"`
Price *struct {
Currency string `json:"currency"`
Amount float64 `json:"amount"`
} `json:"price"`
AvailableSeats *int64 `json:"available_seats"`
}
func (api *BBCDailyCarpoolAPI) GetOperatorId() string {
return api.OperatorId
}
func (api *BBCDailyCarpoolAPI) GetDriverJourneys(
departureLat float64,
departureLng float64,
arrivalLat float64,
arrivalLng float64,
departureDate time.Time,
timeDelta *time.Duration,
departureRadius int64,
arrivalRadius int64,
count int64,
) ([]ocss.DriverJourney, error) {
td := 1 * time.Hour
if timeDelta != nil {
td = *timeDelta
}
results, err := blablacarDailySearch(
api.BaseURL+"/search",
api.APIKey,
departureLat,
departureLng,
arrivalLat,
arrivalLng,
departureDate,
td,
)
if err != nil {
log.Error().Err(err).Msg("error in blablacarDailySearch")
return nil, err
}
journeys := []ocss.DriverJourney{}
for _, r := range results {
var price *ocss.Price
if r.Price != nil {
paying := ocss.Paying
price = &ocss.Price{
Type: &paying,
Amount: &r.Price.Amount,
Currency: &r.Price.Currency,
}
}
var pickupDatetime ocss.OCSSTime
// Try to parse as Unix timestamp first
i, err := strconv.ParseInt(*r.PickupDatetime, 10, 64)
if err != nil {
// If not a Unix timestamp, try parsing as ISO 8601 datetime
pd, err2 := time.Parse(time.RFC3339, *r.PickupDatetime)
if err2 != nil {
log.Error().Err(err2).Str("datetime", *r.PickupDatetime).Msg("error parsing pickup datetime")
pickupDatetime = ocss.OCSSTime(departureDate)
} else {
pickupDatetime = ocss.OCSSTime(pd)
}
} else {
pd := time.Unix(i, 0)
pickupDatetime = ocss.OCSSTime(pd)
}
driverJourney := ocss.DriverJourney{
DriverTrip: ocss.DriverTrip{
Driver: ocss.User{
ID: uuid.NewString(),
Operator: api.OperatorName,
Alias: "Nom anonymisé",
},
DepartureToPickupWalkingDuration: r.DepartureToPickupWalkingTime,
DropoffToArrivalWalkingDuration: r.DropoffToArrivalWalkingTime,
Trip: ocss.Trip{
Operator: api.OperatorName,
PassengerPickupLat: nilCheck(r.PickupLatitude),
PassengerPickupLng: nilCheck(r.PickupLongitude),
PassengerDropLat: nilCheck(r.DropoffLatitude),
PassengerDropLng: nilCheck(r.DropoffLongitude),
Duration: nilCheck(r.Duration),
Distance: r.Distance,
JourneyPolyline: r.JourneyPolyline,
},
},
JourneySchedule: ocss.JourneySchedule{
ID: r.ID,
PassengerPickupDate: pickupDatetime,
WebUrl: r.WebURL,
},
AvailableSteats: r.AvailableSeats,
Price: price,
}
journeys = append(journeys, driverJourney)
}
return journeys, nil
}
func (api *BBCDailyCarpoolAPI) GetPassengerJourneys(
departureLat float64,
departureLng float64,
arrivalLat float64,
arrivalLng float64,
departureDate time.Time,
timeDelta *time.Duration,
departureRadius int64,
arrivalRadius int64,
count int64,
) ([]ocss.PassengerJourney, error) {
return nil, errors.New("not implemented")
}
func blablacarDailySearch(url string, access_token string, departure_latitude float64, departure_longitude float64, arrival_latitude float64, arrival_longitude float64, departure_time time.Time, departure_timedelta time.Duration) ([]BBCDailyResult, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error().Err(err).Msg("new request issue")
return nil, err
}
req.Header.Set("Content-Type", "application/json")
q := req.URL.Query()
q.Add("access_token", access_token)
q.Add("departure_latitude", fmt.Sprintf("%f", departure_latitude))
q.Add("departure_longitude", fmt.Sprintf("%f", departure_longitude))
q.Add("arrival_latitude", fmt.Sprintf("%f", arrival_latitude))
q.Add("arrival_longitude", fmt.Sprintf("%f", arrival_longitude))
q.Add("departure_epoch", strconv.FormatInt(departure_time.Unix(), 10))
q.Add("departure_timedelta", fmt.Sprintf("%v", departure_timedelta.Abs().Seconds()))
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Error().Err(err).Msg("error in BBCDaily request")
return nil, err
}
defer resp.Body.Close()
log.Debug().
Int("status_code", resp.StatusCode).
Str("status", resp.Status).
Msg("BlaBlaCarDaily API response received")
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msg("error reading BBCDaily response body")
return nil, err
}
log.Debug().
Str("response_body", string(body)).
Msg("BlaBlaCarDaily API raw response")
response := []BBCDailyResult{}
err = json.Unmarshal(body, &response)
if err != nil {
log.Error().
Err(err).
Str("resp_body", string(body)).
Any("status", resp.Status).
Msg("cannot parse json response from BlaBlaCarDaily API")
return nil, err
}
log.Debug().
Int("results_count", len(response)).
Msg("BlaBlaCarDaily API response parsed successfully")
return response, nil
}
func nilCheck[T any](a *T) T {
var t T
if a == nil {
return t
}
return *a
}