https://mattermost.atlassian.net/browse/MM-52079 ```release-note We upgrade the module version to 8.0. The new module path is github.com/mattermost-server/server/v8. ``` Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
364 строки
11 KiB
Go
364 строки
11 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type PostCountGrouping string
|
|
|
|
const (
|
|
TimeRangeToday string = "today"
|
|
TimeRange7Day string = "7_day"
|
|
TimeRange28Day string = "28_day"
|
|
|
|
PostsByHour PostCountGrouping = "hour"
|
|
PostsByDay PostCountGrouping = "day"
|
|
)
|
|
|
|
type InsightsOpts struct {
|
|
StartUnixMilli int64
|
|
Page int
|
|
PerPage int
|
|
}
|
|
|
|
type InsightsListData struct {
|
|
HasNext bool `json:"has_next"`
|
|
}
|
|
|
|
// Top Reactions
|
|
type TopReactionList struct {
|
|
InsightsListData
|
|
Items []*TopReaction `json:"items"`
|
|
}
|
|
|
|
type TopReaction struct {
|
|
EmojiName string `json:"emoji_name"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
|
|
// Top Channels
|
|
type TopChannelList struct {
|
|
InsightsListData
|
|
Items []*TopChannel `json:"items"`
|
|
PostCountByDuration ChannelPostCountByDuration `json:"channel_post_counts_by_duration"`
|
|
}
|
|
|
|
func (t *TopChannelList) ChannelIDs() []string {
|
|
var ids []string
|
|
for _, item := range t.Items {
|
|
ids = append(ids, item.ID)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
type TopChannel struct {
|
|
ID string `json:"id"`
|
|
Type ChannelType `json:"type"`
|
|
DisplayName string `json:"display_name"`
|
|
Name string `json:"name"`
|
|
TeamID string `json:"team_id"`
|
|
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
|
|
Items []*TopThread `json:"items"`
|
|
}
|
|
|
|
type TopThread struct {
|
|
PostId string `json:"-"`
|
|
ReplyCount int64 `json:"-"`
|
|
ChannelId string `json:"channel_id"`
|
|
DisplayName string `json:"channel_display_name"`
|
|
Name string `json:"channel_name"`
|
|
Participants StringArray `json:"participants"`
|
|
UserId string `json:"-"`
|
|
UserInformation *InsightUserInformation `json:"user_information"`
|
|
Post *Post `json:"post"`
|
|
}
|
|
|
|
type InsightUserInformation struct {
|
|
Id string `json:"id"`
|
|
LastPictureUpdate int64 `json:"last_picture_update"`
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
NickName string `json:"nickname"`
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
type TopDMInsightUserInformation struct {
|
|
InsightUserInformation
|
|
Position string `json:"position"`
|
|
}
|
|
type NewTeamMembersList struct {
|
|
InsightsListData
|
|
Items []*NewTeamMember `json:"items"`
|
|
TotalCount int64 `json:"total_count"`
|
|
}
|
|
|
|
type NewTeamMember struct {
|
|
Id string `json:"id"`
|
|
Username string `json:"username"`
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
Position string `json:"position"`
|
|
Nickname string `json:"nickname"`
|
|
LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
|
|
CreateAt int64 `json:"create_at"`
|
|
}
|
|
|
|
type DurationPostCount struct {
|
|
ChannelID string `db:"channelid"`
|
|
// Duration is an ISO8601 date string.
|
|
Duration string `db:"duration"`
|
|
PostCount int `db:"postcount"`
|
|
}
|
|
|
|
// Top DMs
|
|
type TopDM struct {
|
|
MessageCount int64 `json:"post_count"`
|
|
OutgoingMessageCount int64 `json:"outgoing_message_count"`
|
|
Participants string `json:"-"`
|
|
ChannelId string `json:"-"`
|
|
SecondParticipant *TopDMInsightUserInformation `json:"second_participant"`
|
|
}
|
|
|
|
type OutgoingMessageQueryResult struct {
|
|
ChannelId string
|
|
MessageCount int
|
|
}
|
|
|
|
type TopDMList struct {
|
|
InsightsListData
|
|
Items []*TopDM `json:"items"`
|
|
}
|
|
|
|
func TimeRangeToNumberDays(timeRange string) int {
|
|
var n int
|
|
switch timeRange {
|
|
case TimeRangeToday:
|
|
n = 1
|
|
case TimeRange7Day:
|
|
n = 7
|
|
case TimeRange28Day:
|
|
n = 28
|
|
}
|
|
return n
|
|
}
|
|
|
|
// ChannelPostCountByDuration contains a count of posts by channel id, grouped by ISO8601 date string.
|
|
// Example 1 (grouped by day):
|
|
//
|
|
// cpc := model.ChannelPostCountByDuration{
|
|
// "2009-11-11": {
|
|
// "ezbp7nqxzjgdir8riodyafr9ww": 90,
|
|
// "p949c1xdojfgzffxma3p3s3ikr": 201,
|
|
// },
|
|
// "2009-11-12": {
|
|
// "ezbp7nqxzjgdir8riodyafr9ww": 45,
|
|
// "p949c1xdojfgzffxma3p3s3ikr": 68,
|
|
// },
|
|
// }
|
|
//
|
|
// Example 2 (grouped by hour):
|
|
//
|
|
// cpc := model.ChannelPostCountByDuration{
|
|
// "2009-11-11T01": {
|
|
// "ezbp7nqxzjgdir8riodyafr9ww": 90,
|
|
// "p949c1xdojfgzffxma3p3s3ikr": 201,
|
|
// },
|
|
// "2009-11-11T02": {
|
|
// "ezbp7nqxzjgdir8riodyafr9ww": 45,
|
|
// "p949c1xdojfgzffxma3p3s3ikr": 68,
|
|
// },
|
|
// }
|
|
type ChannelPostCountByDuration map[string]map[string]int
|
|
|
|
func blankChannelCountsMap(channelIDs []string) map[string]int {
|
|
blankChannelCounts := map[string]int{}
|
|
for _, id := range channelIDs {
|
|
blankChannelCounts[id] = 0
|
|
}
|
|
return blankChannelCounts
|
|
}
|
|
|
|
func ToDailyPostCountViewModel(dpc []*DurationPostCount, startTime *time.Time, numDays int, channelIDs []string) ChannelPostCountByDuration {
|
|
viewModel := ChannelPostCountByDuration{}
|
|
|
|
keyTime := *startTime
|
|
nowAtLocation := time.Now().In(startTime.Location())
|
|
|
|
if numDays == 1 {
|
|
for keyTime.Before(nowAtLocation) {
|
|
dateTimeKey := keyTime.Format(time.RFC3339)
|
|
viewModel[dateTimeKey] = blankChannelCountsMap(channelIDs)
|
|
keyTime = keyTime.Add(time.Hour)
|
|
}
|
|
} else {
|
|
for keyTime.Before(nowAtLocation) {
|
|
dateTimeKey := keyTime.Format("2006-01-02")
|
|
viewModel[dateTimeKey] = blankChannelCountsMap(channelIDs)
|
|
keyTime = keyTime.Add(24 * time.Hour)
|
|
}
|
|
}
|
|
|
|
for _, item := range dpc {
|
|
var parseFormat string
|
|
var keyFormat string
|
|
if numDays == 1 {
|
|
parseFormat = "2006-01-02T15 "
|
|
keyFormat = time.RFC3339
|
|
} else {
|
|
parseFormat = "2006-01-02"
|
|
keyFormat = parseFormat
|
|
}
|
|
durTime, err := time.ParseInLocation(parseFormat, item.Duration, startTime.Location())
|
|
if err != nil {
|
|
continue
|
|
}
|
|
localizedKey := durTime.Format(keyFormat)
|
|
_, hasKey := viewModel[localizedKey]
|
|
if !hasKey {
|
|
viewModel[localizedKey] = map[string]int{}
|
|
}
|
|
viewModel[localizedKey][item.ChannelID] = item.PostCount
|
|
}
|
|
|
|
return viewModel
|
|
}
|
|
|
|
// Deprecated: This method doesn't perform error checking.
|
|
// Use GetStartOfDayForTimeRange instead.
|
|
//
|
|
// StartOfDayForTimeRange gets the unix start time in milliseconds from the given time range.
|
|
// Time range can be one of: "today", "7_day", or "28_day".
|
|
func StartOfDayForTimeRange(timeRange string, location *time.Location) *time.Time {
|
|
now := time.Now().In(location)
|
|
resultTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, location)
|
|
switch timeRange {
|
|
case TimeRange7Day:
|
|
resultTime = resultTime.Add(time.Hour * time.Duration(-144))
|
|
case TimeRange28Day:
|
|
resultTime = resultTime.Add(time.Hour * time.Duration(-648))
|
|
}
|
|
return &resultTime
|
|
}
|
|
|
|
// GetStartOfDayForTimeRange gets the unix start time in milliseconds from the given time range.
|
|
// Time range can be one of: "today", "7_day", or "28_day".
|
|
func GetStartOfDayForTimeRange(timeRange string, location *time.Location) (*time.Time, *AppError) {
|
|
now := time.Now().In(location)
|
|
resultTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, location)
|
|
switch timeRange {
|
|
case TimeRangeToday:
|
|
case TimeRange7Day:
|
|
resultTime = resultTime.Add(time.Hour * time.Duration(-144))
|
|
case TimeRange28Day:
|
|
resultTime = resultTime.Add(time.Hour * time.Duration(-648))
|
|
default:
|
|
return nil, NewAppError("GetStartOfDayForTimeRange", "model.insights.get_start_of_day_for_time_range.time_range.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
return &resultTime, nil
|
|
}
|
|
|
|
// GetTopReactionListWithPagination adds a rank to each item in the given list of TopReaction and checks if there is
|
|
// another page that can be fetched based on the given limit and offset. The given list of TopReaction is assumed to be
|
|
// sorted by Count. Returns a TopReactionList.
|
|
func GetTopReactionListWithPagination(reactions []*TopReaction, limit int) *TopReactionList {
|
|
// Add pagination support
|
|
var hasNext bool
|
|
if (limit != 0) && (len(reactions) == limit+1) {
|
|
hasNext = true
|
|
reactions = reactions[:len(reactions)-1]
|
|
}
|
|
|
|
return &TopReactionList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: reactions}
|
|
}
|
|
|
|
// GetTopChannelListWithPagination adds a rank to each item in the given list of TopChannel and checks if there is
|
|
// another page that can be fetched based on the given limit and offset. The given list of TopChannel is assumed to be
|
|
// sorted by Score. Returns a TopChannelList.
|
|
func GetTopChannelListWithPagination(channels []*TopChannel, limit int) *TopChannelList {
|
|
// Add pagination support
|
|
var hasNext bool
|
|
if (limit != 0) && (len(channels) == limit+1) {
|
|
hasNext = true
|
|
channels = channels[:len(channels)-1]
|
|
}
|
|
|
|
return &TopChannelList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: channels}
|
|
}
|
|
|
|
// GetTopThreadListWithPagination adds a rank to each item in the given list of TopThread and checks if there is
|
|
// another page that can be fetched based on the given limit and offset. The given list of TopThread is assumed to be
|
|
// sorted by ReplyCount(score). Returns a TopThreadList.
|
|
func GetTopThreadListWithPagination(threads []*TopThread, limit int) *TopThreadList {
|
|
// Add pagination support
|
|
var hasNext bool
|
|
if (limit != 0) && (len(threads) == limit+1) {
|
|
hasNext = true
|
|
threads = threads[:len(threads)-1]
|
|
}
|
|
|
|
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.
|
|
func GetTopDMListWithPagination(dms []*TopDM, limit int) *TopDMList {
|
|
// Add pagination support
|
|
var hasNext bool
|
|
if (limit != 0) && (len(dms) == limit+1) {
|
|
hasNext = true
|
|
dms = dms[:len(dms)-1]
|
|
}
|
|
|
|
return &TopDMList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: dms}
|
|
}
|
|
|
|
func GetNewTeamMembersListWithPagination(teamMembers []*NewTeamMember, limit int) *NewTeamMembersList {
|
|
var hasNext bool
|
|
if (limit != 0) && (len(teamMembers) == limit+1) {
|
|
hasNext = true
|
|
teamMembers = teamMembers[:len(teamMembers)-1]
|
|
}
|
|
|
|
return &NewTeamMembersList{InsightsListData: InsightsListData{HasNext: hasNext}, Items: teamMembers}
|
|
}
|