parcoursmob/servers/web/application/journeys.go

419 lines
13 KiB
Go

package application
import (
"encoding/json"
"net/http"
"time"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
groupstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
savedsearchtypes "git.coopgo.io/coopgo-platform/saved-search/data/types"
"github.com/gorilla/mux"
"github.com/paulmach/orb/geojson"
"github.com/rs/zerolog/log"
)
func (h *Handler) JourneysSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract and convert HTTP parameters
departureDate := r.FormValue("departuredate")
departureTime := r.FormValue("departuretime")
departure := r.FormValue("departure")
destination := r.FormValue("destination")
passengerID := r.FormValue("passengerid")
solidarityTransportExcludeDriver := r.FormValue("solidarity_transport_exclude_driver")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
log.Info().
Str("departureDate", departureDate).
Str("departureTime", departureTime).
Time("departureDateTime", departureDateTime).
Str("timezone", departureDateTime.Location().String()).
Str("RFC3339", departureDateTime.Format(time.RFC3339)).
Msg("Journey search - parsed departure datetime")
}
// Parse departure location
var departureGeo *geojson.Feature
if departure == "" && passengerID != "" {
// Get passenger address
p, err := h.services.GetAccount(passengerID)
if err != nil {
log.Error().Err(err).Msg("could not retrieve passenger")
http.Error(w, "Not Found", http.StatusNotFound)
return
}
departureBytes, err := json.Marshal(p.Data["address"])
if err != nil {
log.Error().Err(err).Any("address", p.Data["address"]).Msg("could not marshal address")
} else {
departureGeo, err = geojson.UnmarshalFeature(departureBytes)
if err != nil {
log.Error().Err(err).Msg("error unmarshalling passenger departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
} else if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Call business logic handler
result, err := h.applicationHandler.SearchJourneys(
r.Context(),
departureDateTime,
departureGeo,
destinationGeo,
passengerID,
solidarityTransportExcludeDriver,
)
if err != nil {
log.Error().Err(err).Msg("error in journey search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Get beneficiaries for rendering
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("group not found in request context")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
group := g.(groupstorage.Group)
beneficiaries, err := h.services.GetBeneficiariesInGroup(group)
if err != nil {
log.Error().Err(err).Msg("issue retrieving beneficiaries")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Get saved searches for this group when no search has been performed
var savedSearches []*savedsearchtypes.SavedSearch
var beneficiariesMap map[string]mobilityaccountsstorage.Account
if !result.Searched {
savedSearches, err = h.applicationHandler.GetSavedSearchesByOwner(r.Context(), group.ID)
if err != nil {
log.Error().Err(err).Msg("issue retrieving saved searches")
// Don't fail the request, just log the error
savedSearches = []*savedsearchtypes.SavedSearch{}
}
// Create beneficiaries map for template lookup
beneficiariesMap = make(map[string]mobilityaccountsstorage.Account)
for _, b := range beneficiaries {
beneficiariesMap[b.ID] = b
}
}
// Render HTTP response
h.renderer.JourneysSearch(
w, r,
result.CarpoolResults,
result.TransitResults,
result.VehicleResults,
result.Searched,
departureGeo,
destinationGeo,
departureDate,
departureTime,
result.DriverJourneys,
result.Drivers,
result.OrganizedCarpools,
beneficiaries,
result.KnowledgeBaseResults,
passengerID,
savedSearches,
beneficiariesMap,
)
}
}
func (h *Handler) SaveSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract search parameters from URL query
query := r.URL.Query()
departureDate := query.Get("departuredate")
departureTime := query.Get("departuretime")
departure := query.Get("departure")
destination := query.Get("destination")
passengerID := query.Get("passengerid")
// Debug logging
log.Debug().
Str("departuredate", departureDate).
Str("departuretime", departureTime).
Str("departure", departure).
Str("destination", destination).
Str("passengerid", passengerID).
Str("query", r.URL.RawQuery).
Msg("SaveSearch request parameters")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse departure location
var departureGeo *geojson.Feature
if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Get group ID from session/context
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("group not found in request context")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
group := g.(groupstorage.Group)
// Prepare additional data
additionalData := map[string]interface{}{}
if passengerID != "" {
additionalData["passenger_id"] = passengerID
}
// Save the search using the business logic
err = h.applicationHandler.SaveSearch(
r.Context(),
group.ID,
departureDateTime,
departureGeo,
destinationGeo,
additionalData,
)
if err != nil {
log.Error().Err(err).Msg("error saving search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Redirect back to search with success message
http.Redirect(w, r, "/app/journeys/", http.StatusSeeOther)
}
}
func (h *Handler) DeleteSavedSearchHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get search ID from URL parameters
vars := mux.Vars(r)
searchID := vars["id"]
if searchID == "" {
log.Error().Msg("search ID not provided")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Get group ID from session/context for authorization
g := r.Context().Value(identification.GroupKey)
if g == nil {
log.Error().Msg("group not found in request context")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
group := g.(groupstorage.Group)
// Delete the saved search using the business logic
err := h.applicationHandler.DeleteSavedSearch(r.Context(), searchID, group.ID)
if err != nil {
log.Error().Err(err).Str("search_id", searchID).Msg("error deleting saved search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
log.Info().Str("search_id", searchID).Str("group_id", group.ID).Msg("saved search deleted successfully")
// Redirect back to journeys page
http.Redirect(w, r, "/app/journeys/?deleted=1", http.StatusSeeOther)
}
}
func (h *Handler) JourneysSearchCompactHTTPHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Error().Err(err).Msg("error parsing form")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
// Extract and convert HTTP parameters (same as JourneysSearchHTTPHandler)
departureDate := r.FormValue("departuredate")
departureTime := r.FormValue("departuretime")
departure := r.FormValue("departure")
destination := r.FormValue("destination")
passengerID := r.FormValue("passengerid")
solidarityTransportExcludeDriver := r.FormValue("solidarity_transport_exclude_driver")
// Parse timezone and datetime
locTime, err := time.LoadLocation("Europe/Paris")
if err != nil {
log.Error().Err(err).Msg("timezone error")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
var departureDateTime time.Time
if departureDate != "" && departureTime != "" {
departureDateTime, err = time.ParseInLocation("2006-01-02 15:04", departureDate+" "+departureTime, locTime)
if err != nil {
log.Error().Err(err).Msg("error parsing datetime")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse departure location
var departureGeo *geojson.Feature
if departure == "" && passengerID != "" {
// Get passenger address
p, err := h.services.GetAccount(passengerID)
if err != nil {
log.Error().Err(err).Msg("could not retrieve passenger")
http.Error(w, "Not Found", http.StatusNotFound)
return
}
departureBytes, err := json.Marshal(p.Data["address"])
if err != nil {
log.Error().Err(err).Any("address", p.Data["address"]).Msg("could not marshal address")
} else {
departureGeo, err = geojson.UnmarshalFeature(departureBytes)
if err != nil {
log.Error().Err(err).Msg("error unmarshalling passenger departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
} else if departure != "" {
departureGeo, err = geojson.UnmarshalFeature([]byte(departure))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling departure")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Parse destination location
var destinationGeo *geojson.Feature
if destination != "" {
destinationGeo, err = geojson.UnmarshalFeature([]byte(destination))
if err != nil {
log.Error().Err(err).Msg("error unmarshalling destination")
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}
// Call business logic handler
result, err := h.applicationHandler.SearchJourneys(
r.Context(),
departureDateTime,
departureGeo,
destinationGeo,
passengerID,
solidarityTransportExcludeDriver,
)
if err != nil {
log.Error().Err(err).Msg("error in journey search")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Render compact HTTP response
h.renderer.JourneysSearchCompact(
w, r,
result.CarpoolResults,
result.TransitResults,
result.VehicleResults,
result.Searched,
departureGeo,
destinationGeo,
departureDate,
departureTime,
result.DriverJourneys,
result.Drivers,
result.OrganizedCarpools,
result.KnowledgeBaseResults,
passengerID,
)
}
}