419 lines
13 KiB
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,
|
|
)
|
|
}
|
|
}
|
|
|