Beneficiaries export as XLSX
This commit is contained in:
99
renderer/xlsx/beneficiaries.go
Normal file
99
renderer/xlsx/beneficiaries.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package xlsx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/gender"
|
||||||
|
mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BeneficiaryGeoInfo struct {
|
||||||
|
Commune string
|
||||||
|
EPCI string
|
||||||
|
Departement string
|
||||||
|
Region string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *XLSXRenderer) Beneficiaries(w http.ResponseWriter, accounts []mobilityaccountsstorage.Account, geoInfoMap map[string]BeneficiaryGeoInfo) {
|
||||||
|
spreadsheet := r.NewSpreadsheet("Bénéficiaires")
|
||||||
|
|
||||||
|
// Build headers dynamically based on configuration
|
||||||
|
beneficiaryOptionalFields := r.Config.Get("modules.beneficiaries.profile_optional_fields")
|
||||||
|
beneficiaryFields := []string{"last_name", "first_name", "email", "phone_number", "birthdate", "gender", "file_number"}
|
||||||
|
headers := []string{"ID", "Nom", "Prénom", "Email", "Téléphone", "Date de naissance", "Genre", "Numéro de dossier"}
|
||||||
|
|
||||||
|
if beneficiaryOptionalFieldsList, ok := beneficiaryOptionalFields.([]interface{}); ok {
|
||||||
|
for _, field := range beneficiaryOptionalFieldsList {
|
||||||
|
if fieldMap, ok := field.(map[string]interface{}); ok {
|
||||||
|
if name, ok := fieldMap["name"].(string); ok {
|
||||||
|
beneficiaryFields = append(beneficiaryFields, name)
|
||||||
|
label := name
|
||||||
|
if labelVal, ok := fieldMap["label"].(string); ok {
|
||||||
|
label = labelVal
|
||||||
|
}
|
||||||
|
headers = append(headers, label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = append(headers, "Adresse", "Commune", "EPCI", "Département", "Région", "Archivé")
|
||||||
|
|
||||||
|
spreadsheet.SetHeaders(headers)
|
||||||
|
|
||||||
|
for _, account := range accounts {
|
||||||
|
row := []interface{}{}
|
||||||
|
|
||||||
|
row = append(row, account.ID)
|
||||||
|
|
||||||
|
for _, field := range beneficiaryFields {
|
||||||
|
value := getAccountFieldValue(account.Data, field)
|
||||||
|
if field == "gender" && value != "" {
|
||||||
|
value = gender.ISO5218ToString(value)
|
||||||
|
}
|
||||||
|
row = append(row, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address
|
||||||
|
address := ""
|
||||||
|
if addr, ok := account.Data["address"]; ok {
|
||||||
|
if addrMap, ok := addr.(map[string]interface{}); ok {
|
||||||
|
if props, ok := addrMap["properties"]; ok {
|
||||||
|
if propsMap, ok := props.(map[string]interface{}); ok {
|
||||||
|
if label, ok := propsMap["label"].(string); ok {
|
||||||
|
address = label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row = append(row, address)
|
||||||
|
|
||||||
|
// Geographic info (Commune, EPCI, Département, Région)
|
||||||
|
geoInfo := geoInfoMap[account.ID]
|
||||||
|
row = append(row, geoInfo.Commune)
|
||||||
|
row = append(row, geoInfo.EPCI)
|
||||||
|
row = append(row, geoInfo.Departement)
|
||||||
|
row = append(row, geoInfo.Region)
|
||||||
|
|
||||||
|
// Archived status
|
||||||
|
archived := "Non"
|
||||||
|
if archivedVal, ok := account.Data["archived"].(bool); ok && archivedVal {
|
||||||
|
archived = "Oui"
|
||||||
|
}
|
||||||
|
row = append(row, archived)
|
||||||
|
|
||||||
|
spreadsheet.AddRow(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"export-beneficiaires.xlsx\""))
|
||||||
|
|
||||||
|
if err := spreadsheet.GetFile().Write(w); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error generating Excel file")
|
||||||
|
http.Error(w, "Error generating Excel file", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
63
servers/web/exports/beneficiaries.go
Normal file
63
servers/web/exports/beneficiaries.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package exports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
xlsxrenderer "git.coopgo.io/coopgo-apps/parcoursmob/renderer/xlsx"
|
||||||
|
"github.com/paulmach/orb/geojson"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) Beneficiaries(w http.ResponseWriter, r *http.Request) {
|
||||||
|
archivedFilter := r.URL.Query().Get("archived") == "true"
|
||||||
|
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(), "", archivedFilter, addressGeoLayer, addressGeoCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to get beneficiaries")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve geographic layers (EPCI, Département, Région) for each beneficiary
|
||||||
|
geoInfoMap := map[string]xlsxrenderer.BeneficiaryGeoInfo{}
|
||||||
|
for _, account := range result.Accounts {
|
||||||
|
if addr, ok := account.Data["address"]; ok {
|
||||||
|
jsonAddr, err := json.Marshal(addr)
|
||||||
|
if err == nil {
|
||||||
|
addrFeature, err := geojson.UnmarshalFeature(jsonAddr)
|
||||||
|
if err == nil && addrFeature.Geometry != nil {
|
||||||
|
geo, err := h.services.Geography.GeoSearch(addrFeature)
|
||||||
|
if err == nil {
|
||||||
|
info := xlsxrenderer.BeneficiaryGeoInfo{}
|
||||||
|
if commune, ok := geo["communes"]; ok {
|
||||||
|
info.Commune = commune.Properties.MustString("nom")
|
||||||
|
}
|
||||||
|
if epci, ok := geo["epci"]; ok {
|
||||||
|
info.EPCI = epci.Properties.MustString("nom")
|
||||||
|
}
|
||||||
|
if dept, ok := geo["departements"]; ok {
|
||||||
|
info.Departement = dept.Properties.MustString("nom")
|
||||||
|
}
|
||||||
|
if region, ok := geo["regions"]; ok {
|
||||||
|
info.Region = region.Properties.MustString("nom")
|
||||||
|
}
|
||||||
|
geoInfoMap[account.ID] = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.renderer.XLSX.Beneficiaries(w, result.Accounts, geoInfoMap)
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/application"
|
"git.coopgo.io/coopgo-apps/parcoursmob/core/application"
|
||||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
|
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
|
||||||
"git.coopgo.io/coopgo-apps/parcoursmob/renderer"
|
"git.coopgo.io/coopgo-apps/parcoursmob/renderer"
|
||||||
|
"git.coopgo.io/coopgo-apps/parcoursmob/services"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,14 +15,16 @@ type Handler struct {
|
|||||||
applicationHandler *application.ApplicationHandler
|
applicationHandler *application.ApplicationHandler
|
||||||
idp *identification.IdentificationProvider
|
idp *identification.IdentificationProvider
|
||||||
renderer *renderer.Renderer
|
renderer *renderer.Renderer
|
||||||
|
services *services.ServicesHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(cfg *viper.Viper, applicationHandler *application.ApplicationHandler, idp *identification.IdentificationProvider, renderer *renderer.Renderer) *Handler {
|
func NewHandler(cfg *viper.Viper, applicationHandler *application.ApplicationHandler, idp *identification.IdentificationProvider, renderer *renderer.Renderer, services *services.ServicesHandler) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
applicationHandler: applicationHandler,
|
applicationHandler: applicationHandler,
|
||||||
idp: idp,
|
idp: idp,
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
|
services: services,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func (ws *WebServer) setupExportsRoutes(r *mux.Router) {
|
|||||||
export.HandleFunc("/solidarity-transport/drivers.xlsx", ws.exportsHandler.SolidarityTransportDrivers)
|
export.HandleFunc("/solidarity-transport/drivers.xlsx", ws.exportsHandler.SolidarityTransportDrivers)
|
||||||
export.HandleFunc("/organized-carpool/bookings.xlsx", ws.exportsHandler.OrganizedCarpoolBookings)
|
export.HandleFunc("/organized-carpool/bookings.xlsx", ws.exportsHandler.OrganizedCarpoolBookings)
|
||||||
export.HandleFunc("/organized-carpool/drivers.xlsx", ws.exportsHandler.OrganizedCarpoolDrivers)
|
export.HandleFunc("/organized-carpool/drivers.xlsx", ws.exportsHandler.OrganizedCarpoolDrivers)
|
||||||
|
export.HandleFunc("/beneficiaries/beneficiaries.xlsx", ws.exportsHandler.Beneficiaries)
|
||||||
export.Use(ws.idp.Middleware)
|
export.Use(ws.idp.Middleware)
|
||||||
export.Use(ws.idp.GroupsMiddleware)
|
export.Use(ws.idp.GroupsMiddleware)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func Run(cfg *viper.Viper, services *services.ServicesHandler, renderer *rendere
|
|||||||
// Initialize web handler subpackages
|
// Initialize web handler subpackages
|
||||||
appHandler: webapplication.NewHandler(cfg, renderer, applicationHandler, idp, services),
|
appHandler: webapplication.NewHandler(cfg, renderer, applicationHandler, idp, services),
|
||||||
authHandler: webauth.NewHandler(cfg, applicationHandler, idp, renderer),
|
authHandler: webauth.NewHandler(cfg, applicationHandler, idp, renderer),
|
||||||
exportsHandler: webexports.NewHandler(cfg, applicationHandler, idp, renderer),
|
exportsHandler: webexports.NewHandler(cfg, applicationHandler, idp, renderer, services),
|
||||||
extHandler: webexternal.NewHandler(cfg, applicationHandler, filestorage),
|
extHandler: webexternal.NewHandler(cfg, applicationHandler, filestorage),
|
||||||
protectedAPIHandler: webprotectedapi.NewHandler(cfg, applicationHandler),
|
protectedAPIHandler: webprotectedapi.NewHandler(cfg, applicationHandler),
|
||||||
webAPIHandler: webapi.NewHandler(cfg, idp, applicationHandler, cacheHandler),
|
webAPIHandler: webapi.NewHandler(cfg, idp, applicationHandler, cacheHandler),
|
||||||
|
|||||||
Reference in New Issue
Block a user