Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/src/models/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type CompanyParticipation struct {
// Some random notes about this participation.
Notes string `json:"notes" bson:"notes"`

// GmailThreadIds is an array of Gmail thread IDs linked to this participation.
// These are used to sync communications with Gmail.
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`

// Stand details
StandDetails StandDetails `json:"standDetails,omitempty" bson:"standDetails,omitempty"`

Expand Down
4 changes: 4 additions & 0 deletions backend/src/models/speaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type SpeakerParticipation struct {

// Hotel information regarding this speaker.
Room SpeakerParticipationRoom `json:"room" bson:"room"`

// GmailThreadIds is an array of Gmail thread IDs linked to this participation.
// These are used to sync communications with Gmail.
GmailThreadIds []string `json:"gmailThreadIds,omitempty" bson:"gmailThreadIds,omitempty"`
}

type SpeakerImages struct {
Expand Down
4 changes: 4 additions & 0 deletions backend/src/models/thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ type Thread struct {
// REVIEWED => thread is posted, but some changed must be made before it's ready to be approved.
// PENDING => thread is posted and is waiting for the coordination's approval/review.
Status ThreadStatus `json:"status" bson:"status"`

// GmailMessageId is the ID of the Gmail message this thread was synced from.
// This is used to prevent duplicate syncs.
GmailMessageId string `json:"gmailMessageId,omitempty" bson:"gmailMessageId,omitempty"`
}

type ThreadWithEntry struct {
Expand Down
29 changes: 29 additions & 0 deletions backend/src/mongodb/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,35 @@ func (c *CompaniesType) DeleteCompanyThread(id, threadID primitive.ObjectID) (*m
return &updatedCompany, nil
}

// UpdateCompanyGmailThreadIds updates the gmail thread IDs for a company's current participation
func (c *CompaniesType) UpdateCompanyGmailThreadIds(companyID primitive.ObjectID, gmailThreadIds []string) (*models.Company, error) {
ctx := context.Background()
currentEvent, err := Events.GetCurrentEvent()
if err != nil {
return nil, err
}

var updatedCompany models.Company

var updateQuery = bson.M{
"$set": bson.M{
"participations.$.gmailThreadIds": gmailThreadIds,
},
}

var filterQuery = bson.M{"_id": companyID, "participations.event": currentEvent.ID}

var optionsQuery = options.FindOneAndUpdate()
optionsQuery.SetReturnDocument(options.After)

if err := c.Collection.FindOneAndUpdate(ctx, filterQuery, updateQuery, optionsQuery).Decode(&updatedCompany); err != nil {
log.Println("Error updating company gmail thread IDs:", err)
return nil, err
}

return &updatedCompany, nil
}

// UpdateCompanyParticipationStatus updates a company's participation status
// related to the current event. This is the method used when one does not want necessarily to follow
// the state machine described on models.ParticipationStatus.
Expand Down
29 changes: 29 additions & 0 deletions backend/src/mongodb/speaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,35 @@ func (s *SpeakersType) GetSpeakerParticipationStatusValidSteps(speakerID primiti
return nil, errors.New("No participation found")
}

// UpdateSpeakerGmailThreadIds updates the gmail thread IDs for a speaker's current participation
func (s *SpeakersType) UpdateSpeakerGmailThreadIds(speakerID primitive.ObjectID, gmailThreadIds []string) (*models.Speaker, error) {
ctx := context.Background()
currentEvent, err := Events.GetCurrentEvent()
if err != nil {
return nil, err
}

var updatedSpeaker models.Speaker

var updateQuery = bson.M{
"$set": bson.M{
"participations.$.gmailThreadIds": gmailThreadIds,
},
}

var filterQuery = bson.M{"_id": speakerID, "participations.event": currentEvent.ID}

var optionsQuery = options.FindOneAndUpdate()
optionsQuery.SetReturnDocument(options.After)

if err := s.Collection.FindOneAndUpdate(ctx, filterQuery, updateQuery, optionsQuery).Decode(&updatedSpeaker); err != nil {
log.Println("Error updating speaker gmail thread IDs:", err)
return nil, err
}

return &updatedSpeaker, nil
}

// UpdateSpeakerParticipationStatus updates a speaker's participation status
// related to the current event. This is the method used when one does not want necessarily to follow
// the state machine described on models.ParticipationStatus.
Expand Down
61 changes: 57 additions & 4 deletions backend/src/mongodb/thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ type ThreadsType struct {

// CreateThreadData holds data needed to create a thread
type CreateThreadData struct {
Entry primitive.ObjectID
Meeting *primitive.ObjectID
Kind models.ThreadKind
Entry primitive.ObjectID
Meeting *primitive.ObjectID
Kind models.ThreadKind
GmailMessageId string
Posted *time.Time
}

type UpdateThreadData struct {
Expand Down Expand Up @@ -55,18 +57,27 @@ func (utd *UpdateThreadData) ParseBody(body io.Reader) error {
func (t *ThreadsType) CreateThread(data CreateThreadData) (*models.Thread, error) {
ctx := context.Background()

postedTime := time.Now().UTC()
if data.Posted != nil {
postedTime = *data.Posted
}

query := bson.M{
"entry": data.Entry,
"comments": []primitive.ObjectID{},
"status": models.ThreadStatusPending,
"kind": data.Kind,
"posted": time.Now().UTC(),
"posted": postedTime,
}

if data.Meeting != nil {
query["meeting"] = *data.Meeting
}

if data.GmailMessageId != "" {
query["gmailMessageId"] = data.GmailMessageId
}

insertResult, err := t.Collection.InsertOne(ctx, query)

if err != nil {
Expand Down Expand Up @@ -96,6 +107,48 @@ func (t *ThreadsType) GetThread(threadID primitive.ObjectID) (*models.Thread, er
return &thread, nil
}

// GetThreadByGmailMessageId finds a thread by its Gmail message ID.
func (t *ThreadsType) GetThreadByGmailMessageId(gmailMessageId string) (*models.Thread, error) {
ctx := context.Background()
var thread models.Thread

err := t.Collection.FindOne(ctx, bson.M{"gmailMessageId": gmailMessageId}).Decode(&thread)
if err != nil {
return nil, err
}

return &thread, nil
}

// GetThreadsByGmailMessageIds finds threads by multiple Gmail message IDs.
// Returns a map of gmailMessageId -> thread for quick lookup.
func (t *ThreadsType) GetThreadsByGmailMessageIds(gmailMessageIds []string) (map[string]*models.Thread, error) {
ctx := context.Background()
result := make(map[string]*models.Thread)

if len(gmailMessageIds) == 0 {
return result, nil
}

cursor, err := t.Collection.Find(ctx, bson.M{
"gmailMessageId": bson.M{"$in": gmailMessageIds},
})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)

for cursor.Next(ctx) {
var thread models.Thread
if err := cursor.Decode(&thread); err != nil {
continue
}
result[thread.GmailMessageId] = &thread
}

return result, cursor.Err()
}

// DeleteThread deletes a thread by its ID and cleans up related data.
func (t *ThreadsType) DeleteThread(threadID primitive.ObjectID) (*models.Thread, error) {
ctx := context.Background()
Expand Down
Loading