Add MCP server
This commit is contained in:
@@ -282,6 +282,8 @@ func (h *ApplicationHandler) GetBeneficiaryData(ctx context.Context, beneficiary
|
||||
solidarityTransportBookings = append(solidarityTransportBookings, booking)
|
||||
}
|
||||
|
||||
// Don't filter out replaced bookings from beneficiary profile - show all bookings
|
||||
|
||||
// Collect unique driver IDs
|
||||
driverIDs := []string{}
|
||||
driverIDsMap := make(map[string]bool)
|
||||
|
||||
@@ -38,6 +38,16 @@ type SearchJourneysResult struct {
|
||||
KnowledgeBaseResults []any
|
||||
}
|
||||
|
||||
// SearchJourneyOptions contains per-request options for journey search
|
||||
type SearchJourneyOptions struct {
|
||||
DisableSolidarityTransport bool
|
||||
DisableOrganizedCarpool bool
|
||||
DisableCarpoolOperators bool
|
||||
DisableTransit bool
|
||||
DisableFleetVehicles bool
|
||||
DisableKnowledgeBase bool
|
||||
}
|
||||
|
||||
// SearchJourneys performs the business logic for journey search
|
||||
func (h *ApplicationHandler) SearchJourneys(
|
||||
ctx context.Context,
|
||||
@@ -46,6 +56,8 @@ func (h *ApplicationHandler) SearchJourneys(
|
||||
destinationGeo *geojson.Feature,
|
||||
passengerID string,
|
||||
solidarityTransportExcludeDriver string,
|
||||
solidarityExcludeGroupId string,
|
||||
options *SearchJourneyOptions,
|
||||
) (*SearchJourneysResult, error) {
|
||||
var (
|
||||
// Results
|
||||
@@ -64,6 +76,19 @@ func (h *ApplicationHandler) SearchJourneys(
|
||||
if departureGeo != nil && destinationGeo != nil && !departureDateTime.IsZero() {
|
||||
searched = true
|
||||
|
||||
// Default options if not provided
|
||||
if options == nil {
|
||||
options = &SearchJourneyOptions{}
|
||||
}
|
||||
|
||||
// Check solution type configurations (global config AND per-request options)
|
||||
solidarityTransportEnabled := h.config.GetBool("modules.journeys.solutions.solidarity_transport.enabled") && !options.DisableSolidarityTransport
|
||||
organizedCarpoolEnabled := h.config.GetBool("modules.journeys.solutions.organized_carpool.enabled") && !options.DisableOrganizedCarpool
|
||||
carpoolOperatorsEnabled := h.config.GetBool("modules.journeys.solutions.carpool_operators.enabled") && !options.DisableCarpoolOperators
|
||||
transitEnabled := h.config.GetBool("modules.journeys.solutions.transit.enabled") && !options.DisableTransit
|
||||
fleetVehiclesEnabled := h.config.GetBool("modules.journeys.solutions.fleet_vehicles.enabled") && !options.DisableFleetVehicles
|
||||
knowledgeBaseEnabled := h.config.GetBool("modules.journeys.solutions.knowledge_base.enabled") && !options.DisableKnowledgeBase
|
||||
|
||||
// SOLIDARITY TRANSPORT
|
||||
var err error
|
||||
drivers, err = h.services.GetAccountsInNamespacesMap([]string{"solidarity_drivers", "organized_carpool_drivers"})
|
||||
@@ -74,36 +99,58 @@ func (h *ApplicationHandler) SearchJourneys(
|
||||
protodep, _ := transformers.GeoJsonToProto(departureGeo)
|
||||
protodest, _ := transformers.GeoJsonToProto(destinationGeo)
|
||||
|
||||
log.Debug().Time("departure time", departureDateTime).Msg("calling driver journeys with ...")
|
||||
|
||||
res, err := h.services.GRPC.SolidarityTransport.GetDriverJourneys(ctx, &gen.GetDriverJourneysRequest{
|
||||
Departure: protodep,
|
||||
Arrival: protodest,
|
||||
DepartureDate: timestamppb.New(departureDateTime),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error in grpc call to GetDriverJourneys")
|
||||
} else {
|
||||
solidarityTransportResults = slices.Collect(func(yield func(*gen.SolidarityTransportDriverJourney) bool) {
|
||||
for _, dj := range res.DriverJourneys {
|
||||
if a, ok := drivers[dj.DriverId].Data["archived"]; ok {
|
||||
if archived, ok := a.(bool); ok {
|
||||
if archived {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if dj.DriverId == solidarityTransportExcludeDriver {
|
||||
continue
|
||||
}
|
||||
if !yield(dj) {
|
||||
return
|
||||
// Get driver IDs to exclude based on group_id (drivers who already have bookings in this group)
|
||||
excludedDriverIds := make(map[string]bool)
|
||||
if solidarityExcludeGroupId != "" {
|
||||
bookingsResp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBookings(ctx, &gen.GetSolidarityTransportBookingsRequest{
|
||||
StartDate: timestamppb.New(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)),
|
||||
EndDate: timestamppb.New(time.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC)),
|
||||
})
|
||||
if err == nil {
|
||||
for _, booking := range bookingsResp.Bookings {
|
||||
if booking.GroupId == solidarityExcludeGroupId {
|
||||
excludedDriverIds[booking.DriverId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if solidarityTransportEnabled {
|
||||
log.Debug().Time("departure time", departureDateTime).Msg("calling driver journeys with ...")
|
||||
|
||||
res, err := h.services.GRPC.SolidarityTransport.GetDriverJourneys(ctx, &gen.GetDriverJourneysRequest{
|
||||
Departure: protodep,
|
||||
Arrival: protodest,
|
||||
DepartureDate: timestamppb.New(departureDateTime),
|
||||
})
|
||||
sort.Slice(solidarityTransportResults, func(i, j int) bool {
|
||||
return solidarityTransportResults[i].DriverDistance < solidarityTransportResults[j].DriverDistance
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error in grpc call to GetDriverJourneys")
|
||||
} else {
|
||||
solidarityTransportResults = slices.Collect(func(yield func(*gen.SolidarityTransportDriverJourney) bool) {
|
||||
for _, dj := range res.DriverJourneys {
|
||||
if a, ok := drivers[dj.DriverId].Data["archived"]; ok {
|
||||
if archived, ok := a.(bool); ok {
|
||||
if archived {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if dj.DriverId == solidarityTransportExcludeDriver {
|
||||
continue
|
||||
}
|
||||
// Skip drivers who already have bookings in the same group
|
||||
if excludedDriverIds[dj.DriverId] {
|
||||
continue
|
||||
}
|
||||
if !yield(dj) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
sort.Slice(solidarityTransportResults, func(i, j int) bool {
|
||||
return solidarityTransportResults[i].DriverDistance < solidarityTransportResults[j].DriverDistance
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Get departure and destination addresses from properties
|
||||
@@ -119,124 +166,134 @@ func (h *ApplicationHandler) SearchJourneys(
|
||||
}
|
||||
}
|
||||
|
||||
radius := float64(5)
|
||||
// ORGANIZED CARPOOL
|
||||
organizedCarpoolResultsRes, err := h.services.GRPC.CarpoolService.DriverJourneys(ctx, &carpoolproto.DriverJourneysRequest{
|
||||
DepartureLat: departureGeo.Point().Lat(),
|
||||
DepartureLng: departureGeo.Point().Lon(),
|
||||
ArrivalLat: destinationGeo.Point().Lat(),
|
||||
ArrivalLng: destinationGeo.Point().Lon(),
|
||||
DepartureDate: timestamppb.New(departureDateTime),
|
||||
DepartureAddress: &departureAddress,
|
||||
ArrivalAddress: &destinationAddress,
|
||||
DepartureRadius: &radius,
|
||||
ArrivalRadius: &radius,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving organized carpools")
|
||||
} else {
|
||||
organizedCarpoolResults = organizedCarpoolResultsRes.DriverJourneys
|
||||
sort.Slice(organizedCarpoolResults, func(i, j int) bool {
|
||||
return *organizedCarpoolResults[i].Distance < *organizedCarpoolResults[j].Distance
|
||||
if organizedCarpoolEnabled {
|
||||
radius := float64(5)
|
||||
organizedCarpoolResultsRes, err := h.services.GRPC.CarpoolService.DriverJourneys(ctx, &carpoolproto.DriverJourneysRequest{
|
||||
DepartureLat: departureGeo.Point().Lat(),
|
||||
DepartureLng: departureGeo.Point().Lon(),
|
||||
ArrivalLat: destinationGeo.Point().Lat(),
|
||||
ArrivalLng: destinationGeo.Point().Lon(),
|
||||
DepartureDate: timestamppb.New(departureDateTime),
|
||||
DepartureAddress: &departureAddress,
|
||||
ArrivalAddress: &destinationAddress,
|
||||
DepartureRadius: &radius,
|
||||
ArrivalRadius: &radius,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving organized carpools")
|
||||
} else {
|
||||
organizedCarpoolResults = organizedCarpoolResultsRes.DriverJourneys
|
||||
sort.Slice(organizedCarpoolResults, func(i, j int) bool {
|
||||
return *organizedCarpoolResults[i].Distance < *organizedCarpoolResults[j].Distance
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
// CARPOOL OPERATORS
|
||||
carpools := make(chan *geojson.FeatureCollection)
|
||||
go h.services.InteropCarpool.Search(carpools, *departureGeo, *destinationGeo, departureDateTime)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for c := range carpools {
|
||||
carpoolResults = append(carpoolResults, c)
|
||||
}
|
||||
}()
|
||||
if carpoolOperatorsEnabled {
|
||||
carpools := make(chan *geojson.FeatureCollection)
|
||||
go h.services.InteropCarpool.Search(carpools, *departureGeo, *destinationGeo, departureDateTime)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for c := range carpools {
|
||||
carpoolResults = append(carpoolResults, c)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// TRANSIT
|
||||
transitch := make(chan *transitous.Itinerary)
|
||||
go func(transitch chan *transitous.Itinerary, departure *geojson.Feature, destination *geojson.Feature, datetime *time.Time) {
|
||||
defer close(transitch)
|
||||
response, err := h.services.TransitRouting.PlanWithResponse(ctx, &transitous.PlanParams{
|
||||
FromPlace: fmt.Sprintf("%f,%f", departure.Point().Lat(), departure.Point().Lon()),
|
||||
ToPlace: fmt.Sprintf("%f,%f", destination.Point().Lat(), destination.Point().Lon()),
|
||||
Time: datetime,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving transit data from Transitous server")
|
||||
return
|
||||
}
|
||||
for _, i := range response.Itineraries {
|
||||
transitch <- &i
|
||||
}
|
||||
}(transitch, departureGeo, destinationGeo, &departureDateTime)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
paris, _ := time.LoadLocation("Europe/Paris")
|
||||
requestedDay := departureDateTime.In(paris).Truncate(24 * time.Hour)
|
||||
if transitEnabled {
|
||||
transitch := make(chan *transitous.Itinerary)
|
||||
go func(transitch chan *transitous.Itinerary, departure *geojson.Feature, destination *geojson.Feature, datetime *time.Time) {
|
||||
defer close(transitch)
|
||||
response, err := h.services.TransitRouting.PlanWithResponse(ctx, &transitous.PlanParams{
|
||||
FromPlace: fmt.Sprintf("%f,%f", departure.Point().Lat(), departure.Point().Lon()),
|
||||
ToPlace: fmt.Sprintf("%f,%f", destination.Point().Lat(), destination.Point().Lon()),
|
||||
Time: datetime,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving transit data from Transitous server")
|
||||
return
|
||||
}
|
||||
for _, i := range response.Itineraries {
|
||||
transitch <- &i
|
||||
}
|
||||
}(transitch, departureGeo, destinationGeo, &departureDateTime)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
paris, _ := time.LoadLocation("Europe/Paris")
|
||||
requestedDay := departureDateTime.In(paris).Truncate(24 * time.Hour)
|
||||
|
||||
for itinerary := range transitch {
|
||||
// Only include journeys that start on the requested day (in Paris timezone)
|
||||
if !itinerary.StartTime.IsZero() && !itinerary.EndTime.IsZero() {
|
||||
log.Info().
|
||||
Time("startTime", itinerary.StartTime).
|
||||
Time("endTime", itinerary.EndTime).
|
||||
Str("startTimezone", itinerary.StartTime.Location().String()).
|
||||
Str("endTimezone", itinerary.EndTime.Location().String()).
|
||||
Str("startTimeRFC3339", itinerary.StartTime.Format(time.RFC3339)).
|
||||
Str("endTimeRFC3339", itinerary.EndTime.Format(time.RFC3339)).
|
||||
Msg("Journey search - received transit itinerary from Transitous")
|
||||
|
||||
startInParis := itinerary.StartTime.In(paris)
|
||||
startDay := startInParis.Truncate(24 * time.Hour)
|
||||
|
||||
// Check if journey starts on the requested day
|
||||
if startDay.Equal(requestedDay) {
|
||||
transitResults = append(transitResults, itinerary)
|
||||
} else {
|
||||
for itinerary := range transitch {
|
||||
// Only include journeys that start on the requested day (in Paris timezone)
|
||||
if !itinerary.StartTime.IsZero() && !itinerary.EndTime.IsZero() {
|
||||
log.Info().
|
||||
Str("requestedDay", requestedDay.Format("2006-01-02")).
|
||||
Str("startDay", startDay.Format("2006-01-02")).
|
||||
Msg("Journey search - filtered out transit journey (not on requested day)")
|
||||
Time("startTime", itinerary.StartTime).
|
||||
Time("endTime", itinerary.EndTime).
|
||||
Str("startTimezone", itinerary.StartTime.Location().String()).
|
||||
Str("endTimezone", itinerary.EndTime.Location().String()).
|
||||
Str("startTimeRFC3339", itinerary.StartTime.Format(time.RFC3339)).
|
||||
Str("endTimeRFC3339", itinerary.EndTime.Format(time.RFC3339)).
|
||||
Msg("Journey search - received transit itinerary from Transitous")
|
||||
|
||||
startInParis := itinerary.StartTime.In(paris)
|
||||
startDay := startInParis.Truncate(24 * time.Hour)
|
||||
|
||||
// Check if journey starts on the requested day
|
||||
if startDay.Equal(requestedDay) {
|
||||
transitResults = append(transitResults, itinerary)
|
||||
} else {
|
||||
log.Info().
|
||||
Str("requestedDay", requestedDay.Format("2006-01-02")).
|
||||
Str("startDay", startDay.Format("2006-01-02")).
|
||||
Msg("Journey search - filtered out transit journey (not on requested day)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}()
|
||||
}
|
||||
|
||||
// VEHICLES
|
||||
vehiclech := make(chan fleetsstorage.Vehicle)
|
||||
go h.vehicleRequest(vehiclech, departureDateTime.Add(-24*time.Hour), departureDateTime.Add(168*time.Hour))
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for vehicle := range vehiclech {
|
||||
vehicleResults = append(vehicleResults, vehicle)
|
||||
}
|
||||
slices.SortFunc(vehicleResults, sorting.VehiclesByDistanceFrom(*departureGeo))
|
||||
}()
|
||||
if fleetVehiclesEnabled {
|
||||
vehiclech := make(chan fleetsstorage.Vehicle)
|
||||
go h.vehicleRequest(vehiclech, departureDateTime.Add(-24*time.Hour), departureDateTime.Add(168*time.Hour))
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for vehicle := range vehiclech {
|
||||
vehicleResults = append(vehicleResults, vehicle)
|
||||
}
|
||||
slices.SortFunc(vehicleResults, sorting.VehiclesByDistanceFrom(*departureGeo))
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// KNOWLEDGE BASE
|
||||
departureGeoSearch, _ := h.services.Geography.GeoSearch(departureGeo)
|
||||
kbData := h.config.Get("knowledge_base")
|
||||
if kb, ok := kbData.([]any); ok {
|
||||
for _, sol := range kb {
|
||||
if solution, ok := sol.(map[string]any); ok {
|
||||
if g, ok := solution["geography"]; ok {
|
||||
if geography, ok := g.([]any); ok {
|
||||
for _, gg := range geography {
|
||||
if geog, ok := gg.(map[string]any); ok {
|
||||
if layer, ok := geog["layer"].(string); ok {
|
||||
code := geog["code"]
|
||||
geo, err := h.services.Geography.Find(layer, fmt.Sprintf("%v", code))
|
||||
if err == nil {
|
||||
geog["geography"] = geo
|
||||
geog["name"] = geo.Properties.MustString("nom")
|
||||
}
|
||||
if strings.Compare(fmt.Sprintf("%v", code), departureGeoSearch[layer].Properties.MustString("code")) == 0 {
|
||||
knowledgeBaseResults = append(knowledgeBaseResults, solution)
|
||||
break
|
||||
if knowledgeBaseEnabled {
|
||||
departureGeoSearch, _ := h.services.Geography.GeoSearch(departureGeo)
|
||||
kbData := h.config.Get("knowledge_base")
|
||||
if kb, ok := kbData.([]any); ok {
|
||||
for _, sol := range kb {
|
||||
if solution, ok := sol.(map[string]any); ok {
|
||||
if g, ok := solution["geography"]; ok {
|
||||
if geography, ok := g.([]any); ok {
|
||||
for _, gg := range geography {
|
||||
if geog, ok := gg.(map[string]any); ok {
|
||||
if layer, ok := geog["layer"].(string); ok {
|
||||
code := geog["code"]
|
||||
geo, err := h.services.Geography.Find(layer, fmt.Sprintf("%v", code))
|
||||
if err == nil {
|
||||
geog["geography"] = geo
|
||||
geog["name"] = geo.Properties.MustString("nom")
|
||||
}
|
||||
if strings.Compare(fmt.Sprintf("%v", code), departureGeoSearch[layer].Properties.MustString("code")) == 0 {
|
||||
knowledgeBaseResults = append(knowledgeBaseResults, solution)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,6 +358,18 @@ func (h *ApplicationHandler) GetSolidarityTransportOverview(ctx context.Context,
|
||||
transformedBookings = filterBookingsByGeography(transformedBookings, departurePolygons, destinationPolygons)
|
||||
transformedBookings = filterBookingsByPassengerAddressGeography(transformedBookings, beneficiariesMap, passengerAddressPolygons)
|
||||
|
||||
// Filter out replaced bookings from upcoming bookings list
|
||||
filteredBookings := []*solidaritytypes.Booking{}
|
||||
for _, booking := range transformedBookings {
|
||||
if booking.Data != nil {
|
||||
if _, hasReplacedBy := booking.Data["replaced_by"]; hasReplacedBy {
|
||||
continue // Skip bookings that have been replaced
|
||||
}
|
||||
}
|
||||
filteredBookings = append(filteredBookings, booking)
|
||||
}
|
||||
transformedBookings = filteredBookings
|
||||
|
||||
// Sort upcoming bookings by date (ascending - earliest first)
|
||||
sort.Slice(transformedBookings, func(i, j int) bool {
|
||||
if transformedBookings[i].Journey != nil && transformedBookings[j].Journey != nil {
|
||||
@@ -396,6 +408,8 @@ func (h *ApplicationHandler) GetSolidarityTransportOverview(ctx context.Context,
|
||||
transformedBookingsHistory = filterBookingsByGeography(transformedBookingsHistory, histDeparturePolygons, histDestinationPolygons)
|
||||
transformedBookingsHistory = filterBookingsByPassengerAddressGeography(transformedBookingsHistory, beneficiariesMap, histPassengerAddressPolygons)
|
||||
|
||||
// Don't filter out replaced bookings from history - we want to see them in history
|
||||
|
||||
// Sort history bookings by date (descending - most recent first)
|
||||
sort.Slice(transformedBookingsHistory, func(i, j int) bool {
|
||||
if transformedBookingsHistory[i].Journey != nil && transformedBookingsHistory[j].Journey != nil {
|
||||
@@ -503,6 +517,8 @@ func (h *ApplicationHandler) GetSolidarityTransportBookings(ctx context.Context,
|
||||
transformedBookings = filterBookingsByGeography(transformedBookings, departurePolygons, destinationPolygons)
|
||||
transformedBookings = filterBookingsByPassengerAddressGeography(transformedBookings, beneficiariesMap, passengerAddressPolygons)
|
||||
|
||||
// Don't filter out replaced bookings for exports - include all bookings
|
||||
|
||||
// Sort bookings by date
|
||||
sort.Slice(transformedBookings, func(i, j int) bool {
|
||||
if transformedBookings[i].Journey != nil && transformedBookings[j].Journey != nil {
|
||||
@@ -744,6 +760,8 @@ func (h *ApplicationHandler) GetSolidarityTransportDriverData(ctx context.Contex
|
||||
bookings = append(bookings, booking)
|
||||
}
|
||||
|
||||
// Don't filter out replaced bookings from driver profile - show all bookings
|
||||
|
||||
// Collect unique passenger IDs
|
||||
passengerIDs := []string{}
|
||||
passengerIDsMap := make(map[string]bool)
|
||||
@@ -1048,7 +1066,19 @@ func (h *ApplicationHandler) GetSolidarityTransportJourneyData(ctx context.Conte
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) CreateSolidarityTransportJourneyBooking(ctx context.Context, driverID, journeyID, passengerID, motivation, message string, doNotSend bool, returnWaitingTimeMinutes int) (string, error) {
|
||||
func (h *ApplicationHandler) CreateSolidarityTransportJourneyBooking(ctx context.Context, driverID, journeyID, passengerID, motivation, message string, doNotSend bool, returnWaitingTimeMinutes int, replacesBookingID string) (string, error) {
|
||||
// If this is a replacement booking, get the old booking's group_id
|
||||
var groupID string
|
||||
if replacesBookingID != "" {
|
||||
oldBookingResp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(ctx, &gen.GetSolidarityTransportBookingRequest{
|
||||
Id: replacesBookingID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get booking to replace: %w", err)
|
||||
}
|
||||
groupID = oldBookingResp.Booking.GroupId
|
||||
}
|
||||
|
||||
// Get journey for pricing calculation
|
||||
journeyRequest := &gen.GetDriverJourneyRequest{
|
||||
DriverId: driverID,
|
||||
@@ -1083,6 +1113,12 @@ func (h *ApplicationHandler) CreateSolidarityTransportJourneyBooking(ctx context
|
||||
returnWaitingDuration := int64(returnWaitingTimeMinutes) * int64(time.Minute)
|
||||
|
||||
// Create booking request
|
||||
dataFields := map[string]*structpb.Value{
|
||||
"motivation": structpb.NewStringValue(motivation),
|
||||
"message": structpb.NewStringValue(message),
|
||||
"do_not_send": structpb.NewBoolValue(doNotSend),
|
||||
}
|
||||
|
||||
bookingRequest := &gen.BookDriverJourneyRequest{
|
||||
PassengerId: passengerID,
|
||||
DriverId: driverID,
|
||||
@@ -1093,19 +1129,45 @@ func (h *ApplicationHandler) CreateSolidarityTransportJourneyBooking(ctx context
|
||||
DriverCompensationAmount: driverCompensation,
|
||||
DriverCompensationCurrency: "EUR",
|
||||
Data: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"motivation": structpb.NewStringValue(motivation),
|
||||
"message": structpb.NewStringValue(message),
|
||||
"do_not_send": structpb.NewBoolValue(doNotSend),
|
||||
},
|
||||
Fields: dataFields,
|
||||
},
|
||||
}
|
||||
|
||||
// Set group_id if this is a replacement booking
|
||||
if groupID != "" {
|
||||
bookingRequest.GroupId = &groupID
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.SolidarityTransport.BookDriverJourney(ctx, bookingRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If this is a replacement booking, update the old booking to mark it as replaced
|
||||
if replacesBookingID != "" {
|
||||
oldBookingResp, err := h.services.GRPC.SolidarityTransport.GetSolidarityTransportBooking(ctx, &gen.GetSolidarityTransportBookingRequest{
|
||||
Id: replacesBookingID,
|
||||
})
|
||||
if err == nil {
|
||||
oldBooking, err := solidaritytransformers.BookingProtoToType(oldBookingResp.Booking)
|
||||
if err == nil && oldBooking != nil {
|
||||
if oldBooking.Data == nil {
|
||||
oldBooking.Data = make(map[string]any)
|
||||
}
|
||||
oldBooking.Data["replaced_by"] = resp.Booking.Id
|
||||
|
||||
// Update the booking
|
||||
oldBookingProto, _ := solidaritytransformers.BookingTypeToProto(oldBooking)
|
||||
_, err = h.services.GRPC.SolidarityTransport.UpdateSolidarityTransportBooking(ctx, &gen.UpdateSolidarityTransportBookingRequest{
|
||||
Booking: oldBookingProto,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("old_booking_id", replacesBookingID).Str("new_booking_id", resp.Booking.Id).Msg("failed to mark old booking as replaced")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send SMS if not disabled
|
||||
if !doNotSend && message != "" {
|
||||
send_message := strings.ReplaceAll(message, "{booking_id}", resp.Booking.Id)
|
||||
@@ -1253,6 +1315,20 @@ func (h *ApplicationHandler) pricingGeography(loc *geojson.Feature) pricing.Geog
|
||||
}
|
||||
}
|
||||
|
||||
// CalculateSolidarityTransportPricing is the exported wrapper for calculateSolidarityTransportPricing
|
||||
func (h *ApplicationHandler) CalculateSolidarityTransportPricing(ctx context.Context, journey *gen.SolidarityTransportDriverJourney, passengerID string) (map[string]pricing.Price, error) {
|
||||
// Get passenger account
|
||||
var passenger mobilityaccountsstorage.Account
|
||||
if passengerID != "" {
|
||||
passengerResp, err := h.services.GetAccount(passengerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get passenger account: %w", err)
|
||||
}
|
||||
passenger = passengerResp
|
||||
}
|
||||
return h.calculateSolidarityTransportPricing(ctx, journey, passengerID, passenger)
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) calculateSolidarityTransportPricing(ctx context.Context, journey *gen.SolidarityTransportDriverJourney, passengerID string, passenger mobilityaccountsstorage.Account) (map[string]pricing.Price, error) {
|
||||
// Transform proto to type for geography access
|
||||
journeyType, err := solidaritytransformers.DriverJourneyProtoToType(journey)
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
package geo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/paulmach/orb/geojson"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type GeoService struct {
|
||||
peliasURL string
|
||||
geoType string
|
||||
baseURL string
|
||||
autocompleteURL string
|
||||
}
|
||||
|
||||
func NewGeoService(peliasURL string) *GeoService {
|
||||
return &GeoService{peliasURL: peliasURL}
|
||||
func NewGeoService(geoType, baseURL, autocompleteEndpoint string) *GeoService {
|
||||
return &GeoService{
|
||||
geoType: geoType,
|
||||
baseURL: baseURL,
|
||||
autocompleteURL: baseURL + autocompleteEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
type AutocompleteResult struct {
|
||||
Features []any
|
||||
}
|
||||
|
||||
func (s *GeoService) Autocomplete(text string) (*AutocompleteResult, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("%s/autocomplete?text=%s", s.peliasURL, text))
|
||||
func (s *GeoService) Autocomplete(text string) (*geojson.FeatureCollection, error) {
|
||||
resp, err := http.Get(s.autocompleteURL + text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -34,17 +35,11 @@ func (s *GeoService) Autocomplete(text string) (*AutocompleteResult, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response map[string]any
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
featureCollection, err := geojson.UnmarshalFeatureCollection(body)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to unmarshal feature collection")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
features, ok := response["features"].([]any)
|
||||
if !ok {
|
||||
features = []any{}
|
||||
}
|
||||
|
||||
return &AutocompleteResult{
|
||||
Features: features,
|
||||
}, nil
|
||||
return featureCollection, nil
|
||||
}
|
||||
Reference in New Issue
Block a user