package application import ( "bytes" "context" "encoding/json" "errors" "fmt" "image/png" "io" "log" "net/http" "sort" "strconv" "strings" "time" formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators" "git.coopgo.io/coopgo-apps/parcoursmob/utils/identification" profilepictures "git.coopgo.io/coopgo-apps/parcoursmob/utils/profile-pictures" "git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting" filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage" agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi" agendastorage "git.coopgo.io/coopgo-platform/agenda/storage" fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi" fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage" groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi" "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/google/uuid" "github.com/gorilla/mux" "google.golang.org/protobuf/types/known/structpb" ) type BeneficiariesForm struct { FirstName string `json:"first_name" validate:"required"` LastName string `json:"last_name" validate:"required"` Email string `json:"email" validate:"required,email"` Birthdate *time.Time `json:"birthdate" validate:"required"` PhoneNumber string `json:"phone_number" validate:"required,phoneNumber"` FileNumber string `json:"file_number"` Address any `json:"address,omitempty"` Gender string `json:"gender"` } type Event_Beneficiary interface { Name() string Date() time.Time DateEnd() time.Time Type() string Db() string ID() string Icons() string Status() int } type Event struct { IDVal string NameVal string DateVal time.Time DateEndVal time.Time TypeVal string DbVal string Deleted bool IconSet string StatusVal int } func (e Event) Name() string { return e.NameVal } func (e Event) Date() time.Time { return e.DateVal } func (e Event) DateEnd() time.Time { return e.DateEndVal } func (e Event) Type() string { return e.TypeVal } func (e Event) ID() string { return e.IDVal } func (e Event) Db() string { return e.DbVal } func (e Event) Icons() string { return e.IconSet } func (e Event) Status() int { return e.StatusVal } func sortByDate(events []Event_Beneficiary) { sort.Slice(events, func(i, j int) bool { return events[i].Date().After(events[j].Date()) }) } func (h *ApplicationHandler) BeneficiariesList(w http.ResponseWriter, r *http.Request) { accounts, err := h.beneficiaries(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } sort.Sort(sorting.BeneficiariesByName(accounts)) cacheid := uuid.NewString() h.cache.PutWithTTL(cacheid, accounts, 1*time.Hour) h.Renderer.BeneficiariesList(w, r, accounts, cacheid) } func (h *ApplicationHandler) BeneficiaryCreate(w http.ResponseWriter, r *http.Request) { g := r.Context().Value(identification.GroupKey) if g == nil { fmt.Println("Create beneficiary : could not find group") w.WriteHeader(http.StatusBadRequest) return } group := g.(storage.Group) if r.Method == "POST" { dataMap, err := parseBeneficiariesForm(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } data, err := structpb.NewValue(dataMap) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } request := &mobilityaccounts.RegisterRequest{ Account: &mobilityaccounts.Account{ Namespace: "parcoursmob_beneficiaries", Data: data.GetStructValue(), }, } resp, err := h.services.GRPC.MobilityAccounts.Register(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } subscribe := &groupsmanagement.SubscribeRequest{ Groupid: group.ID, Memberid: resp.Account.Id, } _, err = h.services.GRPC.GroupsManagement.Subscribe(context.TODO(), subscribe) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) return } h.Renderer.BeneficiaryCreate(w, r) } func (h *ApplicationHandler) BeneficiaryDisplay(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] documents := h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiaryID) request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } subscriptionrequest := &agenda.GetSubscriptionByUserRequest{ Subscriber: beneficiaryID, } subcriptionresp, err := h.services.GRPC.Agenda.GetSubscriptionByUser(context.TODO(), subscriptionrequest) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } events := []agendastorage.Event{} currentTime := time.Now().Truncate(24 * time.Hour) for _, e := range subcriptionresp.Subscription { eventresquest := &agenda.GetEventRequest{ Id: e.Eventid, } eventresp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), eventresquest) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } events = append(events, eventresp.Event.ToStorageType()) } sort.Sort(sorting.EventsByStartdate(events)) bookingsrequest := &fleets.GetDriverBookingsRequest{ Driver: beneficiaryID, } bookingsresp, err := h.services.GRPC.Fleets.GetDriverBookings(context.TODO(), bookingsrequest) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } bookings := []fleetsstorage.Booking{} for _, b := range bookingsresp.Bookings { bookings = append(bookings, b.ToStorageType()) } var events_list []Event_Beneficiary var status_event int for _, e := range events { if e.Startdate.After(currentTime) { status_event = 1 } else if e.Startdate.Before(currentTime) && e.Enddate.After(currentTime) || e.Enddate.Equal(currentTime) { status_event = 2 } else { status_event = 3 } event := Event{ NameVal: e.Name, DateVal: e.Startdate, DateEndVal: e.Enddate, TypeVal: e.Type, IDVal: e.ID, DbVal: "/app/agenda/", IconSet: "calendar", StatusVal: status_event, } events_list = append(events_list, event) } var status_booking int for _, b := range bookings { if b.Enddate.After(currentTime) || b.Enddate.Equal(currentTime) { GetVehiculeRequest := &fleets.GetVehicleRequest{ Vehicleid: b.Vehicleid, } GetVehiculeResp, err := h.services.GRPC.Fleets.GetVehicle(context.Background(), GetVehiculeRequest) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } if b.Startdate.After(currentTime) { status_booking = 1 } else if b.Startdate.Before(currentTime) && b.Enddate.After(currentTime) || b.Enddate.Equal(currentTime) { status_booking = 2 } else { status_booking = 3 } event := Event{ NameVal: GetVehiculeResp.Vehicle.ToStorageType().Data["name"].(string), DateVal: b.Startdate, DateEndVal: b.Enddate, TypeVal: "Réservation de véhicule", IDVal: b.ID, DbVal: "/app/vehicles-management/bookings/", IconSet: "vehicle", StatusVal: status_booking, } events_list = append(events_list, event) } } sortByDate(events_list) groupsrequest := &groupsmanagement.GetGroupsRequest{ Namespaces: []string{"parcoursmob_organizations"}, Member: beneficiaryID, } groupsresp, err := h.services.GRPC.GroupsManagement.GetGroups(context.TODO(), groupsrequest) if err != nil { fmt.Println(err) } organizations := []any{} for _, o := range groupsresp.Groups { organizations = append(organizations, o.ToStorageType()) } beneficiaries_file_types := h.config.GetStringSlice("modules.beneficiaries.documents_types") file_types_map := h.config.GetStringMapString("storage.files.file_types") h.Renderer.BeneficiaryDisplay(w, r, resp.Account.ToStorageType(), bookings, organizations, beneficiaries_file_types, file_types_map, documents, events_list) } func (h *ApplicationHandler) BeneficiaryUpdate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] if r.Method == "POST" { dataMap, err := parseBeneficiariesForm(r) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusBadRequest) return } data, err := structpb.NewValue(dataMap) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } request := &mobilityaccounts.UpdateDataRequest{ Account: &mobilityaccounts.Account{ Id: beneficiaryID, Namespace: "parcoursmob_beneficiaries", Data: data.GetStructValue(), }, } resp, err := h.services.GRPC.MobilityAccounts.UpdateData(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", resp.Account.Id), http.StatusFound) return } request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } //TODO filter namespaces //TODO filter groups h.Renderer.BeneficiaryUpdate(w, r, resp.Account.ToStorageType()) } func (h *ApplicationHandler) BeneficiaryPicture(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] request := &mobilityaccounts.GetAccountRequest{ Id: beneficiaryID, } resp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), request) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } account := resp.Account.ToStorageType() firstName := account.Data["first_name"].(string) lastName := account.Data["last_name"].(string) picture := profilepictures.DefaultProfilePicture(strings.ToUpper(firstName[0:1] + lastName[0:1])) buffer := new(bytes.Buffer) if err := png.Encode(buffer, picture); err != nil { log.Println("unable to encode image.") } w.Header().Set("Content-Type", "image/png") w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes()))) if _, err := w.Write(buffer.Bytes()); err != nil { log.Println("unable to write image.") } } func (h *ApplicationHandler) BeneficiaryDocuments(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] //r.ParseForm() r.ParseMultipartForm(100 * 1024 * 1024) document_type := r.FormValue("type") document_name := r.FormValue("name") file, header, err := r.FormFile("file-upload") if err != nil { fmt.Println(err) return } defer file.Close() fileid := uuid.NewString() metadata := map[string]string{ "type": document_type, "name": document_name, } if err := h.filestorage.Put(file, filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s_%s", beneficiaryID, fileid, header.Filename), header.Size, metadata); err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) } func (h *ApplicationHandler) BeneficiaryDocumentDownload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) beneficiaryID := vars["beneficiaryid"] document := vars["document"] file, info, err := h.filestorage.Get(filestorage.PREFIX_BENEFICIARIES, fmt.Sprintf("%s/%s", beneficiaryID, document)) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } w.Header().Set("Content-Type", info.ContentType) if _, err = io.Copy(w, file); err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } http.Redirect(w, r, fmt.Sprintf("/app/beneficiaries/%s", beneficiaryID), http.StatusFound) } func filterAccount(r *http.Request, a *mobilityaccounts.Account) bool { searchFilter, ok := r.URL.Query()["search"] if ok && len(searchFilter[0]) > 0 { name := a.Data.AsMap()["first_name"].(string) + " " + a.Data.AsMap()["last_name"].(string) if !strings.Contains(strings.ToLower(name), strings.ToLower(searchFilter[0])) { return false } } return true } // func BeneficiariesEventList() func (h *ApplicationHandler) beneficiaries(r *http.Request) ([]mobilityaccountsstorage.Account, error) { var accounts = []mobilityaccountsstorage.Account{} g := r.Context().Value(identification.GroupKey) if g == nil { return accounts, errors.New("no group provided") } group := g.(storage.Group) request := &mobilityaccounts.GetAccountsBatchRequest{ Accountids: group.Members, } resp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(context.TODO(), request) if err != nil { return accounts, err } for _, account := range resp.Accounts { if filterAccount(r, account) { a := account.ToStorageType() accounts = append(accounts, a) } } return accounts, err } func parseBeneficiariesForm(r *http.Request) (map[string]any, error) { if err := r.ParseForm(); err != nil { return nil, err } var date *time.Time if r.PostFormValue("birthdate") != "" { d, err := time.Parse("2006-01-02", r.PostFormValue("birthdate")) if err != nil { return nil, err } date = &d } formData := BeneficiariesForm{ FirstName: r.PostFormValue("first_name"), LastName: r.PostFormValue("last_name"), Email: r.PostFormValue("email"), Birthdate: date, PhoneNumber: r.PostFormValue("phone_number"), FileNumber: r.PostFormValue("file_number"), Gender: r.PostFormValue("gender"), } if r.PostFormValue("address") != "" { var a any json.Unmarshal([]byte(r.PostFormValue("address")), &a) formData.Address = a } validate := formvalidators.New() if err := validate.Struct(formData); err != nil { return nil, err } d, err := json.Marshal(formData) if err != nil { return nil, err } var dataMap map[string]any err = json.Unmarshal(d, &dataMap) if err != nil { return nil, err } return dataMap, nil }