github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/analytics.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Righs Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"net/http"
     8  
     9  	"github.com/mattermost/mattermost-server/v5/mlog"
    10  	"github.com/mattermost/mattermost-server/v5/model"
    11  	"github.com/mattermost/mattermost-server/v5/store"
    12  )
    13  
    14  const (
    15  	DAY_MILLISECONDS   = 24 * 60 * 60 * 1000
    16  	MONTH_MILLISECONDS = 31 * DAY_MILLISECONDS
    17  )
    18  
    19  func (a *App) GetAnalytics(name string, teamId string) (model.AnalyticsRows, *model.AppError) {
    20  	skipIntensiveQueries := false
    21  	var systemUserCount int64
    22  	systemUserCount, err := a.Srv().Store.User().Count(model.UserCountOptions{})
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	if systemUserCount > int64(*a.Config().AnalyticsSettings.MaxUsersForStatistics) {
    28  		mlog.Debug("More than limit users are on the system, intensive queries skipped", mlog.Int("limit", *a.Config().AnalyticsSettings.MaxUsersForStatistics))
    29  		skipIntensiveQueries = true
    30  	}
    31  
    32  	if name == "standard" {
    33  		var rows model.AnalyticsRows = make([]*model.AnalyticsRow, 11)
    34  		rows[0] = &model.AnalyticsRow{Name: "channel_open_count", Value: 0}
    35  		rows[1] = &model.AnalyticsRow{Name: "channel_private_count", Value: 0}
    36  		rows[2] = &model.AnalyticsRow{Name: "post_count", Value: 0}
    37  		rows[3] = &model.AnalyticsRow{Name: "unique_user_count", Value: 0}
    38  		rows[4] = &model.AnalyticsRow{Name: "team_count", Value: 0}
    39  		rows[5] = &model.AnalyticsRow{Name: "total_websocket_connections", Value: 0}
    40  		rows[6] = &model.AnalyticsRow{Name: "total_master_db_connections", Value: 0}
    41  		rows[7] = &model.AnalyticsRow{Name: "total_read_db_connections", Value: 0}
    42  		rows[8] = &model.AnalyticsRow{Name: "daily_active_users", Value: 0}
    43  		rows[9] = &model.AnalyticsRow{Name: "monthly_active_users", Value: 0}
    44  		rows[10] = &model.AnalyticsRow{Name: "inactive_user_count", Value: 0}
    45  
    46  		openChan := make(chan store.StoreResult, 1)
    47  		privateChan := make(chan store.StoreResult, 1)
    48  		go func() {
    49  			count, err2 := a.Srv().Store.Channel().AnalyticsTypeCount(teamId, model.CHANNEL_OPEN)
    50  			openChan <- store.StoreResult{Data: count, Err: err2}
    51  			close(openChan)
    52  		}()
    53  		go func() {
    54  			count, err2 := a.Srv().Store.Channel().AnalyticsTypeCount(teamId, model.CHANNEL_PRIVATE)
    55  			privateChan <- store.StoreResult{Data: count, Err: err2}
    56  			close(privateChan)
    57  		}()
    58  
    59  		var userChan chan store.StoreResult
    60  		var userInactiveChan chan store.StoreResult
    61  		if teamId == "" {
    62  			userInactiveChan = make(chan store.StoreResult, 1)
    63  			go func() {
    64  				count, err2 := a.Srv().Store.User().AnalyticsGetInactiveUsersCount()
    65  				userInactiveChan <- store.StoreResult{Data: count, Err: err2}
    66  				close(userInactiveChan)
    67  			}()
    68  		} else {
    69  			userChan = make(chan store.StoreResult, 1)
    70  			go func() {
    71  				count, err2 := a.Srv().Store.User().Count(model.UserCountOptions{TeamId: teamId})
    72  				userChan <- store.StoreResult{Data: count, Err: err2}
    73  				close(userChan)
    74  			}()
    75  		}
    76  
    77  		var postChan chan store.StoreResult
    78  		if !skipIntensiveQueries {
    79  			postChan = make(chan store.StoreResult, 1)
    80  			go func() {
    81  				count, err2 := a.Srv().Store.Post().AnalyticsPostCount(teamId, false, false)
    82  				postChan <- store.StoreResult{Data: count, Err: err2}
    83  				close(postChan)
    84  			}()
    85  		}
    86  
    87  		teamCountChan := make(chan store.StoreResult, 1)
    88  		go func() {
    89  			teamCount, err2 := a.Srv().Store.Team().AnalyticsTeamCount(false)
    90  			teamCountChan <- store.StoreResult{Data: teamCount, Err: err2}
    91  			close(teamCountChan)
    92  		}()
    93  
    94  		dailyActiveChan := make(chan store.StoreResult, 1)
    95  		go func() {
    96  			dailyActive, err2 := a.Srv().Store.User().AnalyticsActiveCount(DAY_MILLISECONDS, model.UserCountOptions{IncludeBotAccounts: false, IncludeDeleted: false})
    97  			dailyActiveChan <- store.StoreResult{Data: dailyActive, Err: err2}
    98  			close(dailyActiveChan)
    99  		}()
   100  
   101  		monthlyActiveChan := make(chan store.StoreResult, 1)
   102  		go func() {
   103  			monthlyActive, err2 := a.Srv().Store.User().AnalyticsActiveCount(MONTH_MILLISECONDS, model.UserCountOptions{IncludeBotAccounts: false, IncludeDeleted: false})
   104  			monthlyActiveChan <- store.StoreResult{Data: monthlyActive, Err: err2}
   105  			close(monthlyActiveChan)
   106  		}()
   107  
   108  		r := <-openChan
   109  		if r.Err != nil {
   110  			return nil, r.Err
   111  		}
   112  		rows[0].Value = float64(r.Data.(int64))
   113  
   114  		r = <-privateChan
   115  		if r.Err != nil {
   116  			return nil, r.Err
   117  		}
   118  		rows[1].Value = float64(r.Data.(int64))
   119  
   120  		if postChan == nil {
   121  			rows[2].Value = -1
   122  		} else {
   123  			r = <-postChan
   124  			if r.Err != nil {
   125  				return nil, r.Err
   126  			}
   127  			rows[2].Value = float64(r.Data.(int64))
   128  		}
   129  
   130  		if userChan == nil {
   131  			rows[3].Value = float64(systemUserCount)
   132  		} else {
   133  			r = <-userChan
   134  			if r.Err != nil {
   135  				return nil, r.Err
   136  			}
   137  			rows[3].Value = float64(r.Data.(int64))
   138  		}
   139  
   140  		if userInactiveChan == nil {
   141  			rows[10].Value = -1
   142  		} else {
   143  			r = <-userInactiveChan
   144  			if r.Err != nil {
   145  				return nil, r.Err
   146  			}
   147  			rows[10].Value = float64(r.Data.(int64))
   148  		}
   149  
   150  		r = <-teamCountChan
   151  		if r.Err != nil {
   152  			return nil, r.Err
   153  		}
   154  		rows[4].Value = float64(r.Data.(int64))
   155  
   156  		// If in HA mode then aggregate all the stats
   157  		if a.Cluster() != nil && *a.Config().ClusterSettings.Enable {
   158  			stats, err2 := a.Cluster().GetClusterStats()
   159  			if err2 != nil {
   160  				return nil, err2
   161  			}
   162  
   163  			totalSockets := a.TotalWebsocketConnections()
   164  			totalMasterDb := a.Srv().Store.TotalMasterDbConnections()
   165  			totalReadDb := a.Srv().Store.TotalReadDbConnections()
   166  
   167  			for _, stat := range stats {
   168  				totalSockets = totalSockets + stat.TotalWebsocketConnections
   169  				totalMasterDb = totalMasterDb + stat.TotalMasterDbConnections
   170  				totalReadDb = totalReadDb + stat.TotalReadDbConnections
   171  			}
   172  
   173  			rows[5].Value = float64(totalSockets)
   174  			rows[6].Value = float64(totalMasterDb)
   175  			rows[7].Value = float64(totalReadDb)
   176  
   177  		} else {
   178  			rows[5].Value = float64(a.TotalWebsocketConnections())
   179  			rows[6].Value = float64(a.Srv().Store.TotalMasterDbConnections())
   180  			rows[7].Value = float64(a.Srv().Store.TotalReadDbConnections())
   181  		}
   182  
   183  		r = <-dailyActiveChan
   184  		if r.Err != nil {
   185  			return nil, r.Err
   186  		}
   187  		rows[8].Value = float64(r.Data.(int64))
   188  
   189  		r = <-monthlyActiveChan
   190  		if r.Err != nil {
   191  			return nil, r.Err
   192  		}
   193  		rows[9].Value = float64(r.Data.(int64))
   194  
   195  		return rows, nil
   196  	} else if name == "bot_post_counts_day" {
   197  		if skipIntensiveQueries {
   198  			rows := model.AnalyticsRows{&model.AnalyticsRow{Name: "", Value: -1}}
   199  			return rows, nil
   200  		}
   201  		return a.Srv().Store.Post().AnalyticsPostCountsByDay(&model.AnalyticsPostCountsOptions{
   202  			TeamId:        teamId,
   203  			BotsOnly:      true,
   204  			YesterdayOnly: false,
   205  		})
   206  	} else if name == "post_counts_day" {
   207  		if skipIntensiveQueries {
   208  			rows := model.AnalyticsRows{&model.AnalyticsRow{Name: "", Value: -1}}
   209  			return rows, nil
   210  		}
   211  		return a.Srv().Store.Post().AnalyticsPostCountsByDay(&model.AnalyticsPostCountsOptions{
   212  			TeamId:        teamId,
   213  			BotsOnly:      false,
   214  			YesterdayOnly: false,
   215  		})
   216  	} else if name == "user_counts_with_posts_day" {
   217  		if skipIntensiveQueries {
   218  			rows := model.AnalyticsRows{&model.AnalyticsRow{Name: "", Value: -1}}
   219  			return rows, nil
   220  		}
   221  
   222  		return a.Srv().Store.Post().AnalyticsUserCountsWithPostsByDay(teamId)
   223  	} else if name == "extra_counts" {
   224  		var rows model.AnalyticsRows = make([]*model.AnalyticsRow, 6)
   225  		rows[0] = &model.AnalyticsRow{Name: "file_post_count", Value: 0}
   226  		rows[1] = &model.AnalyticsRow{Name: "hashtag_post_count", Value: 0}
   227  		rows[2] = &model.AnalyticsRow{Name: "incoming_webhook_count", Value: 0}
   228  		rows[3] = &model.AnalyticsRow{Name: "outgoing_webhook_count", Value: 0}
   229  		rows[4] = &model.AnalyticsRow{Name: "command_count", Value: 0}
   230  		rows[5] = &model.AnalyticsRow{Name: "session_count", Value: 0}
   231  
   232  		iHookChan := make(chan store.StoreResult, 1)
   233  		go func() {
   234  			c, err2 := a.Srv().Store.Webhook().AnalyticsIncomingCount(teamId)
   235  			iHookChan <- store.StoreResult{Data: c, NErr: err2}
   236  			close(iHookChan)
   237  		}()
   238  
   239  		oHookChan := make(chan store.StoreResult, 1)
   240  		go func() {
   241  			c, err2 := a.Srv().Store.Webhook().AnalyticsOutgoingCount(teamId)
   242  			oHookChan <- store.StoreResult{Data: c, NErr: err2}
   243  			close(oHookChan)
   244  		}()
   245  
   246  		commandChan := make(chan store.StoreResult, 1)
   247  		go func() {
   248  			c, nErr := a.Srv().Store.Command().AnalyticsCommandCount(teamId)
   249  			commandChan <- store.StoreResult{Data: c, NErr: nErr}
   250  			close(commandChan)
   251  		}()
   252  
   253  		sessionChan := make(chan store.StoreResult, 1)
   254  		go func() {
   255  			count, err2 := a.Srv().Store.Session().AnalyticsSessionCount()
   256  			sessionChan <- store.StoreResult{Data: count, NErr: err2}
   257  			close(sessionChan)
   258  		}()
   259  
   260  		var fileChan chan store.StoreResult
   261  		var hashtagChan chan store.StoreResult
   262  
   263  		if !skipIntensiveQueries {
   264  			fileChan = make(chan store.StoreResult, 1)
   265  			go func() {
   266  				count, err2 := a.Srv().Store.Post().AnalyticsPostCount(teamId, true, false)
   267  				fileChan <- store.StoreResult{Data: count, Err: err2}
   268  				close(fileChan)
   269  			}()
   270  
   271  			hashtagChan = make(chan store.StoreResult, 1)
   272  			go func() {
   273  				count, err2 := a.Srv().Store.Post().AnalyticsPostCount(teamId, false, true)
   274  				hashtagChan <- store.StoreResult{Data: count, Err: err2}
   275  				close(hashtagChan)
   276  			}()
   277  		}
   278  
   279  		if fileChan == nil {
   280  			rows[0].Value = -1
   281  		} else {
   282  			r := <-fileChan
   283  			if r.Err != nil {
   284  				return nil, r.Err
   285  			}
   286  			rows[0].Value = float64(r.Data.(int64))
   287  		}
   288  
   289  		if hashtagChan == nil {
   290  			rows[1].Value = -1
   291  		} else {
   292  			r := <-hashtagChan
   293  			if r.Err != nil {
   294  				return nil, r.Err
   295  			}
   296  			rows[1].Value = float64(r.Data.(int64))
   297  		}
   298  
   299  		r := <-iHookChan
   300  		if r.NErr != nil {
   301  			return nil, model.NewAppError("GetAnalytics", "app.webhooks.analytics_incoming_count.app_error", nil, r.NErr.Error(), http.StatusInternalServerError)
   302  		}
   303  		rows[2].Value = float64(r.Data.(int64))
   304  
   305  		r = <-oHookChan
   306  		if r.NErr != nil {
   307  			return nil, model.NewAppError("GetAnalytics", "app.webhooks.analytics_outgoing_count.app_error", nil, r.NErr.Error(), http.StatusInternalServerError)
   308  		}
   309  		rows[3].Value = float64(r.Data.(int64))
   310  
   311  		r = <-commandChan
   312  		if r.NErr != nil {
   313  			return nil, model.NewAppError("GetAnalytics", "app.analytics.getanalytics.internal_error", nil, err.Error(), http.StatusInternalServerError)
   314  		}
   315  		rows[4].Value = float64(r.Data.(int64))
   316  
   317  		r = <-sessionChan
   318  		if r.NErr != nil {
   319  			return nil, model.NewAppError("GetAnalytics", "app.session.analytics_session_count.app_error", nil, r.NErr.Error(), http.StatusInternalServerError)
   320  		}
   321  		rows[5].Value = float64(r.Data.(int64))
   322  
   323  		return rows, nil
   324  	}
   325  
   326  	return nil, nil
   327  }
   328  
   329  func (a *App) GetRecentlyActiveUsersForTeam(teamId string) (map[string]*model.User, *model.AppError) {
   330  	users, err := a.Srv().Store.User().GetRecentlyActiveUsersForTeam(teamId, 0, 100, nil)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	userMap := make(map[string]*model.User)
   336  
   337  	for _, user := range users {
   338  		userMap[user.Id] = user
   339  	}
   340  
   341  	return userMap, nil
   342  }
   343  
   344  func (a *App) GetRecentlyActiveUsersForTeamPage(teamId string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
   345  	users, err := a.Srv().Store.User().GetRecentlyActiveUsersForTeam(teamId, page*perPage, perPage, viewRestrictions)
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  
   350  	return a.sanitizeProfiles(users, asAdmin), nil
   351  }
   352  
   353  func (a *App) GetNewUsersForTeamPage(teamId string, page, perPage int, asAdmin bool, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
   354  	users, err := a.Srv().Store.User().GetNewUsersForTeam(teamId, page*perPage, perPage, viewRestrictions)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	return a.sanitizeProfiles(users, asAdmin), nil
   360  }