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 }