Report DAU and MAU by role (#19657)

pull/19685/head
Rodrigo Nascimento 5 years ago committed by GitHub
parent c056793c3e
commit cd6bcbfd44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 59
      app/models/server/models/Sessions.js
  2. 34
      app/models/server/models/Sessions.tests.js
  3. 2
      app/models/server/raw/Sessions.js
  4. 5
      app/statistics/server/lib/SAUMonitor.js
  5. 47
      app/statistics/server/lib/getMostImportantRole.js
  6. 45
      app/statistics/server/lib/getMostImportantRole.tests.js
  7. 1
      server/startup/migrations/index.js
  8. 41
      server/startup/migrations/v211.js

@ -8,7 +8,6 @@ export const aggregates = {
userId: { $exists: true },
lastActivityAt: { $exists: true },
device: { $exists: true },
roles: { $ne: 'anonymous' },
type: 'session',
$or: [{
year: { $lt: year },
@ -32,6 +31,7 @@ export const aggregates = {
day: 1,
month: 1,
year: 1,
mostImportantRole: 1,
time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } },
},
}, {
@ -47,6 +47,7 @@ export const aggregates = {
month: '$month',
year: '$year',
},
mostImportantRole: { $first: '$mostImportantRole' },
time: { $sum: '$time' },
sessions: { $sum: 1 },
},
@ -58,6 +59,7 @@ export const aggregates = {
month: '$_id.month',
year: '$_id.year',
},
mostImportantRole: { $first: '$mostImportantRole' },
time: { $sum: '$time' },
sessions: { $sum: '$sessions' },
devices: {
@ -77,6 +79,7 @@ export const aggregates = {
month: '$_id.month',
year: '$_id.year',
userId: '$_id.userId',
mostImportantRole: 1,
time: 1,
sessions: 1,
devices: 1,
@ -98,6 +101,7 @@ export const aggregates = {
day: '$day',
month: '$month',
year: '$year',
mostImportantRole: '$mostImportantRole',
},
count: {
$sum: 1,
@ -109,12 +113,38 @@ export const aggregates = {
$sum: '$time',
},
},
}, {
$group: {
_id: {
day: '$day',
month: '$month',
year: '$year',
},
roles: {
$push: {
role: '$_id.mostImportantRole',
count: '$count',
sessions: '$sessions',
time: '$time',
},
},
count: {
$sum: '$count',
},
sessions: {
$sum: '$sessions',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
sessions: 1,
time: 1,
roles: 1,
},
}]).toArray();
},
@ -130,6 +160,7 @@ export const aggregates = {
_id: {
userId: '$userId',
},
mostImportantRole: { $first: '$mostImportantRole' },
sessions: {
$sum: '$sessions',
},
@ -139,7 +170,9 @@ export const aggregates = {
},
}, {
$group: {
_id: 1,
_id: {
mostImportantRole: '$mostImportantRole',
},
count: {
$sum: 1,
},
@ -150,10 +183,32 @@ export const aggregates = {
$sum: '$time',
},
},
}, {
$group: {
_id: 1,
roles: {
$push: {
role: '$_id.mostImportantRole',
count: '$count',
sessions: '$sessions',
time: '$time',
},
},
count: {
$sum: '$count',
},
sessions: {
$sum: '$sessions',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
roles: 1,
sessions: 1,
time: 1,
},

@ -47,6 +47,7 @@ const DATA = {
loginAt: new Date('2019-04-30T00:11:34.047Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
lastActivityAt: new Date('2019-04-30T00:16:20.349Z'),
closedAt: new Date('2019-04-30T00:16:20.349Z'),
}, {
@ -73,6 +74,7 @@ const DATA = {
loginAt: new Date('2019-05-03T00:11:34.047Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
lastActivityAt: new Date('2019-05-03T00:16:20.349Z'),
closedAt: new Date('2019-05-03T00:16:20.349Z'),
}, {
@ -99,6 +101,7 @@ const DATA = {
loginAt: new Date('2019-05-03T00:16:21.846Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
lastActivityAt: new Date('2019-05-03T00:17:21.081Z'),
closedAt: new Date('2019-05-03T00:17:21.081Z'),
}, {
@ -125,6 +128,7 @@ const DATA = {
loginAt: new Date('2019-05-03T00:17:22.375Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
lastActivityAt: new Date('2019-05-03T01:48:31.695Z'),
closedAt: new Date('2019-05-03T01:48:31.695Z'),
}, {
@ -151,6 +155,7 @@ const DATA = {
loginAt: new Date('2019-05-03T01:48:43.521Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
closedAt: new Date('2019-05-03T01:48:43.761Z'),
lastActivityAt: new Date('2019-05-03T01:48:43.761Z'),
}, {
@ -177,6 +182,7 @@ const DATA = {
loginAt: new Date('2019-05-03T01:48:45.064Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
}, {
_id: 'CJwfxASo62FHDgqog',
day: 2,
@ -201,6 +207,7 @@ const DATA = {
loginAt: new Date('2019-05-03T01:50:31.092Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
closedAt: new Date('2019-05-03T01:50:31.355Z'),
lastActivityAt: new Date('2019-05-03T01:50:31.355Z'),
}, {
@ -227,6 +234,7 @@ const DATA = {
loginAt: new Date('2019-05-03T01:50:32.765Z'),
type: 'session',
userId: 'xPZXw9xqM3kKshsse2',
mostImportantRole: 'admin',
lastActivityAt: new Date('2019-05-03T02:59:59.999Z'),
}],
sessions_dates,
@ -651,6 +659,7 @@ describe('Sessions Aggregates', () => {
month: 5,
year: 2019,
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
}, {
_id: 'xPZXw9xqM3kKshsse-2019-4-30',
day: 30,
@ -674,6 +683,7 @@ describe('Sessions Aggregates', () => {
type: 'user_daily',
_computedAt: docs[1]._computedAt,
userId: 'xPZXw9xqM3kKshsse',
mostImportantRole: 'user',
year: 2019,
}, {
_id: 'xPZXw9xqM3kKshsse2-2019-5-1',
@ -699,6 +709,7 @@ describe('Sessions Aggregates', () => {
month: 5,
year: 2019,
userId: 'xPZXw9xqM3kKshsse2',
mostImportantRole: 'admin',
}]);
return collection.insertMany(docs);
@ -712,6 +723,17 @@ describe('Sessions Aggregates', () => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 2,
roles: [{
count: 1,
role: 'user',
sessions: 3,
time: 5814,
}, {
count: 1,
role: 'admin',
sessions: 1,
time: 4167,
}],
sessions: 4,
time: 9981,
}]);
@ -725,6 +747,12 @@ describe('Sessions Aggregates', () => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 1,
roles: [{
count: 1,
role: 'admin',
sessions: 1,
time: 4167,
}],
sessions: 1,
time: 4167,
}]);
@ -738,6 +766,12 @@ describe('Sessions Aggregates', () => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 1,
roles: [{
count: 1,
role: 'user',
sessions: 3,
time: 5814,
}],
sessions: 3,
time: 5814,
}]);

@ -127,6 +127,7 @@ export class SessionsRaw extends BaseRaw {
$match: {
...matchBasedOnDate(start, end),
type: 'user_daily',
mostImportantRole: { $ne: 'anonymous' },
},
},
{
@ -194,6 +195,7 @@ export class SessionsRaw extends BaseRaw {
$match: {
...matchBasedOnDate(start, end),
type: 'user_daily',
mostImportantRole: { $ne: 'anonymous' },
},
},
{

@ -7,6 +7,7 @@ import { UAParserMobile, UAParserDesktop } from './UAParserCustom';
import { Sessions } from '../../../models/server';
import { Logger } from '../../../logger';
import { aggregates } from '../../../models/server/models/Sessions';
import { getMostImportantRole } from './getMostImportantRole';
const getDateObj = (dateTime = new Date()) => ({
day: dateTime.getDate(),
@ -127,8 +128,10 @@ export class SAUMonitorClass {
const { roles, _id: userId } = info.user;
const mostImportantRole = getMostImportantRole(roles);
const loginAt = new Date();
const params = { userId, roles, loginAt, ...getDateObj() };
const params = { userId, roles, mostImportantRole, loginAt, ...getDateObj() };
this._handleSession(info.connection, params);
this._updateConnectionInfo(info.connection.id, { loginAt });
});

@ -0,0 +1,47 @@
const order = [
'admin',
'livechat-manager',
'livechat-monitor',
'livechat-agent',
'custom-role',
'user',
'app',
'bot',
'guest',
'anonymous',
];
const rolesToConsiderAsUser = [
'auditor',
'auditor-log',
];
export function getMostImportantRole(roles = []) {
if (!roles.length) {
return 'no-role';
}
roles = roles.map((r) => (rolesToConsiderAsUser.includes(r) ? 'user' : r));
if (roles.length === 1) {
if (!order.includes(roles[0])) {
return 'custom-role';
}
return roles[0];
}
const newRoles = [];
for (const role of roles) {
if (order.includes(role)) {
newRoles.push(role);
} else if (!newRoles.includes('custom-role')) {
newRoles.push('custom-role');
}
}
for (const item of order) {
if (newRoles.includes(item)) {
return item;
}
}
}

@ -0,0 +1,45 @@
/* eslint-env mocha */
import { expect } from 'chai';
import { getMostImportantRole } from './getMostImportantRole';
describe('getMostImportantRole', () => {
it('should return the same role if only one exists', () => {
expect(getMostImportantRole(['admin'])).to.be.eq('admin');
expect(getMostImportantRole(['livechat-manager'])).to.be.eq('livechat-manager');
expect(getMostImportantRole(['livechat-monitor'])).to.be.eq('livechat-monitor');
expect(getMostImportantRole(['livechat-agent'])).to.be.eq('livechat-agent');
expect(getMostImportantRole(['user'])).to.be.eq('user');
expect(getMostImportantRole(['guest'])).to.be.eq('guest');
expect(getMostImportantRole(['anonymous'])).to.be.eq('anonymous');
expect(getMostImportantRole(['app'])).to.be.eq('app');
expect(getMostImportantRole(['bot'])).to.be.eq('bot');
});
it('should return custom roles as `custom-role`', () => {
expect(getMostImportantRole(['custom1'])).to.be.eq('custom-role');
});
it('should return auditor, app and bot as `user`', () => {
expect(getMostImportantRole(['auditor'])).to.be.eq('user');
expect(getMostImportantRole(['auditor-log'])).to.be.eq('user');
});
it('should return `no-role` if no one exists', () => {
expect(getMostImportantRole([])).to.be.eq('no-role');
expect(getMostImportantRole()).to.be.eq('no-role');
});
it('should return correct role', () => {
expect(getMostImportantRole(['user', 'admin'])).to.be.eq('admin');
expect(getMostImportantRole(['user', 'anonymous'])).to.be.eq('user');
expect(getMostImportantRole(['user', 'guest'])).to.be.eq('user');
expect(getMostImportantRole(['user', 'guest', 'livechat-monitor'])).to.be.eq('livechat-monitor');
expect(getMostImportantRole(['user', 'custom1'])).to.be.eq('custom-role');
expect(getMostImportantRole(['custom2', 'user', 'custom1'])).to.be.eq('custom-role');
expect(getMostImportantRole(['custom2', 'admin', 'custom1'])).to.be.eq('admin');
expect(getMostImportantRole(['custom2', 'app'])).to.be.eq('custom-role');
expect(getMostImportantRole(['anonymous', 'app'])).to.be.eq('app');
});
});

@ -207,4 +207,5 @@ import './v207';
import './v208';
import './v209';
import './v210';
import './v211';
import './xrun';

@ -0,0 +1,41 @@
import { Promise } from 'meteor/promise';
import { Migrations } from '../../../app/migrations';
import { Sessions } from '../../../app/models/server/raw';
import { getMostImportantRole } from '../../../app/statistics/server/lib/getMostImportantRole';
async function migrateSessions() {
const cursor = Sessions.col.aggregate([{
$match: { $or: [{ mostImportantRole: { $exists: 0 } }, { mostImportantRole: null }] },
}, {
$group: {
_id: '$userId',
},
}, {
$lookup: {
from: 'users',
localField: '_id',
foreignField: '_id',
as: 'user',
},
}, {
$unwind: '$user',
}, {
$project: {
'user.roles': 1,
},
}, {
$match: { 'user.roles.0': { $exists: 1 } },
}]);
for await (const session of cursor) {
await Sessions.col.updateMany({ userId: session._id }, { $set: { mostImportantRole: getMostImportantRole(session.user.roles) } });
}
}
Migrations.add({
version: 211,
up() {
Promise.await(migrateSessions());
},
});
Loading…
Cancel
Save