evol: filter geography on beneficiaries

This commit is contained in:
Arnaud Delcasse
2026-02-25 10:15:22 +01:00
parent 1b1c4443fc
commit 092d1acfbd
3 changed files with 99 additions and 9 deletions

View File

@@ -31,6 +31,8 @@ import (
solidaritytransformers "git.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/transformers"
solidaritytypes "git.coopgo.io/coopgo-platform/solidarity-transport/types"
"github.com/google/uuid"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
"github.com/rs/zerolog/log"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
@@ -41,12 +43,38 @@ type BeneficiariesResult struct {
CacheID string
}
func (h *ApplicationHandler) GetBeneficiaries(ctx context.Context, searchFilter string, archivedFilter bool) (*BeneficiariesResult, error) {
func (h *ApplicationHandler) GetBeneficiaries(ctx context.Context, searchFilter string, archivedFilter bool, addressGeoLayer, addressGeoCode string) (*BeneficiariesResult, error) {
accounts, err := h.getBeneficiariesWithFilters(ctx, searchFilter, archivedFilter)
if err != nil {
return nil, err
}
// Apply address geography filtering
if addressGeoLayer != "" && addressGeoCode != "" {
addressPolygons, err := h.loadGeographyPolygon(addressGeoLayer, addressGeoCode)
if err != nil {
log.Warn().Err(err).Msg("failed to load beneficiary address geography filter")
} else {
filtered := []mobilityaccountsstorage.Account{}
for _, account := range accounts {
if addr, ok := account.Data["address"]; 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, addressPolygons) {
filtered = append(filtered, account)
}
}
}
}
}
}
accounts = filtered
}
}
sort.Sort(sorting.BeneficiariesByName(accounts))
cacheID := uuid.NewString()

View File

@@ -30,15 +30,25 @@ func (s BeneficiariesListState) JSONWithLimits(a int, b int) template.JS {
return s.JSON()
}
func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string, archived bool) {
func (renderer *Renderer) BeneficiariesList(w http.ResponseWriter, r *http.Request, accounts []mobilityaccountsstorage.Account, cacheid string, archived bool, enrichedGeoFilters []map[string]string, selectedAddressGeo string) {
files := renderer.ThemeConfig.GetStringSlice("views.beneficiaries.list.files")
geoFiltersEnabled := len(enrichedGeoFilters) > 0
state := NewState(r, renderer.ThemeConfig, beneficiariesMenu)
state.ViewState = BeneficiariesListState{
state.ViewState = map[string]any{
"list": BeneficiariesListState{
Count: len(accounts),
CacheId: cacheid,
Beneficiaries: accounts,
Archived: archived,
},
"geography_filters_enabled": geoFiltersEnabled,
"geography_filters_list": enrichedGeoFilters,
"archived": archived,
"filters": map[string]any{
"beneficiary_address_geo": selectedAddressGeo,
},
}
renderer.Render("beneficiaries_list", w, r, files, state)

View File

@@ -5,7 +5,9 @@ import (
"fmt"
"io"
"net/http"
"sort"
"strconv"
"strings"
"time"
formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/form-validators"
@@ -83,14 +85,64 @@ func (h *Handler) BeneficiariesListHTTPHandler() http.HandlerFunc {
archivedFilter = true
}
result, err := h.applicationHandler.GetBeneficiaries(r.Context(), searchFilter, archivedFilter)
// Extract beneficiary address geography filter
beneficiaryAddressGeo := r.URL.Query().Get("beneficiary_address_geo")
addressGeoLayer, addressGeoCode := "", ""
if beneficiaryAddressGeo != "" {
parts := strings.SplitN(beneficiaryAddressGeo, ":", 2)
if len(parts) == 2 {
addressGeoLayer, addressGeoCode = parts[0], parts[1]
}
}
result, err := h.applicationHandler.GetBeneficiaries(r.Context(), searchFilter, archivedFilter, addressGeoLayer, addressGeoCode)
if err != nil {
log.Error().Err(err).Msg("error retrieving beneficiaries")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
h.renderer.BeneficiariesList(w, r, result.Accounts, result.CacheID, archivedFilter)
// Enrich geography filters with names from geography service
var enrichedGeoFilters []map[string]string
if h.cfg.GetBool("geography.filters.enabled") {
geoFilters := h.cfg.Get("geography.filters.geographies")
if geoList, ok := geoFilters.([]any); ok {
for _, geoItem := range geoList {
if geoMap, ok := geoItem.(map[string]any); ok {
layer := ""
code := ""
if l, ok := geoMap["layer"].(string); ok {
layer = l
}
if c, ok := geoMap["code"].(string); ok {
code = c
}
enrichedGeo := map[string]string{
"layer": layer,
"code": code,
"name": code,
}
if layer != "" && code != "" {
if geoFeature, err := h.services.Geography.Find(layer, code); err == nil {
if name := geoFeature.Properties.MustString("nom"); name != "" {
enrichedGeo["name"] = name
}
}
}
enrichedGeoFilters = append(enrichedGeoFilters, enrichedGeo)
}
}
}
sort.Slice(enrichedGeoFilters, func(i, j int) bool {
return enrichedGeoFilters[i]["name"] < enrichedGeoFilters[j]["name"]
})
}
h.renderer.BeneficiariesList(w, r, result.Accounts, result.CacheID, archivedFilter, enrichedGeoFilters, beneficiaryAddressGeo)
}
}