Add MCP server

This commit is contained in:
Arnaud Delcasse
2025-11-03 11:45:23 +01:00
parent d992a7984f
commit 52de8d363e
18 changed files with 997 additions and 210 deletions

View File

@@ -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)