lot of new functionalities
This commit is contained in:
602
core/application/agenda.go
Executable file
602
core/application/agenda.go
Executable file
@@ -0,0 +1,602 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/identification"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/core/utils/sorting"
|
||||
"git.coopgo.io/coopgo-apps/parcoursmob/services"
|
||||
filestorage "git.coopgo.io/coopgo-apps/parcoursmob/core/utils/storage"
|
||||
agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi"
|
||||
agendastorage "git.coopgo.io/coopgo-platform/agenda/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"
|
||||
ics "github.com/arran4/golang-ical"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
|
||||
type AgendaEventsResult struct {
|
||||
Events []agendastorage.Event
|
||||
Groups map[string]any
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetAgendaEvents(ctx context.Context, minDate, maxDate *time.Time) (*AgendaEventsResult, error) {
|
||||
request := &agenda.GetEventsRequest{
|
||||
Namespaces: []string{"parcoursmob_dispositifs"},
|
||||
}
|
||||
|
||||
if minDate != nil {
|
||||
request.Mindate = timestamppb.New(*minDate)
|
||||
}
|
||||
if maxDate != nil {
|
||||
request.Maxdate = timestamppb.New(*maxDate)
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvents(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := []agendastorage.Event{}
|
||||
groupids := []string{}
|
||||
for _, e := range resp.Events {
|
||||
groupids = append(groupids, e.Owners...)
|
||||
responses = append(responses, e.ToStorageType())
|
||||
}
|
||||
|
||||
sort.Sort(sorting.EventsByStartdate(responses))
|
||||
|
||||
groups := map[string]any{}
|
||||
if len(groupids) > 0 {
|
||||
groupsresp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(ctx, &groupsmanagement.GetGroupsBatchRequest{
|
||||
Groupids: groupids,
|
||||
})
|
||||
if err == nil {
|
||||
for _, g := range groupsresp.Groups {
|
||||
groups[g.Id] = g.ToStorageType()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &AgendaEventsResult{
|
||||
Events: responses,
|
||||
Groups: groups,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (h *ApplicationHandler) CreateAgendaEvent(ctx context.Context, name, eventType, description string, address any, allday bool, startdate, enddate *time.Time, starttime, endtime string, maxSubscribers int, file io.Reader, filename string, fileSize int64, documentType, documentName string) (string, error) {
|
||||
// Get current group
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return "", fmt.Errorf("no group found in context")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
data, _ := structpb.NewStruct(map[string]any{
|
||||
"address": address,
|
||||
})
|
||||
|
||||
request := &agenda.CreateEventRequest{
|
||||
Event: &agenda.Event{
|
||||
Namespace: "parcoursmob_dispositifs",
|
||||
Owners: []string{group.ID},
|
||||
Type: eventType,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Startdate: timestamppb.New(*startdate),
|
||||
Enddate: timestamppb.New(*enddate),
|
||||
Starttime: starttime,
|
||||
Endtime: endtime,
|
||||
Allday: allday,
|
||||
MaxSubscribers: int64(maxSubscribers),
|
||||
Data: data,
|
||||
Deleted: false,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.CreateEvent(ctx, request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Handle file upload if provided
|
||||
if file != nil && filename != "" {
|
||||
fileid := uuid.NewString()
|
||||
|
||||
metadata := map[string]string{
|
||||
"file_type": documentType,
|
||||
"file_name": documentName,
|
||||
}
|
||||
|
||||
if err := h.filestorage.Put(file, filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s_%s", resp.Event.Id, fileid, filename), fileSize, metadata); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return resp.Event.Id, nil
|
||||
}
|
||||
|
||||
|
||||
type AgendaEventResult struct {
|
||||
Event agendastorage.Event
|
||||
Group storage.Group
|
||||
Documents []filestorage.FileInfo
|
||||
Subscribers map[string]any
|
||||
Accounts []any
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetAgendaEvent(ctx context.Context, eventID string) (*AgendaEventResult, error) {
|
||||
request := &agenda.GetEventRequest{
|
||||
Id: eventID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvent(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grouprequest := &groupsmanagement.GetGroupRequest{
|
||||
Id: resp.Event.Owners[0],
|
||||
}
|
||||
|
||||
groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(ctx, grouprequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subscribers := map[string]any{}
|
||||
accids := []string{}
|
||||
for _, v := range resp.Event.Subscriptions {
|
||||
accids = append(accids, v.Subscriber)
|
||||
}
|
||||
|
||||
if len(accids) > 0 {
|
||||
subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
|
||||
ctx,
|
||||
&mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: accids,
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
for _, sub := range subscriberresp.Accounts {
|
||||
subscribers[sub.Id] = sub.ToStorageType()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return nil, fmt.Errorf("no group found in context")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
accountids := []string{}
|
||||
for _, m := range group.Members {
|
||||
if !contains(resp.Event.Subscriptions, m) {
|
||||
accountids = append(accountids, m)
|
||||
}
|
||||
}
|
||||
|
||||
accounts := []any{}
|
||||
if len(accountids) > 0 {
|
||||
accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
|
||||
ctx,
|
||||
&mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: accountids,
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
for _, acc := range accountresp.Accounts {
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
documents := h.filestorage.List(filestorage.PREFIX_AGENDA + "/" + eventID)
|
||||
|
||||
return &AgendaEventResult{
|
||||
Event: resp.Event.ToStorageType(),
|
||||
Group: groupresp.Group.ToStorageType(),
|
||||
Documents: documents,
|
||||
Subscribers: subscribers,
|
||||
Accounts: accounts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
func (h *ApplicationHandler) SubscribeToAgendaEvent(ctx context.Context, eventID, subscriber string, subscriptionData map[string]any) error {
|
||||
datapb, err := structpb.NewStruct(subscriptionData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := &agenda.SubscribeEventRequest{
|
||||
Eventid: eventID,
|
||||
Subscriber: subscriber,
|
||||
Data: datapb,
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.Agenda.SubscribeEvent(ctx, request)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) UnsubscribeFromAgendaEvent(ctx context.Context, eventID, subscribeID, motif, currentUserID, currentUserDisplayName, currentUserEmail, currentGroupID, currentGroupName string) error {
|
||||
// Get the event first
|
||||
request := &agenda.GetEventRequest{
|
||||
Id: eventID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvent(ctx, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find subscription data for the subscriber being removed
|
||||
var s_b_id, s_b_name, s_b_email, s_b_group_id, s_b_group_name string
|
||||
for i := range resp.Event.Subscriptions {
|
||||
if resp.Event.Subscriptions[i].Subscriber == subscribeID {
|
||||
s_b_id = resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["id"].GetStringValue()
|
||||
s_b_name = resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["display_name"].GetStringValue()
|
||||
s_b_email = resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["email"].GetStringValue()
|
||||
s_b_group_id = resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["id"].GetStringValue()
|
||||
s_b_group_name = resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["name"].GetStringValue()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"subscribed_by": map[string]any{
|
||||
"user": map[string]any{
|
||||
"id": s_b_id,
|
||||
"display_name": s_b_name,
|
||||
"email": s_b_email,
|
||||
},
|
||||
"group": map[string]any{
|
||||
"id": s_b_group_id,
|
||||
"name": s_b_group_name,
|
||||
},
|
||||
},
|
||||
"unsubscribed_by": map[string]any{
|
||||
"user": map[string]any{
|
||||
"id": currentUserID,
|
||||
"display_name": currentUserDisplayName,
|
||||
"email": currentUserEmail,
|
||||
},
|
||||
"group": map[string]any{
|
||||
"id": currentGroupID,
|
||||
"name": currentGroupName,
|
||||
},
|
||||
},
|
||||
"motif": motif,
|
||||
}
|
||||
|
||||
datapb, err := structpb.NewStruct(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteRequest := &agenda.DeleteSubscriptionRequest{
|
||||
Subscriber: subscribeID,
|
||||
Eventid: eventID,
|
||||
Data: datapb,
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.Agenda.DeleteSubscription(ctx, deleteRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send email notification
|
||||
emailData := map[string]any{
|
||||
"motif": motif,
|
||||
"user": currentUserDisplayName,
|
||||
"subscriber": fmt.Sprintf("http://localhost:9000/app/beneficiaries/%s", subscribeID),
|
||||
"link": fmt.Sprintf("http://localhost:9000/app/agenda/%s", eventID),
|
||||
}
|
||||
|
||||
if err := h.emailing.Send("delete_subscriber.request", s_b_email, emailData); err != nil {
|
||||
log.Error().Err(err).Msg("Cannot send email")
|
||||
// Don't return error for email failure
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AgendaEventHistoryResult struct {
|
||||
Event agendastorage.Event
|
||||
Group storage.Group
|
||||
Subscribers map[string]any
|
||||
Accounts []any
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetAgendaEventHistory(ctx context.Context, eventID string) (*AgendaEventHistoryResult, error) {
|
||||
request := &agenda.GetEventRequest{
|
||||
Id: eventID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvent(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grouprequest := &groupsmanagement.GetGroupRequest{
|
||||
Id: resp.Event.Owners[0],
|
||||
}
|
||||
|
||||
groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(ctx, grouprequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subscribers := map[string]any{}
|
||||
|
||||
accids := []string{}
|
||||
for _, v := range resp.Event.DeletedSubscription {
|
||||
accids = append(accids, v.Subscriber)
|
||||
}
|
||||
|
||||
if len(accids) > 0 {
|
||||
subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
|
||||
ctx,
|
||||
&mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: accids,
|
||||
},
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
for _, sub := range subscriberresp.Accounts {
|
||||
subscribers[sub.Id] = sub.ToStorageType()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return nil, fmt.Errorf("no group found in context")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
accountids := []string{}
|
||||
for _, m := range group.Members {
|
||||
if !contains(resp.Event.DeletedSubscription, m) {
|
||||
accountids = append(accountids, m)
|
||||
}
|
||||
}
|
||||
|
||||
accounts := []any{}
|
||||
if len(accountids) > 0 {
|
||||
accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
|
||||
ctx,
|
||||
&mobilityaccounts.GetAccountsBatchRequest{
|
||||
Accountids: accountids,
|
||||
},
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
for _, acc := range accountresp.Accounts {
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &AgendaEventHistoryResult{
|
||||
Event: resp.Event.ToStorageType(),
|
||||
Group: groupresp.Group.ToStorageType(),
|
||||
Subscribers: subscribers,
|
||||
Accounts: accounts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) AddEventDocument(ctx context.Context, eventID string, file io.Reader, filename string, fileSize int64, documentType, documentName string) error {
|
||||
fileid := uuid.NewString()
|
||||
|
||||
metadata := map[string]string{
|
||||
"type": documentType,
|
||||
"name": documentName,
|
||||
}
|
||||
|
||||
if err := h.filestorage.Put(file, filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s_%s", eventID, fileid, filename), fileSize, metadata); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GetEventDocument(ctx context.Context, eventID, document string) (io.Reader, *filestorage.FileInfo, error) {
|
||||
file, info, err := h.filestorage.Get(filestorage.PREFIX_AGENDA, fmt.Sprintf("%s/%s", eventID, document))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return file, info, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
func contains(s []*agenda.Subscription, e string) bool {
|
||||
for _, a := range s {
|
||||
if a.Subscriber == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) UpdateAgendaEvent(ctx context.Context, eventID, name, eventType, description string, address any, allday bool, startdate, enddate *time.Time, starttime, endtime string, maxSubscribers int) (string, error) {
|
||||
// Get current group
|
||||
g := ctx.Value(identification.GroupKey)
|
||||
if g == nil {
|
||||
return "", fmt.Errorf("no group found in context")
|
||||
}
|
||||
|
||||
group := g.(storage.Group)
|
||||
|
||||
// Get existing event first
|
||||
getRequest := &agenda.GetEventRequest{
|
||||
Id: eventID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvent(ctx, getRequest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
data, _ := structpb.NewStruct(map[string]any{
|
||||
"address": address,
|
||||
})
|
||||
|
||||
request := &agenda.UpdateEventRequest{
|
||||
Event: &agenda.Event{
|
||||
Namespace: "parcoursmob_dispositifs",
|
||||
Id: eventID,
|
||||
Owners: []string{group.ID},
|
||||
Type: eventType,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Startdate: timestamppb.New(*startdate),
|
||||
Enddate: timestamppb.New(*enddate),
|
||||
Starttime: starttime,
|
||||
Endtime: endtime,
|
||||
Allday: allday,
|
||||
MaxSubscribers: int64(maxSubscribers),
|
||||
Data: data,
|
||||
Subscriptions: resp.Event.Subscriptions,
|
||||
},
|
||||
}
|
||||
|
||||
updateResp, err := h.services.GRPC.Agenda.UpdateEvent(ctx, request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return updateResp.Event.Id, nil
|
||||
}
|
||||
|
||||
|
||||
func (h *ApplicationHandler) DeleteAgendaEvent(ctx context.Context, eventID string) error {
|
||||
request := &agenda.GetEventRequest{
|
||||
Id: eventID,
|
||||
}
|
||||
|
||||
resp, err := h.services.GRPC.Agenda.GetEvent(ctx, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updateRequest := &agenda.UpdateEventRequest{
|
||||
Event: &agenda.Event{
|
||||
Namespace: resp.Event.Namespace,
|
||||
Id: resp.Event.Id,
|
||||
Owners: resp.Event.Owners,
|
||||
Type: resp.Event.Type,
|
||||
Name: resp.Event.Name,
|
||||
Description: resp.Event.Description,
|
||||
Startdate: resp.Event.Startdate,
|
||||
Enddate: resp.Event.Enddate,
|
||||
Starttime: resp.Event.Starttime,
|
||||
Endtime: resp.Event.Endtime,
|
||||
Allday: resp.Event.Allday,
|
||||
MaxSubscribers: int64(resp.Event.MaxSubscribers),
|
||||
Data: resp.Event.Data,
|
||||
Subscriptions: resp.Event.Subscriptions,
|
||||
Deleted: true,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = h.services.GRPC.Agenda.UpdateEvent(ctx, updateRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
type CalendarResult struct {
|
||||
CalendarData string
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GenerateGlobalCalendar(ctx context.Context) (*CalendarResult, error) {
|
||||
events, err := h.services.GetAgendaEvents()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving agenda events")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calendar, err := h.icsCalendar(events)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CalendarResult{
|
||||
CalendarData: calendar.Serialize(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) GenerateOrganizationCalendar(ctx context.Context, groupID string) (*CalendarResult, error) {
|
||||
events, err := h.services.GetAgendaEvents()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error retrieving agenda events")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filteredEvents := []services.AgendaEvent{}
|
||||
for _, e := range events {
|
||||
for _, g := range e.Owners {
|
||||
if g == groupID {
|
||||
filteredEvents = append(filteredEvents, e)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calendar, err := h.icsCalendar(filteredEvents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CalendarResult{
|
||||
CalendarData: calendar.Serialize(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *ApplicationHandler) icsCalendar(events []services.AgendaEvent) (*ics.Calendar, error) {
|
||||
calendar := ics.NewCalendarFor(h.config.GetString("service_name"))
|
||||
|
||||
for _, e := range events {
|
||||
vevent := ics.NewEvent(e.ID)
|
||||
vevent.SetSummary(e.Name)
|
||||
vevent.SetDescription(e.Description)
|
||||
if e.Allday {
|
||||
vevent.SetAllDayStartAt(e.Startdate)
|
||||
if e.Enddate.After(e.Startdate) {
|
||||
vevent.SetAllDayEndAt(e.Enddate.Add(24 * time.Hour))
|
||||
}
|
||||
} else {
|
||||
timeloc, err := time.LoadLocation("Europe/Paris")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Tried to load timezone location Europe/Paris. Error. Missing zones in container ?")
|
||||
return nil, err
|
||||
}
|
||||
vevent.SetStartAt(e.Startdate.In(timeloc))
|
||||
vevent.SetEndAt(e.Enddate.In(timeloc))
|
||||
}
|
||||
calendar.AddVEvent(vevent)
|
||||
}
|
||||
|
||||
return calendar, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user