lot of new functionalities
This commit is contained in:
418
servers/web/application/journeys.go
Normal file
418
servers/web/application/journeys.go
Normal file
@@ -0,0 +1,418 @@
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user