generated from mattermost/mattermost-plugin-starter-template
-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MM-228] Add feature to update custom status and status of the user during meeting #359
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
ed87b58
[MM-188] Add feature to update custom status and status of the user d…
ayusht2810 b696601
[MM-188] Add test case for custom status change
ayusht2810 d1c271b
Fix test cases
raghavaggarwal2308 a9c4531
[MM-188] Review fixes: Update constant name, add some comments and re…
ayusht2810 10d6649
[MM-252] Review fixes
raghavaggarwal2308 3c2ed2b
[MM-250] Review fixes
raghavaggarwal2308 56441cc
fix link error
raghavaggarwal2308 8fd8412
Fix failing test
raghavaggarwal2308 642b212
[MM-256] Added test cases for back-to-back event and non overlapping …
raghavaggarwal2308 59f5951
Merge branch 'master' into MM-188-fix
fmartingr 85fe326
Merge branch 'master' into MM-188-fix
raghavaggarwal2308 ef5152e
Merge branch 'master' into MM-188-fix
hanzei 67c24e7
Merge branch 'master' into MM-188-fix
raghavaggarwal2308 dbd3e5d
[MM-188] Fix review fixes: Use legacy settings, update and add new te…
ayusht2810 14eddb0
[MM-188] Update case to filter events on attendee as well
ayusht2810 8f503b1
Merge branch 'master' into MM-188-fix
ayusht2810 0ad2bb8
Merge branch 'master' into MM-188-fix
wiggin77 03b1d25
Merge branch 'master' into MM-188-fix
raghavaggarwal2308 5b08060
[MM-188] Add comments to the code
ayusht2810 c7441e0
Merge branch 'master' into MM-188-fix
wiggin77 f287370
Merge branch 'master' into MM-188-fix
raghavaggarwal2308 b851b21
Merge branch 'master' into MM-188-fix
raghavaggarwal2308 716ebcc
Merge branch 'master' of github.com:mattermost/mattermost-plugin-msca…
raghavaggarwal2308 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,7 @@ GO_BUILD_FLAGS = -ldflags '$(LDFLAGS)' | |
mock: | ||
ifneq ($(HAS_SERVER),) | ||
go install github.com/golang/mock/[email protected] | ||
mockgen -destination calendar/jobs/mock_cluster/mock_cluster.go github.com/mattermost/mattermost-plugin-api/cluster JobPluginAPI | ||
mockgen -destination calendar/jobs/mock_cluster/mock_cluster.go github.com/mattermost/mattermost/server/public/pluginapi/cluster JobPluginAPI | ||
mockgen -destination calendar/engine/mock_engine/mock_engine.go $(REPOSITORY_URL)/calendar/engine Engine | ||
mockgen -destination calendar/engine/mock_welcomer/mock_welcomer.go -package mock_welcomer $(REPOSITORY_URL)/calendar/engine Welcomer | ||
mockgen -destination calendar/engine/mock_plugin_api/mock_plugin_api.go -package mock_plugin_api $(REPOSITORY_URL)/calendar/engine PluginAPI | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ package engine | |
|
||
import ( | ||
"fmt" | ||
"sort" | ||
"time" | ||
|
||
"github.com/mattermost/mattermost/server/public/model" | ||
|
@@ -109,7 +110,7 @@ func (m *mscalendar) retrieveUsersToSync(userIndex store.UserIndex, syncJobSumma | |
} | ||
|
||
// If user does not have the proper features enabled, just go to the next one | ||
if !(user.Settings.UpdateStatus || user.Settings.ReceiveReminders) { | ||
if !(user.IsConfiguredForStatusUpdates() || user.IsConfiguredForCustomStatusUpdates() || user.Settings.ReceiveReminders) { | ||
continue | ||
} | ||
|
||
|
@@ -153,6 +154,14 @@ func (m *mscalendar) retrieveUsersToSync(userIndex store.UserIndex, syncJobSumma | |
return users, calendarViews, fmt.Errorf("no calendar views found") | ||
} | ||
|
||
// Sort events for all fetched calendar views | ||
for _, view := range calendarViews { | ||
ayusht2810 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
events := view.Events | ||
sort.Slice(events, func(i, j int) bool { | ||
return events[i].Start.Time().UnixMicro() < events[j].Start.Time().UnixMicro() | ||
}) | ||
} | ||
|
||
return users, calendarViews, nil | ||
} | ||
|
||
|
@@ -230,7 +239,7 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot | |
numberOfLogs, numberOfUserStatusChange, numberOfUserErrorInStatusChange := 0, 0, 0 | ||
toUpdate := []*store.User{} | ||
for _, u := range users { | ||
if u.Settings.UpdateStatus { | ||
if u.IsConfiguredForStatusUpdates() || u.IsConfiguredForCustomStatusUpdates() { | ||
toUpdate = append(toUpdate, u) | ||
} | ||
} | ||
|
@@ -278,38 +287,112 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot | |
continue | ||
} | ||
|
||
events := filterBusyAndAttendeeEvents(view.Events) | ||
events = getMergedEvents(events) | ||
|
||
var err error | ||
res, isStatusChanged, err = m.setStatusFromCalendarView(user, status, view) | ||
if err != nil { | ||
if numberOfLogs < logTruncateLimit { | ||
m.Logger.Warnf("Error setting user %s status. err=%v", user.MattermostUserID, err) | ||
} else if numberOfLogs == logTruncateLimit { | ||
m.Logger.Warnf(logTruncateMsg) | ||
if user.IsConfiguredForStatusUpdates() { | ||
res, isStatusChanged, err = m.setStatusFromCalendarView(user, status, events) | ||
if err != nil { | ||
if numberOfLogs < logTruncateLimit { | ||
m.Logger.Warnf("Error setting user %s status. err=%v", user.MattermostUserID, err) | ||
} else if numberOfLogs == logTruncateLimit { | ||
m.Logger.Warnf(logTruncateMsg) | ||
} | ||
numberOfLogs++ | ||
numberOfUserErrorInStatusChange++ | ||
} | ||
if isStatusChanged { | ||
numberOfUserStatusChange++ | ||
} | ||
numberOfLogs++ | ||
numberOfUserErrorInStatusChange++ | ||
} | ||
if isStatusChanged { | ||
numberOfUserStatusChange++ | ||
|
||
if user.IsConfiguredForCustomStatusUpdates() { | ||
res, isStatusChanged, err = m.setCustomStatusFromCalendarView(user, events) | ||
if err != nil { | ||
if numberOfLogs < logTruncateLimit { | ||
m.Logger.Warnf("Error setting user %s custom status. err=%v", user.MattermostUserID, err) | ||
} else if numberOfLogs == logTruncateLimit { | ||
m.Logger.Warnf(logTruncateMsg) | ||
} | ||
numberOfLogs++ | ||
numberOfUserErrorInStatusChange++ | ||
} | ||
|
||
// Increment count only when we have not updated the status of the user from the options to have status change count per user. | ||
if isStatusChanged && user.Settings.UpdateStatusFromOptions == store.NotSetStatusOption { | ||
numberOfUserStatusChange++ | ||
} | ||
} | ||
} | ||
|
||
if res != "" { | ||
return res, numberOfUserStatusChange, numberOfUserErrorInStatusChange, nil | ||
} | ||
|
||
return utils.JSONBlock(calendarViews), numberOfUserStatusChange, numberOfUserErrorInStatusChange, nil | ||
} | ||
|
||
func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.Status, res *remote.ViewCalendarResponse) (string, bool, error) { | ||
func (m *mscalendar) setCustomStatusFromCalendarView(user *store.User, events []*remote.Event) (string, bool, error) { | ||
isStatusChanged := false | ||
if !user.IsConfiguredForCustomStatusUpdates() { | ||
return "User doesn't want to set custom status", isStatusChanged, nil | ||
} | ||
|
||
if len(events) == 0 { | ||
if user.IsCustomStatusSet { | ||
if err := m.PluginAPI.RemoveMattermostUserCustomStatus(user.MattermostUserID); err != nil { | ||
m.Logger.Warnf("Error removing user %s custom status. err=%v", user.MattermostUserID, err) | ||
} | ||
|
||
if err := m.Store.StoreUserCustomStatusUpdates(user.MattermostUserID, false); err != nil { | ||
return "", isStatusChanged, err | ||
} | ||
} | ||
|
||
return "No event present to set custom status", isStatusChanged, nil | ||
} | ||
|
||
currentUser, err := m.PluginAPI.GetMattermostUser(user.MattermostUserID) | ||
if err != nil { | ||
return "", isStatusChanged, err | ||
} | ||
|
||
currentCustomStatus := currentUser.GetCustomStatus() | ||
if currentCustomStatus != nil && !user.IsCustomStatusSet { | ||
return "User already has a custom status set, ignoring custom status change", isStatusChanged, nil | ||
} | ||
|
||
if appErr := m.PluginAPI.UpdateMattermostUserCustomStatus(user.MattermostUserID, &model.CustomStatus{ | ||
Emoji: "calendar", | ||
Text: "In a meeting", | ||
ExpiresAt: events[0].End.Time(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is an event at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the flow now |
||
Duration: "date_and_time", | ||
}); appErr != nil { | ||
return "", isStatusChanged, appErr | ||
} | ||
|
||
isStatusChanged = true | ||
if err := m.Store.StoreUserCustomStatusUpdates(user.MattermostUserID, true); err != nil { | ||
return "", isStatusChanged, err | ||
} | ||
|
||
return "", isStatusChanged, nil | ||
} | ||
|
||
func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.Status, events []*remote.Event) (string, bool, error) { | ||
isStatusChanged := false | ||
currentStatus := status.Status | ||
if !user.IsConfiguredForStatusUpdates() { | ||
return "No value set from options to update status", isStatusChanged, nil | ||
} | ||
|
||
if currentStatus == model.StatusOffline && !user.Settings.GetConfirmation { | ||
return "User offline and does not want status change confirmations. No status change", isStatusChanged, nil | ||
} | ||
|
||
events := filterBusyEvents(res.Events) | ||
busyStatus := model.StatusDnd | ||
if user.Settings.ReceiveNotificationsDuringMeeting { | ||
if user.Settings.UpdateStatusFromOptions == store.AwayStatusOption { | ||
busyStatus = model.StatusAway | ||
} | ||
|
||
|
@@ -424,7 +507,7 @@ func (m *mscalendar) setStatusOrAskUser(user *store.User, currentStatus *model.S | |
|
||
if !isFree { | ||
toSet = model.StatusDnd | ||
if user.Settings.ReceiveNotificationsDuringMeeting { | ||
if user.Settings.UpdateStatusFromOptions == store.AwayStatusOption { | ||
toSet = model.StatusAway | ||
} | ||
if !user.Settings.GetConfirmation { | ||
|
@@ -563,12 +646,46 @@ func (m *mscalendar) notifyUpcomingEvents(mattermostUserID string, events []*rem | |
} | ||
} | ||
|
||
func filterBusyEvents(events []*remote.Event) []*remote.Event { | ||
func filterBusyAndAttendeeEvents(events []*remote.Event) []*remote.Event { | ||
result := []*remote.Event{} | ||
for _, e := range events { | ||
if e.ShowAs == "busy" { | ||
// Not setting custom status for events without attendees since those are unlikely to be meetings. | ||
if e.ShowAs == "busy" && !e.IsCancelled && len(e.Attendees) >= 1 { | ||
result = append(result, e) | ||
} | ||
} | ||
return result | ||
} | ||
|
||
// getMergedEvents accepts a sorted array of events, and returns events after merging them, if overlapping or if the meeting duration is less than StatusSyncJobInterval. | ||
func getMergedEvents(events []*remote.Event) []*remote.Event { | ||
if len(events) <= 1 { | ||
return events | ||
} | ||
|
||
idx := 0 | ||
for i := 1; i < len(events); i++ { | ||
if areEventsMergeable(events[idx], events[i]) { | ||
events[idx].End = events[i].End | ||
} else { | ||
idx++ | ||
events[idx] = events[i] | ||
} | ||
} | ||
|
||
return events[0 : idx+1] | ||
} | ||
|
||
/* | ||
areEventsMergeable function checks if two events can be merged into a single event. | ||
There are two conditions that are being checked in the function: | ||
- If two events overlap, the end time of event1 will be | ||
greater than or equal to event2 and we can merge those events into a single event. | ||
For e.g.- event1: 1:01–1:04, event2: 1:03–1:05. Final event: 1:01–1:05. | ||
- If the difference between event1 end time and event1 start time isor equal to StatusSyncJobInterval and the difference between event2 start time and event1 end time is less than or equal to StatusSyncJobInterval. This is done to merge those events that occur within the time span of StatusSyncJobInterval. | ||
For e.g.- event1: 1:01–1:02, event2: 1:03–1:05, StatusSyncJobInterval: 5 mins. Final event: 1:01–1:05. | ||
This is done to avoid skipping of event2 as both events are fetched together in a single API call when the job runs every 5 minutes. | ||
*/ | ||
func areEventsMergeable(event1, event2 *remote.Event) bool { | ||
return (event1.End.Time().UnixMicro() >= event2.Start.Time().UnixMicro()) || (event1.End.Time().Sub(event1.Start.Time()) <= StatusSyncJobInterval && event2.Start.Time().Sub(event1.End.Time()) <= StatusSyncJobInterval) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would expect these methods to be defined on
user.Settings
, though I don't think it matters. This current way allows for less typing throughout the usages of the method