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

@@ -10,6 +10,8 @@ import (
"time"
groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
gen "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/gen"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
@@ -520,6 +522,7 @@ func (h *Handler) SolidarityTransportDriverJourneyHTTPHandler() http.HandlerFunc
driverID := vars["driverid"]
journeyID := vars["journeyid"]
passengerID := r.URL.Query().Get("passengerid")
replacesBookingID := r.URL.Query().Get("replaces_booking_id")
if r.Method == "POST" {
// Parse form data
@@ -531,7 +534,8 @@ func (h *Handler) SolidarityTransportDriverJourneyHTTPHandler() http.HandlerFunc
fmt.Sscanf(r.PostFormValue("return_waiting_time"), "%d", &returnWaitingTimeMinutes)
}
bookingID, err := h.applicationHandler.CreateSolidarityTransportJourneyBooking(r.Context(), driverID, journeyID, passengerID, motivation, message, doNotSend, returnWaitingTimeMinutes)
replacesBookingID := r.PostFormValue("replaces_booking_id")
bookingID, err := h.applicationHandler.CreateSolidarityTransportJourneyBooking(r.Context(), driverID, journeyID, passengerID, motivation, message, doNotSend, returnWaitingTimeMinutes, replacesBookingID)
if err != nil {
log.Error().Err(err).Msg("error creating solidarity transport journey booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
@@ -559,7 +563,7 @@ func (h *Handler) SolidarityTransportDriverJourneyHTTPHandler() http.HandlerFunc
return
}
h.renderer.SolidarityTransportDriverJourney(w, r, result.Journey, result.Driver, result.Passenger, result.Beneficiaries, result.PassengerWalletBalance, result.PricingResult)
h.renderer.SolidarityTransportDriverJourney(w, r, result.Journey, result.Driver, result.Passenger, result.Beneficiaries, result.PassengerWalletBalance, result.PricingResult, replacesBookingID)
}
}
@@ -592,7 +596,141 @@ func (h *Handler) SolidarityTransportBookingDisplayHTTPHandler() http.HandlerFun
return
}
h.renderer.SolidarityTransportBookingDisplay(w, r, result.Booking, result.Driver, result.Passenger, result.PassengerWalletBalance)
// If booking is cancelled, search for replacement drivers
var replacementDrivers any
var replacementDriversMap map[string]mobilityaccountsstorage.Account
var replacementPricing map[string]map[string]interface{}
var replacementLocations map[string]string
if result.Booking.Status == "CANCELLED" {
// Initialize maps to avoid nil pointer in template
replacementDriversMap = make(map[string]mobilityaccountsstorage.Account)
replacementPricing = make(map[string]map[string]interface{})
replacementLocations = make(map[string]string)
searchResult, err := h.applicationHandler.SearchJourneys(
r.Context(),
result.Booking.Journey.PassengerPickupDate,
result.Booking.Journey.PassengerPickup,
result.Booking.Journey.PassengerDrop,
result.Booking.PassengerId,
result.Booking.DriverId, // Exclude the original driver
result.Booking.GroupId, // Exclude drivers with bookings in this group
nil, // options - use defaults
)
if err == nil {
replacementDrivers = searchResult.DriverJourneys
replacementDriversMap = searchResult.Drivers
// Calculate pricing for each replacement driver journey
for _, dj := range searchResult.DriverJourneys {
// Extract driver departure location
if dj.DriverDeparture != nil && dj.DriverDeparture.Serialized != "" {
var feature map[string]interface{}
if err := json.Unmarshal([]byte(dj.DriverDeparture.Serialized), &feature); err == nil {
if props, ok := feature["properties"].(map[string]interface{}); ok {
if name, ok := props["name"].(string); ok {
replacementLocations[dj.Id] = name
} else if label, ok := props["label"].(string); ok {
replacementLocations[dj.Id] = label
}
}
}
}
pricingResult, err := h.applicationHandler.CalculateSolidarityTransportPricing(r.Context(), dj, result.Booking.PassengerId)
if err == nil {
pricing := map[string]interface{}{
"passenger": map[string]interface{}{
"amount": pricingResult["passenger"].Amount,
"currency": pricingResult["passenger"].Currency,
},
"driver": map[string]interface{}{
"amount": pricingResult["driver"].Amount,
"currency": pricingResult["driver"].Currency,
},
}
replacementPricing[dj.Id] = pricing
}
}
}
}
h.renderer.SolidarityTransportBookingDisplay(w, r, result.Booking, result.Driver, result.Passenger, result.PassengerWalletBalance, replacementDrivers, replacementDriversMap, replacementPricing, replacementLocations)
}
}
func (h *Handler) SolidarityTransportCreateReplacementBookingHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
vars := mux.Vars(r)
oldBookingID := vars["bookingid"]
// Get the old booking to retrieve its data
oldBookingResult, err := h.applicationHandler.GetSolidarityTransportBookingData(r.Context(), oldBookingID)
if err != nil {
log.Error().Err(err).Msg("error retrieving old booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Parse form data for new driver/journey
driverID := r.PostFormValue("driver_id")
journeyID := r.PostFormValue("journey_id")
message := r.PostFormValue("message")
doNotSend := r.PostFormValue("do_not_send") == "on"
// Use old booking's data
passengerID := oldBookingResult.Booking.PassengerId
motivation := ""
if oldBookingResult.Booking.Data != nil {
if m, ok := oldBookingResult.Booking.Data["motivation"].(string); ok {
motivation = m
}
}
// Get the new driver journey to retrieve journey information
driverJourneyResp, err := h.services.GRPC.SolidarityTransport.GetDriverJourney(r.Context(), &gen.GetDriverJourneyRequest{
DriverId: driverID,
JourneyId: journeyID,
})
if err != nil {
log.Error().Err(err).Msg("error retrieving new driver journey")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Calculate return waiting time based on journey type
returnWaitingTimeMinutes := 30 // Default for round trips
if driverJourneyResp.DriverJourney.Noreturn {
returnWaitingTimeMinutes = 0
}
// Create the replacement booking with pricing calculated in CreateSolidarityTransportJourneyBooking
bookingID, err := h.applicationHandler.CreateSolidarityTransportJourneyBooking(
r.Context(),
driverID,
journeyID,
passengerID,
motivation,
message, // message from form
doNotSend, // doNotSend from form checkbox
returnWaitingTimeMinutes,
oldBookingID,
)
if err != nil {
log.Error().Err(err).Msg("error creating replacement booking")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
log.Info().Str("booking_id", bookingID).Str("replaces", oldBookingID).Msg("Replacement booking created successfully")
// Redirect to the new booking
http.Redirect(w, r, fmt.Sprintf("/app/solidarity-transport/bookings/%s", bookingID), http.StatusFound)
}
}