parcoursmob/core/application/dashboard.go

229 lines
6.8 KiB
Go
Executable File

package application
import (
"context"
"encoding/json"
"fmt"
"sort"
"sync"
"time"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/sorting"
agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi"
agendastorage "git.coopgo.io/coopgo-platform/agenda/storage"
fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi"
fleetstorage "git.coopgo.io/coopgo-platform/fleets/storage"
"git.coopgo.io/coopgo-platform/groups-management/storage"
mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi"
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
"github.com/rs/zerolog/log"
"google.golang.org/protobuf/types/known/timestamppb"
)
type DashboardResult struct {
Accounts []mobilityaccountsstorage.Account
Members []mobilityaccountsstorage.Account
Events []agendastorage.Event
Bookings []fleetstorage.Booking
SolidarityDrivers []mobilityaccountsstorage.Account
OrganizedCarpoolDrivers []mobilityaccountsstorage.Account
}
func (h *ApplicationHandler) GetDashboardData(ctx context.Context, driverAddressGeoLayer, driverAddressGeoCode string) (*DashboardResult, error) {
g := ctx.Value(identification.GroupKey)
if g == nil {
return nil, fmt.Errorf("no group found in context")
}
group := g.(storage.Group)
// Load geography polygons for driver address filtering
var driverAddressPolygons []orb.Polygon
if driverAddressGeoLayer != "" && driverAddressGeoCode != "" {
polygons, err := h.loadGeographyPolygon(driverAddressGeoLayer, driverAddressGeoCode)
if err != nil {
log.Warn().Err(err).Msg("failed to load driver address geography filter")
} else {
driverAddressPolygons = polygons
}
}
// Get accounts (recent beneficiaries)
request := &mobilityaccounts.GetAccountsBatchRequest{
Accountids: group.Members,
}
resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(ctx, request)
if err != nil {
return nil, err
}
accounts := []mobilityaccountsstorage.Account{}
// We only display the 5 most recent here
count := len(resp.Accounts)
min := count - 5
if min < 0 {
min = 0
}
for _, account := range resp.Accounts[min:] {
// Check if not archived
if archived, ok := account.Data.AsMap()["archived"].(bool); !ok || !archived {
a := account.ToStorageType()
accounts = append([]mobilityaccountsstorage.Account{a}, accounts...)
}
}
// Fetch remaining data in parallel using goroutines
var wg sync.WaitGroup
var mu sync.Mutex
var members []mobilityaccountsstorage.Account
var events []agendastorage.Event
var bookings []fleetstorage.Booking
var solidarityDrivers []mobilityaccountsstorage.Account
var organizedCarpoolDrivers []mobilityaccountsstorage.Account
// Get members
wg.Add(1)
go func() {
defer wg.Done()
m, _, err := h.groupmembers(group.ID)
if err == nil {
mu.Lock()
members = m
mu.Unlock()
}
}()
// Get events
wg.Add(1)
go func() {
defer wg.Done()
eventsresp, err := h.services.GRPC.Agenda.GetEvents(ctx, &agenda.GetEventsRequest{
Namespaces: []string{"parcoursmob_dispositifs"},
Mindate: timestamppb.Now(),
})
if err == nil {
mu.Lock()
for _, e := range eventsresp.Events {
events = append(events, e.ToStorageType())
}
sort.Sort(sorting.EventsByStartdate(events))
mu.Unlock()
}
}()
// Get bookings
wg.Add(1)
go func() {
defer wg.Done()
bookingsresp, err := h.services.GRPC.Fleets.GetBookings(ctx, &fleets.GetBookingsRequest{
Namespaces: []string{"parcoursmob_dispositifs"},
})
if err == nil {
mu.Lock()
for _, b := range bookingsresp.Bookings {
if b.Enddate.AsTime().After(time.Now()) {
bookings = append(bookings, b.ToStorageType())
}
}
mu.Unlock()
}
}()
// Get solidarity transport drivers
wg.Add(1)
go func() {
defer wg.Done()
solidarityRequest := &mobilityaccounts.GetAccountsRequest{
Namespaces: []string{"solidarity_drivers"},
}
solidarityResp, err := h.services.GRPC.MobilityAccounts.GetAccounts(ctx, solidarityRequest)
if err == nil {
mu.Lock()
for _, account := range solidarityResp.Accounts {
// Only include non-archived drivers with addresses
if archived, ok := account.Data.AsMap()["archived"].(bool); !ok || !archived {
if address, ok := account.Data.AsMap()["address"]; ok && address != nil {
// Apply geography filter if specified
if len(driverAddressPolygons) > 0 {
if addr, ok := account.Data.AsMap()["address"].(map[string]interface{}); ok {
jsonAddr, err := json.Marshal(addr)
if err == nil {
addrGeojson, err := geojson.UnmarshalFeature(jsonAddr)
if err == nil && addrGeojson.Geometry != nil {
if point, ok := addrGeojson.Geometry.(orb.Point); ok {
if isPointInGeographies(point, driverAddressPolygons) {
solidarityDrivers = append(solidarityDrivers, account.ToStorageType())
}
}
}
}
}
} else {
solidarityDrivers = append(solidarityDrivers, account.ToStorageType())
}
}
}
}
mu.Unlock()
}
}()
// Get organized carpool drivers
wg.Add(1)
go func() {
defer wg.Done()
carpoolRequest := &mobilityaccounts.GetAccountsRequest{
Namespaces: []string{"organized_carpool_drivers"},
}
carpoolResp, err := h.services.GRPC.MobilityAccounts.GetAccounts(ctx, carpoolRequest)
if err == nil {
mu.Lock()
for _, account := range carpoolResp.Accounts {
// Only include non-archived drivers with addresses
if archived, ok := account.Data.AsMap()["archived"].(bool); !ok || !archived {
if address, ok := account.Data.AsMap()["address"]; ok && address != nil {
// Apply geography filter if specified
if len(driverAddressPolygons) > 0 {
if addr, ok := account.Data.AsMap()["address"].(map[string]interface{}); ok {
jsonAddr, err := json.Marshal(addr)
if err == nil {
addrGeojson, err := geojson.UnmarshalFeature(jsonAddr)
if err == nil && addrGeojson.Geometry != nil {
if point, ok := addrGeojson.Geometry.(orb.Point); ok {
if isPointInGeographies(point, driverAddressPolygons) {
organizedCarpoolDrivers = append(organizedCarpoolDrivers, account.ToStorageType())
}
}
}
}
}
} else {
organizedCarpoolDrivers = append(organizedCarpoolDrivers, account.ToStorageType())
}
}
}
}
mu.Unlock()
}
}()
// Wait for all goroutines to complete
wg.Wait()
return &DashboardResult{
Accounts: accounts,
Members: members,
Events: events,
Bookings: bookings,
SolidarityDrivers: solidarityDrivers,
OrganizedCarpoolDrivers: organizedCarpoolDrivers,
}, nil
}