Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Convert LivechatAgentActivity to raw model and TS #25123

Merged
merged 4 commits into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@ import { Meteor } from 'meteor/meteor';
import { SyncedCron } from 'meteor/littledata:synced-cron';

import { callbacks } from '../../../../lib/callbacks';
import { LivechatAgentActivity, Users } from '../../../models/server';
import { Sessions } from '../../../models/server/raw';
import { Users } from '../../../models/server';
import { LivechatAgentActivity, Sessions } from '../../../models/server/raw';
import { ISocketConnection } from '../../../../definition/ISocketConnection';

const formatDate = (dateTime = new Date()) => ({
const formatDate = (dateTime = new Date()): { date: number } => ({
date: parseInt(moment(dateTime).format('YYYYMMDD')),
});

export class LivechatAgentActivityMonitor {
private _started: boolean;

private _name: string;

constructor() {
this._started = false;
this._handleAgentStatusChanged = this._handleAgentStatusChanged.bind(this);
this._handleUserStatusLivechatChanged = this._handleUserStatusLivechatChanged.bind(this);
this._name = 'Livechat Agent Activity Monitor';
}

start() {
start(): void {
this._setupListeners();
}

stop() {
stop(): void {
if (!this.isRunning()) {
return;
}
Expand All @@ -32,68 +37,70 @@ export class LivechatAgentActivityMonitor {
this._started = false;
}

isRunning() {
isRunning(): boolean {
return this._started;
}

_setupListeners() {
_setupListeners(): void {
if (this.isRunning()) {
return;
}
this._startMonitoring();
Meteor.onConnection((connection) => this._handleMeteorConnection(connection));

// TODO use service event socket.connected instead
Meteor.onConnection((connection: unknown) => this._handleMeteorConnection(connection as ISocketConnection));
callbacks.add('livechat.agentStatusChanged', this._handleAgentStatusChanged);
callbacks.add('livechat.setUserStatusLivechat', this._handleUserStatusLivechatChanged);
this._started = true;
}

_startMonitoring() {
_startMonitoring(): void {
SyncedCron.add({
name: this._name,
schedule: (parser) => parser.cron('0 0 * * *'),
schedule: (parser: any) => parser.cron('0 0 * * *'),
job: () => {
this._updateActiveSessions();
Promise.await(this._updateActiveSessions());
},
});
}

_updateActiveSessions() {
const openLivechatAgentSessions = LivechatAgentActivity.findOpenSessions().fetch();
if (!openLivechatAgentSessions.length) {
async _updateActiveSessions(): Promise<void> {
const openLivechatAgentSessions = await LivechatAgentActivity.findOpenSessions();
if (!(await openLivechatAgentSessions.count())) {
return;
}
const today = moment(new Date());
const startedAt = new Date(today.year(), today.month(), today.date());
for (const session of openLivechatAgentSessions) {
for await (const session of openLivechatAgentSessions) {
const startDate = moment(session.lastStartedAt);
const stoppedAt = new Date(startDate.year(), startDate.month(), startDate.date(), 23, 59, 59);
const data = { ...formatDate(startDate.toDate()), agentId: session.agentId };
const availableTime = moment(stoppedAt).diff(moment(new Date(session.lastStartedAt)), 'seconds');
LivechatAgentActivity.updateLastStoppedAt({
await LivechatAgentActivity.updateLastStoppedAt({
...data,
availableTime,
lastStoppedAt: stoppedAt,
});
LivechatAgentActivity.updateServiceHistory({
await LivechatAgentActivity.updateServiceHistory({
...data,
serviceHistory: { startedAt: session.lastStartedAt, stoppedAt },
});
this._createOrUpdateSession(session.agentId, startedAt);
await this._createOrUpdateSession(session.agentId, startedAt);
}
}

async _handleMeteorConnection(connection) {
async _handleMeteorConnection(connection: ISocketConnection): Promise<void> {
if (!this.isRunning()) {
return;
}

const session = await Sessions.findOne({ sessionId: connection.id });
const session = await Sessions.findOneBySessionId(connection.id);
if (!session) {
return;
}
const user = Users.findOneById(session.userId);
if (user && user.status !== 'offline' && user.statusLivechat === 'available') {
this._createOrUpdateSession(user._id);
await this._createOrUpdateSession(user._id);
}
connection.onClose(() => {
if (session) {
Expand All @@ -102,7 +109,7 @@ export class LivechatAgentActivityMonitor {
});
}

_handleAgentStatusChanged({ userId, status }) {
_handleAgentStatusChanged({ userId, status }: { userId: string; status: string }): void {
if (!this.isRunning()) {
return;
}
Expand All @@ -119,7 +126,7 @@ export class LivechatAgentActivityMonitor {
}
}

_handleUserStatusLivechatChanged({ userId, status }) {
async _handleUserStatusLivechatChanged({ userId, status }: { userId: string; status: string }): Promise<void> {
if (!this.isRunning()) {
return;
}
Expand All @@ -130,33 +137,39 @@ export class LivechatAgentActivityMonitor {
}

if (status === 'available') {
this._createOrUpdateSession(userId);
await this._createOrUpdateSession(userId);
}
if (status === 'not-available') {
this._updateSessionWhenAgentStop(userId);
}
}

_createOrUpdateSession(userId, lastStartedAt) {
async _createOrUpdateSession(userId: string, lastStartedAt?: Date): Promise<void> {
const data = { ...formatDate(lastStartedAt), agentId: userId, lastStartedAt };
LivechatAgentActivity.createOrUpdate(data);
await LivechatAgentActivity.createOrUpdate(data);
}

_updateSessionWhenAgentStop(userId) {
const data = { ...formatDate(), agentId: userId };
const livechatSession = LivechatAgentActivity.findOne(data);
if (livechatSession) {
const stoppedAt = new Date();
const availableTime = moment(stoppedAt).diff(moment(new Date(livechatSession.lastStartedAt)), 'seconds');
LivechatAgentActivity.updateLastStoppedAt({
...data,
availableTime,
lastStoppedAt: stoppedAt,
});
LivechatAgentActivity.updateServiceHistory({
...data,
serviceHistory: { startedAt: livechatSession.lastStartedAt, stoppedAt },
});
async _updateSessionWhenAgentStop(agentId: string): Promise<void> {
const { date } = formatDate();

const livechatSession = await LivechatAgentActivity.findOneByAgendIdAndDate(agentId, date);
if (!livechatSession) {
return;
}

const stoppedAt = new Date();
const availableTime = moment(stoppedAt).diff(moment(new Date(livechatSession.lastStartedAt)), 'seconds');

await LivechatAgentActivity.updateLastStoppedAt({
agentId,
date,
availableTime,
lastStoppedAt: stoppedAt,
});
await LivechatAgentActivity.updateServiceHistory({
agentId,
date,
serviceHistory: { startedAt: livechatSession.lastStartedAt, stoppedAt },
});
}
}
2 changes: 0 additions & 2 deletions apps/meteor/app/models/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import LivechatDepartment from './models/LivechatDepartment';
import LivechatDepartmentAgents from './models/LivechatDepartmentAgents';
import LivechatRooms from './models/LivechatRooms';
import LivechatVisitors from './models/LivechatVisitors';
import LivechatAgentActivity from './models/LivechatAgentActivity';
import LivechatInquiry from './models/LivechatInquiry';
import LivechatExternalMessage from './models/LivechatExternalMessages';
import OmnichannelQueue from './models/OmnichannelQueue';
Expand All @@ -36,7 +35,6 @@ export {
LivechatDepartmentAgents,
LivechatRooms,
LivechatVisitors,
LivechatAgentActivity,
LivechatExternalMessage,
LivechatInquiry,
OmnichannelQueue,
Expand Down
10 changes: 0 additions & 10 deletions apps/meteor/app/models/server/models/EmailMessageHistory.js

This file was deleted.

72 changes: 0 additions & 72 deletions apps/meteor/app/models/server/models/LivechatAgentActivity.js

This file was deleted.

82 changes: 80 additions & 2 deletions apps/meteor/app/models/server/raw/LivechatAgentActivity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,88 @@
import moment from 'moment';
import { AggregationCursor } from 'mongodb';
import type { AggregationCursor, Cursor, FindAndModifyWriteOpResultObject, IndexSpecification, UpdateWriteOpResult } from 'mongodb';

import { ILivechatAgentActivity, IServiceHistory } from '../../../../definition/ILivechatAgentActivity';
import { BaseRaw } from './BaseRaw';
import { ILivechatAgentActivity } from '../../../../definition/ILivechatAgentActivity';

export class LivechatAgentActivityRaw extends BaseRaw<ILivechatAgentActivity> {
modelIndexes(): IndexSpecification[] {
return [{ key: { date: 1 } }, { key: { agentId: 1, date: 1 }, unique: true }];
}

findOneByAgendIdAndDate(agentId: string, date: ILivechatAgentActivity['date']): Promise<ILivechatAgentActivity | null> {
return this.findOne({ agentId, date });
}

async createOrUpdate(
data: Partial<Pick<ILivechatAgentActivity, 'date' | 'agentId' | 'lastStartedAt'>> = {},
): Promise<FindAndModifyWriteOpResultObject<ILivechatAgentActivity> | undefined> {
const { date, agentId, lastStartedAt } = data;

if (!date || !agentId) {
return;
}

return this.findOneAndUpdate(
{ agentId, date },
{
$unset: {
lastStoppedAt: 1,
},
$set: {
lastStartedAt: lastStartedAt || new Date(),
},
$setOnInsert: {
date,
agentId,
},
},
);
}

updateLastStoppedAt({
agentId,
date,
lastStoppedAt,
availableTime,
}: Pick<ILivechatAgentActivity, 'date' | 'agentId' | 'lastStoppedAt' | 'availableTime'>): Promise<UpdateWriteOpResult> {
const query = {
agentId,
date,
};
const update = {
$inc: { availableTime },
$set: {
lastStoppedAt,
},
};
return this.updateMany(query, update);
}

updateServiceHistory({
agentId,
date,
serviceHistory,
}: Pick<ILivechatAgentActivity, 'date' | 'agentId'> & { serviceHistory: IServiceHistory }): Promise<UpdateWriteOpResult> {
const query = {
agentId,
date,
};
const update = {
$addToSet: {
serviceHistory,
},
};
return this.updateMany(query, update);
}

findOpenSessions(): Cursor<ILivechatAgentActivity> {
const query = {
lastStoppedAt: { $exists: false },
};

return this.find(query);
}

findAllAverageAvailableServiceTime({ date, departmentId }: { date: Date; departmentId: string }): Promise<ILivechatAgentActivity[]> {
const match = { $match: { date } };
const lookup = {
Expand Down
Loading