mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* PLT-5860 Updated copyright date in about modal * PLT-5860 Updated copyright notice in JSX files * PLT-5860 Updated copyright notice in go files * Fixed misc copyright dates * Fixed component snapshots
493 lines
16 KiB
JavaScript
493 lines
16 KiB
JavaScript
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
import LineChart from './line_chart.jsx';
|
|
import DoughnutChart from './doughnut_chart.jsx';
|
|
import StatisticCount from './statistic_count.jsx';
|
|
|
|
import AnalyticsStore from 'stores/analytics_store.jsx';
|
|
|
|
import * as Utils from 'utils/utils.jsx';
|
|
import * as AsyncClient from 'utils/async_client.jsx';
|
|
import Constants from 'utils/constants.jsx';
|
|
const StatTypes = Constants.StatTypes;
|
|
|
|
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
|
|
|
import React from 'react';
|
|
|
|
export default 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;
|
|
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]);
|
|
|
|
let banner;
|
|
let postCount;
|
|
let postTotalGraph;
|
|
let activeUserGraph;
|
|
if (skippedIntensiveQueries) {
|
|
banner = (
|
|
<div className='banner'>
|
|
<div className='banner__content'>
|
|
<FormattedHTMLMessage
|
|
id='analytics.system.skippedIntensiveQueries'
|
|
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'
|
|
/>
|
|
}
|
|
icon='fa-comment'
|
|
count={stats[StatTypes.TOTAL_POSTS]}
|
|
/>
|
|
);
|
|
|
|
postTotalGraph = (
|
|
<div className='row'>
|
|
<LineChart
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.totalPosts'
|
|
defaultMessage='Total Posts'
|
|
/>
|
|
}
|
|
data={postCountsDay}
|
|
options={{
|
|
legend: {
|
|
display: false
|
|
}
|
|
}}
|
|
width='740'
|
|
height='225'
|
|
/>
|
|
</div>
|
|
);
|
|
|
|
activeUserGraph = (
|
|
<div className='row'>
|
|
<LineChart
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.activeUsers'
|
|
defaultMessage='Active Users With Posts'
|
|
/>
|
|
}
|
|
data={userCountsWithPostsDay}
|
|
options={{
|
|
legend: {
|
|
display: false
|
|
}
|
|
}}
|
|
width='740'
|
|
height='225'
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
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]}
|
|
/>
|
|
);
|
|
|
|
advancedStats = (
|
|
<div>
|
|
<StatisticCount
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.totalWebsockets'
|
|
defaultMessage='WebSocket Conns'
|
|
/>
|
|
}
|
|
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>
|
|
);
|
|
|
|
const channelTypeData = formatChannelDoughtnutData(stats[StatTypes.TOTAL_PUBLIC_CHANNELS], stats[StatTypes.TOTAL_PRIVATE_GROUPS]);
|
|
const postTypeData = formatPostDoughtnutData(stats[StatTypes.TOTAL_FILE_POSTS], stats[StatTypes.TOTAL_HASHTAG_POSTS], stats[StatTypes.TOTAL_POSTS]);
|
|
|
|
let postTypeGraph;
|
|
if (stats[StatTypes.TOTAL_POSTS] !== -1) {
|
|
postTypeGraph = (
|
|
<DoughnutChart
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.postTypes'
|
|
defaultMessage='Posts, Files and Hashtags'
|
|
/>
|
|
}
|
|
data={postTypeData}
|
|
width='300'
|
|
height='225'
|
|
/>
|
|
);
|
|
}
|
|
|
|
advancedGraphs = (
|
|
<div className='row'>
|
|
<DoughnutChart
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.channelTypes'
|
|
defaultMessage='Channel Types'
|
|
/>
|
|
}
|
|
data={channelTypeData}
|
|
width='300'
|
|
height='225'
|
|
/>
|
|
{postTypeGraph}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const userCount = (
|
|
<StatisticCount
|
|
title={
|
|
<FormattedMessage
|
|
id='analytics.system.totalUsers'
|
|
defaultMessage='Total Users'
|
|
/>
|
|
}
|
|
icon='fa-user'
|
|
count={stats[StatTypes.TOTAL_USERS]}
|
|
/>
|
|
);
|
|
|
|
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]}
|
|
/>
|
|
);
|
|
|
|
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]}
|
|
/>
|
|
);
|
|
|
|
let firstRow;
|
|
let secondRow;
|
|
if (isLicensed && skippedIntensiveQueries) {
|
|
firstRow = (
|
|
<div>
|
|
{userCount}
|
|
{teamCount}
|
|
{channelCount}
|
|
{sessionCount}
|
|
</div>
|
|
);
|
|
|
|
secondRow = (
|
|
<div>
|
|
{commandCount}
|
|
{incomingCount}
|
|
{outgoingCount}
|
|
</div>
|
|
);
|
|
} else if (isLicensed && !skippedIntensiveQueries) {
|
|
firstRow = (
|
|
<div>
|
|
{userCount}
|
|
{teamCount}
|
|
{channelCount}
|
|
{postCount}
|
|
</div>
|
|
);
|
|
|
|
secondRow = (
|
|
<div>
|
|
{sessionCount}
|
|
{commandCount}
|
|
{incomingCount}
|
|
{outgoingCount}
|
|
</div>
|
|
);
|
|
} else if (!isLicensed) {
|
|
firstRow = (
|
|
<div>
|
|
{userCount}
|
|
{teamCount}
|
|
{channelCount}
|
|
{postCount}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const thirdRow = (
|
|
<div>
|
|
{dailyActiveUsers}
|
|
{monthlyActiveUsers}
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className='wrapper--fixed team_statistics'>
|
|
<h3 className='admin-console-header'>
|
|
<FormattedMessage
|
|
id='analytics.system.title'
|
|
defaultMessage='System Statistics'
|
|
/>
|
|
</h3>
|
|
{banner}
|
|
<div className='row'>
|
|
{firstRow}
|
|
{secondRow}
|
|
{thirdRow}
|
|
{advancedStats}
|
|
</div>
|
|
{advancedGraphs}
|
|
{postTotalGraph}
|
|
{activeUserGraph}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export function formatChannelDoughtnutData(totalPublic, totalPrivate) {
|
|
const channelTypeData = {
|
|
labels: [
|
|
Utils.localizeMessage('analytics.system.publicChannels', 'Public Channels'),
|
|
Utils.localizeMessage('analytics.system.privateGroups', 'Private Channels')
|
|
],
|
|
datasets: [{
|
|
data: [totalPublic, totalPrivate],
|
|
backgroundColor: ['#46BFBD', '#FDB45C'],
|
|
hoverBackgroundColor: ['#5AD3D1', '#FFC870']
|
|
}]
|
|
};
|
|
|
|
return channelTypeData;
|
|
}
|
|
|
|
export function formatPostDoughtnutData(filePosts, hashtagPosts, totalPosts) {
|
|
const postTypeData = {
|
|
labels: [
|
|
Utils.localizeMessage('analytics.system.totalFilePosts', 'Posts with Files'),
|
|
Utils.localizeMessage('analytics.system.totalHashtagPosts', 'Posts with Hashtags'),
|
|
Utils.localizeMessage('analytics.system.textPosts', 'Posts with Text-only')
|
|
],
|
|
datasets: [{
|
|
data: [filePosts, hashtagPosts, (totalPosts - filePosts - hashtagPosts)],
|
|
backgroundColor: ['#46BFBD', '#F7464A', '#FDB45C'],
|
|
hoverBackgroundColor: ['#5AD3D1', '#FF5A5E', '#FFC870']
|
|
}]
|
|
};
|
|
|
|
return postTypeData;
|
|
}
|
|
|
|
export function formatPostsPerDayData(data) {
|
|
var chartData = {
|
|
labels: [],
|
|
datasets: [{
|
|
fillColor: 'rgba(151,187,205,0.2)',
|
|
borderColor: 'rgba(151,187,205,1)',
|
|
pointBackgroundColor: 'rgba(151,187,205,1)',
|
|
pointBorderColor: '#fff',
|
|
pointHoverBackgroundColor: '#fff',
|
|
pointHoverBorderColor: 'rgba(151,187,205,1)',
|
|
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: [{
|
|
label: '',
|
|
fillColor: 'rgba(151,187,205,0.2)',
|
|
borderColor: 'rgba(151,187,205,1)',
|
|
pointBackgroundColor: 'rgba(151,187,205,1)',
|
|
pointBorderColor: '#fff',
|
|
pointHoverBackgroundColor: '#fff',
|
|
pointHoverBorderColor: 'rgba(151,187,205,1)',
|
|
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;
|
|
}
|