2016-02-25 12:32:46 -05:00
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
2016-05-30 09:59:53 -04:00
import Banner from 'components/admin_console/banner.jsx' ;
2016-02-25 12:32:46 -05:00
import LineChart from './line_chart.jsx' ;
import DoughnutChart from './doughnut_chart.jsx' ;
import StatisticCount from './statistic_count.jsx' ;
2016-03-14 08:50:46 -04:00
import AnalyticsStore from 'stores/analytics_store.jsx' ;
2016-02-25 12:32:46 -05:00
2016-03-14 08:50:46 -04:00
import * as Utils from 'utils/utils.jsx' ;
2016-05-30 09:59:53 -04:00
import { isLicenseExpired , isLicenseExpiring , displayExpiryDate } from 'utils/license_utils.jsx' ;
2016-03-14 08:50:46 -04:00
import * as AsyncClient from 'utils/async_client.jsx' ;
import Constants from 'utils/constants.jsx' ;
2016-02-25 12:32:46 -05:00
const StatTypes = Constants . StatTypes ;
2016-05-30 09:59:53 -04:00
import { injectIntl , intlShape , defineMessages , FormattedMessage , FormattedHTMLMessage } from 'react-intl' ;
2016-02-25 12:32:46 -05:00
const holders = defineMessages ( {
analyticsPublicChannels : {
id : 'analytics.system.publicChannels' ,
defaultMessage : 'Public Channels'
} ,
analyticsPrivateGroups : {
id : 'analytics.system.privateGroups' ,
defaultMessage : 'Private Groups'
} ,
analyticsFilePosts : {
id : 'analytics.system.totalFilePosts' ,
defaultMessage : 'Posts with Files'
} ,
analyticsHashtagPosts : {
id : 'analytics.system.totalHashtagPosts' ,
defaultMessage : 'Posts with Hashtags'
} ,
analyticsTextPosts : {
id : 'analytics.system.textPosts' ,
defaultMessage : 'Posts with Text-only'
}
} ) ;
2016-03-14 08:50:46 -04:00
import React from 'react' ;
2016-02-25 12:32:46 -05:00
class SystemAnalytics extends React . Component {
constructor ( props ) {
super ( props ) ;
this . onChange = this . onChange . bind ( this ) ;
this . state = { stats : AnalyticsStore . getAllSystem ( ) } ;
}
componentDidMount ( ) {
AnalyticsStore . addChangeListener ( this . onChange ) ;
AsyncClient . getStandardAnalytics ( ) ;
AsyncClient . getPostsPerDayAnalytics ( ) ;
AsyncClient . getUsersPerDayAnalytics ( ) ;
if ( global . window . mm _license . IsLicensed === 'true' ) {
AsyncClient . getAdvancedAnalytics ( ) ;
}
}
componentWillUnmount ( ) {
AnalyticsStore . removeChangeListener ( this . onChange ) ;
}
shouldComponentUpdate ( nextProps , nextState ) {
if ( ! Utils . areObjectsEqual ( nextState . stats , this . state . stats ) ) {
return true ;
}
return false ;
}
onChange ( ) {
this . setState ( { stats : AnalyticsStore . getAllSystem ( ) } ) ;
}
render ( ) {
const stats = this . state . stats ;
2017-01-09 09:26:07 -05:00
const isLicensed = global . window . mm _license . IsLicensed === 'true' ;
const skippedIntensiveQueries = stats [ StatTypes . TOTAL _POSTS ] === - 1 ;
const postCountsDay = formatPostsPerDayData ( stats [ StatTypes . POST _PER _DAY ] ) ;
const userCountsWithPostsDay = formatUsersWithPostsPerDayData ( stats [ StatTypes . USERS _WITH _POSTS _PER _DAY ] ) ;
2016-02-25 12:32:46 -05:00
2017-01-04 14:24:04 -05:00
let banner ;
2017-01-09 09:26:07 -05:00
let postCount ;
let postTotalGraph ;
let activeUserGraph ;
if ( skippedIntensiveQueries ) {
2017-01-04 14:24:04 -05:00
banner = (
2017-01-09 09:26:07 -05:00
< div className = 'banner' >
< div className = 'banner__content' >
2017-01-04 14:24:04 -05:00
< FormattedHTMLMessage
id = 'analytics.system.skippedIntensiveQueries'
2017-01-09 09:26:07 -05:00
defaultMessage = "To maximize performance, some statistics are disabled. You can re-enable them in config.json. See: <a href='https://docs.mattermost.com/administration/statistics.html' target='_blank'>https://docs.mattermost.com/administration/statistics.html</a>"
/ >
< / div >
< / div >
) ;
} else {
postCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalPosts'
defaultMessage = 'Total Posts'
2017-01-04 14:24:04 -05:00
/ >
}
2017-01-09 09:26:07 -05:00
icon = 'fa-comment'
count = { stats [ StatTypes . TOTAL _POSTS ] }
2017-01-04 14:24:04 -05:00
/ >
) ;
2017-01-09 09:26:07 -05:00
postTotalGraph = (
2016-02-25 12:32:46 -05:00
< div className = 'row' >
2017-01-09 09:26:07 -05:00
< LineChart
2016-02-25 12:32:46 -05:00
title = {
< FormattedMessage
2017-01-09 09:26:07 -05:00
id = 'analytics.system.totalPosts'
defaultMessage = 'Total Posts'
2016-02-25 12:32:46 -05:00
/ >
}
2017-01-09 09:26:07 -05:00
data = { postCountsDay }
options = { {
legend : {
display : false
}
} }
width = '740'
height = '225'
2016-02-25 12:32:46 -05:00
/ >
2017-01-09 09:26:07 -05:00
< / div >
) ;
activeUserGraph = (
< div className = 'row' >
< LineChart
2016-02-25 12:32:46 -05:00
title = {
< FormattedMessage
2017-01-09 09:26:07 -05:00
id = 'analytics.system.activeUsers'
defaultMessage = 'Active Users With Posts'
2016-02-25 12:32:46 -05:00
/ >
}
2017-01-09 09:26:07 -05:00
data = { userCountsWithPostsDay }
options = { {
legend : {
display : false
}
} }
width = '740'
height = '225'
2016-02-25 12:32:46 -05:00
/ >
< / div >
) ;
2017-01-09 09:26:07 -05:00
}
let advancedStats ;
let advancedGraphs ;
let sessionCount ;
let commandCount ;
let incomingCount ;
let outgoingCount ;
if ( global . window . mm _license . IsLicensed === 'true' ) {
sessionCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalSessions'
defaultMessage = 'Total Sessions'
/ >
}
icon = 'fa-signal'
count = { stats [ StatTypes . TOTAL _SESSIONS ] }
/ >
) ;
commandCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalCommands'
defaultMessage = 'Total Commands'
/ >
}
icon = 'fa-terminal'
count = { stats [ StatTypes . TOTAL _COMMANDS ] }
/ >
) ;
incomingCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalIncomingWebhooks'
defaultMessage = 'Incoming Webhooks'
/ >
}
icon = 'fa-arrow-down'
count = { stats [ StatTypes . TOTAL _IHOOKS ] }
/ >
) ;
outgoingCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalOutgoingWebhooks'
defaultMessage = 'Outgoing Webhooks'
/ >
}
icon = 'fa-arrow-up'
count = { stats [ StatTypes . TOTAL _OHOOKS ] }
/ >
) ;
2016-02-25 12:32:46 -05:00
2016-10-19 14:49:25 -04:00
advancedStats = (
2017-03-02 19:10:20 +05:00
< div >
2016-10-19 14:49:25 -04:00
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalWebsockets'
2016-12-21 23:29:38 +01:00
defaultMessage = 'WebSocket Conns'
2016-10-19 14:49:25 -04:00
/ >
}
icon = 'fa-user'
count = { stats [ StatTypes . TOTAL _WEBSOCKET _CONNECTIONS ] }
/ >
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalMasterDbConnections'
defaultMessage = 'Master DB Conns'
/ >
}
icon = 'fa-terminal'
count = { stats [ StatTypes . TOTAL _MASTER _DB _CONNECTIONS ] }
/ >
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalReadDbConnections'
defaultMessage = 'Replica DB Conns'
/ >
}
icon = 'fa-terminal'
count = { stats [ StatTypes . TOTAL _READ _DB _CONNECTIONS ] }
/ >
< / div >
) ;
2016-02-25 12:32:46 -05:00
const channelTypeData = formatChannelDoughtnutData ( stats [ StatTypes . TOTAL _PUBLIC _CHANNELS ] , stats [ StatTypes . TOTAL _PRIVATE _GROUPS ] , this . props . intl ) ;
const postTypeData = formatPostDoughtnutData ( stats [ StatTypes . TOTAL _FILE _POSTS ] , stats [ StatTypes . TOTAL _HASHTAG _POSTS ] , stats [ StatTypes . TOTAL _POSTS ] , this . props . intl ) ;
2017-01-04 14:24:04 -05:00
let postTypeGraph ;
if ( stats [ StatTypes . TOTAL _POSTS ] !== - 1 ) {
postTypeGraph = (
2016-02-25 12:32:46 -05:00
< DoughnutChart
title = {
< FormattedMessage
2017-01-04 14:24:04 -05:00
id = 'analytics.system.postTypes'
defaultMessage = 'Posts, Files and Hashtags'
2016-02-25 12:32:46 -05:00
/ >
}
2017-01-04 14:24:04 -05:00
data = { postTypeData }
2016-02-25 12:32:46 -05:00
width = '300'
height = '225'
/ >
2017-01-04 14:24:04 -05:00
) ;
}
advancedGraphs = (
< div className = 'row' >
2016-02-25 12:32:46 -05:00
< DoughnutChart
title = {
< FormattedMessage
2017-01-04 14:24:04 -05:00
id = 'analytics.system.channelTypes'
defaultMessage = 'Channel Types'
2016-02-25 12:32:46 -05:00
/ >
}
2017-01-04 14:24:04 -05:00
data = { channelTypeData }
2016-02-25 12:32:46 -05:00
width = '300'
height = '225'
/ >
2017-01-04 14:24:04 -05:00
{ postTypeGraph }
2016-02-25 12:32:46 -05:00
< / div >
) ;
2016-05-30 09:59:53 -04:00
if ( isLicenseExpired ( ) ) {
banner = (
< Banner
description = {
< FormattedHTMLMessage
id = 'analytics.system.expiredBanner'
defaultMessage = 'The Enterprise license expired on {date}. You have 15 days from this date to renew the license, please contact <a href="mailto:commercial@mattermost.com">commercial@mattermost.com</a>.'
values = { {
date : displayExpiryDate ( )
} }
/ >
}
/ >
) ;
} else if ( isLicenseExpiring ( ) ) {
banner = (
< Banner
description = {
< FormattedHTMLMessage
id = 'analytics.system.expiringBanner'
defaultMessage = 'The Enterprise license is expiring on {date}. To renew your license, please contact <a href="mailto:commercial@mattermost.com">commercial@mattermost.com</a>.'
values = { {
date : displayExpiryDate ( )
} }
/ >
}
/ >
) ;
}
2016-02-25 12:32:46 -05:00
}
2017-01-09 09:26:07 -05:00
const userCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalUsers'
defaultMessage = 'Total Users'
/ >
}
icon = 'fa-user'
count = { stats [ StatTypes . TOTAL _USERS ] }
/ >
) ;
2016-02-25 12:32:46 -05:00
2017-01-09 09:26:07 -05:00
const teamCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalTeams'
defaultMessage = 'Total Teams'
/ >
}
icon = 'fa-users'
count = { stats [ StatTypes . TOTAL _TEAMS ] }
/ >
) ;
const channelCount = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.totalChannels'
defaultMessage = 'Total Channels'
/ >
}
icon = 'fa-globe'
count = { stats [ StatTypes . TOTAL _PUBLIC _CHANNELS ] + stats [ StatTypes . TOTAL _PRIVATE _GROUPS ] }
/ >
) ;
2017-01-20 15:24:53 -05:00
const dailyActiveUsers = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.dailyActiveUsers'
defaultMessage = 'Daily Active Users'
/ >
}
icon = 'fa-users'
count = { stats [ StatTypes . DAILY _ACTIVE _USERS ] }
/ >
) ;
const monthlyActiveUsers = (
< StatisticCount
title = {
< FormattedMessage
id = 'analytics.system.monthlyActiveUsers'
defaultMessage = 'Monthly Active Users'
/ >
}
icon = 'fa-users'
count = { stats [ StatTypes . MONTHLY _ACTIVE _USERS ] }
/ >
) ;
2017-01-09 09:26:07 -05:00
let firstRow ;
let secondRow ;
if ( isLicensed && skippedIntensiveQueries ) {
firstRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-09 09:26:07 -05:00
{ userCount }
{ teamCount }
{ channelCount }
{ sessionCount }
< / div >
2017-01-04 14:24:04 -05:00
) ;
2017-01-09 09:26:07 -05:00
secondRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-09 09:26:07 -05:00
{ commandCount }
{ incomingCount }
{ outgoingCount }
< / div >
) ;
} else if ( isLicensed && ! skippedIntensiveQueries ) {
firstRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-09 09:26:07 -05:00
{ userCount }
{ teamCount }
{ channelCount }
{ postCount }
2017-01-04 14:24:04 -05:00
< / div >
) ;
2017-01-09 09:26:07 -05:00
secondRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-09 09:26:07 -05:00
{ sessionCount }
{ commandCount }
{ incomingCount }
{ outgoingCount }
< / div >
) ;
} else if ( ! isLicensed ) {
firstRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-09 09:26:07 -05:00
{ userCount }
{ teamCount }
{ channelCount }
{ postCount }
2017-01-04 14:24:04 -05:00
< / div >
) ;
}
2017-01-20 15:24:53 -05:00
const thirdRow = (
2017-03-02 19:10:20 +05:00
< div >
2017-01-20 15:24:53 -05:00
{ dailyActiveUsers }
{ monthlyActiveUsers }
< / div >
) ;
2016-02-25 12:32:46 -05:00
return (
< div className = 'wrapper--fixed team_statistics' >
< h3 >
< FormattedMessage
id = 'analytics.system.title'
defaultMessage = 'System Statistics'
/ >
< / h3 >
2016-05-30 09:59:53 -04:00
{ banner }
2017-03-02 19:10:20 +05:00
< div className = 'row' >
{ firstRow }
{ secondRow }
{ thirdRow }
{ advancedStats }
< / div >
2016-02-25 12:32:46 -05:00
{ advancedGraphs }
2017-01-04 14:24:04 -05:00
{ postTotalGraph }
{ activeUserGraph }
2016-02-25 12:32:46 -05:00
< / div >
) ;
}
}
SystemAnalytics . propTypes = {
2016-05-17 07:21:39 -04:00
intl : intlShape . isRequired
2016-02-25 12:32:46 -05:00
} ;
export default injectIntl ( SystemAnalytics ) ;
export function formatChannelDoughtnutData ( totalPublic , totalPrivate , intl ) {
const { formatMessage } = intl ;
2016-05-30 09:44:32 -03:00
const channelTypeData = {
labels : [ formatMessage ( holders . analyticsPublicChannels ) , formatMessage ( holders . analyticsPrivateGroups ) ] ,
datasets : [ {
data : [ totalPublic , totalPrivate ] ,
backgroundColor : [ '#46BFBD' , '#FDB45C' ] ,
hoverBackgroundColor : [ '#5AD3D1' , '#FFC870' ]
} ]
} ;
2016-02-25 12:32:46 -05:00
return channelTypeData ;
}
export function formatPostDoughtnutData ( filePosts , hashtagPosts , totalPosts , intl ) {
const { formatMessage } = intl ;
2016-05-30 09:44:32 -03:00
const postTypeData = {
labels : [ formatMessage ( holders . analyticsFilePosts ) , formatMessage ( holders . analyticsHashtagPosts ) , formatMessage ( holders . analyticsTextPosts ) ] ,
datasets : [ {
data : [ filePosts , hashtagPosts , ( totalPosts - filePosts - hashtagPosts ) ] ,
backgroundColor : [ '#46BFBD' , '#F7464A' , '#FDB45C' ] ,
hoverBackgroundColor : [ '#5AD3D1' , '#FF5A5E' , '#FFC870' ]
} ]
} ;
2016-02-25 12:32:46 -05:00
return postTypeData ;
}
export function formatPostsPerDayData ( data ) {
var chartData = {
labels : [ ] ,
datasets : [ {
fillColor : 'rgba(151,187,205,0.2)' ,
2016-05-30 09:44:32 -03:00
borderColor : 'rgba(151,187,205,1)' ,
pointBackgroundColor : 'rgba(151,187,205,1)' ,
pointBorderColor : '#fff' ,
pointHoverBackgroundColor : '#fff' ,
pointHoverBorderColor : 'rgba(151,187,205,1)' ,
2016-02-25 12:32:46 -05:00
data : [ ]
} ]
} ;
for ( var index in data ) {
if ( data [ index ] ) {
var row = data [ index ] ;
chartData . labels . push ( row . name ) ;
chartData . datasets [ 0 ] . data . push ( row . value ) ;
}
}
return chartData ;
}
export function formatUsersWithPostsPerDayData ( data ) {
var chartData = {
labels : [ ] ,
datasets : [ {
2016-05-30 09:44:32 -03:00
label : '' ,
2016-02-25 12:32:46 -05:00
fillColor : 'rgba(151,187,205,0.2)' ,
2016-05-30 09:44:32 -03:00
borderColor : 'rgba(151,187,205,1)' ,
pointBackgroundColor : 'rgba(151,187,205,1)' ,
pointBorderColor : '#fff' ,
pointHoverBackgroundColor : '#fff' ,
pointHoverBorderColor : 'rgba(151,187,205,1)' ,
2016-02-25 12:32:46 -05:00
data : [ ]
} ]
} ;
for ( var index in data ) {
if ( data [ index ] ) {
var row = data [ index ] ;
chartData . labels . push ( row . name ) ;
chartData . datasets [ 0 ] . data . push ( row . value ) ;
}
}
return chartData ;
}