MM-45899: Insights: least active channels (#20796)

* Add api endpoints, app layers for top inactive channels with dummy store calls

* Add store functions for top inactive channels

* Add model, store, app tests.

* Add client function and api tests

* Add participants information to TopInactiveChannel

* Translation fix

* Style fix while writing response

* Return channelmember IDs instead of profiles, query in batch avoiding inside the loop

* Make the following changes

 - move DeleteAt to subqueries, to avoid select, group by
 - Remove TeamId from response
 - Count bots and webhook posts

* SQL query lint fix, store test fix to include bot messages

* make app-layers

* Fix empty participant lists being sent as [""]

* Track channel joins, to distinguish 0 activity channels vs new channels

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Shivashis Padhi
2022-08-24 23:14:56 +05:30
коммит произвёл GitHub
родитель 8fd1762c3b
Коммит 6adbcc5d05
17 изменённых файлов: 1041 добавлений и 0 удалений

Просмотреть файл

@@ -3642,6 +3642,41 @@ func (c *Client4) GetTopChannelsForUserSince(teamId string, timeRange string, pa
return topChannels, BuildResponse(r), nil
}
// GetTopInactiveChannelsForTeamSince will return an ordered list of the top channels in a given team.
func (c *Client4) GetTopInactiveChannelsForTeamSince(teamId string, timeRange string, page int, perPage int) (*TopInactiveChannelList, *Response, error) {
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
r, err := c.DoAPIGet(c.teamRoute(teamId)+"/top/inactive_channels"+query, "")
if err != nil {
return nil, BuildResponse(r), err
}
defer closeBody(r)
var topInactiveChannels *TopInactiveChannelList
if jsonErr := json.NewDecoder(r.Body).Decode(&topInactiveChannels); jsonErr != nil {
return nil, nil, NewAppError("GetTopInactiveChannelsForTeamSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
}
return topInactiveChannels, BuildResponse(r), nil
}
// GetTopInactiveChannelsForUserSince will return an ordered list of your top channels in a given team.
func (c *Client4) GetTopInactiveChannelsForUserSince(teamId string, timeRange string, page int, perPage int) (*TopInactiveChannelList, *Response, error) {
query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage)
if teamId != "" {
query += fmt.Sprintf("&team_id=%v", teamId)
}
r, err := c.DoAPIGet(c.usersRoute()+"/me/top/inactive_channels"+query, "")
if err != nil {
return nil, BuildResponse(r), err
}
defer closeBody(r)
var topInactiveChannels *TopInactiveChannelList
if jsonErr := json.NewDecoder(r.Body).Decode(&topInactiveChannels); jsonErr != nil {
return nil, nil, NewAppError("GetTopInactiveChannelsForUserSince", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
}
return topInactiveChannels, BuildResponse(r), nil
}
// Post Section
// CreatePost creates a post based on the provided post struct.

Просмотреть файл

@@ -63,6 +63,22 @@ type TopChannel struct {
MessageCount int64 `json:"message_count"`
}
// Top Channels
type TopInactiveChannelList struct {
InsightsListData
Items []*TopInactiveChannel `json:"items"`
}
type TopInactiveChannel struct {
ID string `json:"id"`
Type ChannelType `json:"type"`
DisplayName string `json:"display_name"`
Name string `json:"name"`
LastActivityAt int64 `json:"last_activity_at"`
Participants StringArray `json:"participants"`
MessageCount int64 `json:"-"`
}
// Top Threads
type TopThreadList struct {
InsightsListData
@@ -286,6 +302,20 @@ func GetTopThreadListWithPagination(threads []*TopThread, limit int) *TopThreadL
return &TopThreadList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: threads}
}
// GetTopInactiveChannelListWithPagination adds a rank to each item in the given list of TopInactiveChannel and checks if there is
// another page that can be fetched based on the given limit and offset. The given list of TopInactiveChannel is assumed to be
// sorted by Score. Returns a TopInactiveChannelList.
func GetTopInactiveChannelListWithPagination(channels []*TopInactiveChannel, limit int) *TopInactiveChannelList {
// Add pagination support
var hasNext bool
if (limit != 0) && (len(channels) == limit+1) {
hasNext = true
channels = channels[:len(channels)-1]
}
return &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: channels}
}
// GetTopDMListWithPagination adds a rank to each item in the given list of TopDM and checks if there is
// another page that can be fetched based on the given limit and offset. The given list of TopDM is assumed to be
// sorted by MessageCount(score). Returns a TopDMList.

Просмотреть файл

@@ -121,6 +121,43 @@ func TestGetTopThreadListWithPagination(t *testing.T) {
}
}
func TestGetTopInactiveChannelListWithPagination(t *testing.T) {
channels := []*TopInactiveChannel{
{ID: NewId(), MessageCount: 2},
{ID: NewId(), MessageCount: 5},
{ID: NewId(), MessageCount: 7},
{ID: NewId(), MessageCount: 80},
{ID: NewId(), MessageCount: 85},
{ID: NewId(), MessageCount: 92}}
hasNextTC := []struct {
Description string
Limit int
Offset int
Expected *TopInactiveChannelList
}{
{
Description: "has one page",
Limit: len(channels),
Offset: 0,
Expected: &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: false}, Items: channels},
},
{
Description: "has more than one page",
Limit: len(channels) - 1,
Offset: 0,
Expected: &TopInactiveChannelList{InsightsListData: InsightsListData{HasNext: true}, Items: channels},
},
}
for _, test := range hasNextTC {
t.Run(test.Description, func(t *testing.T) {
actual := GetTopInactiveChannelListWithPagination(channels, test.Limit)
assert.Equal(t, test.Expected.HasNext, actual.HasNext)
})
}
}
func TestGetTopDMsListWithPagination(t *testing.T) {
dms := []*TopDM{
{SecondParticipant: &TopDMInsightUserInformation{InsightUserInformation: InsightUserInformation{Id: NewId()}}, MessageCount: 100},