diff options
| author | bobzel <zzzman@gmail.com> | 2024-04-17 12:27:21 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2024-04-17 12:27:21 -0400 |
| commit | 2a313f28fcb8675223708b0657de7517a3281095 (patch) | |
| tree | ed6db226cc7d323aee378eddee43dc5f3bdb1ef9 /src/server/DashStats.ts | |
| parent | 62937027183dc8acf14e489fbb4590aff6fce2cd (diff) | |
restoring eslint - updates not complete yet
Diffstat (limited to 'src/server/DashStats.ts')
| -rw-r--r-- | src/server/DashStats.ts | 241 |
1 files changed, 117 insertions, 124 deletions
diff --git a/src/server/DashStats.ts b/src/server/DashStats.ts index a9e6af67c..485ab9f99 100644 --- a/src/server/DashStats.ts +++ b/src/server/DashStats.ts @@ -1,9 +1,8 @@ import { cyan, magenta } from 'colors'; import { Response } from 'express'; -import SocketIO from 'socket.io'; -import { timeMap } from './ApiManagers/UserManager'; -import { WebSocket } from './websocket'; import * as fs from 'fs'; +import SocketIO from 'socket.io'; +import { socketMap, timeMap, userOperations } from './SocketData'; /** * DashStats focuses on tracking user data for each session. @@ -17,7 +16,6 @@ export namespace DashStats { const statsCSVDirectory = './src/server/stats/'; const statsCSVFilename = statsCSVDirectory + 'userLoginStats.csv'; - const columns = ['USERNAME', 'ACTION', 'TIME']; /** * UserStats holds the stats associated with a particular user. @@ -78,14 +76,118 @@ export namespace DashStats { export const lastUserOperations = new Map<string, UserLastOperations>(); /** + * convertToCSV() is a helper method that stringifies a CSVStore object + * that can be written to the CSV file later. + * @param dataObject the object to stringify + * @returns the object as a string. + */ + function convertToCSV(dataObject: CSVStore): string { + return `${dataObject.USERNAME},${dataObject.ACTION},${dataObject.TIME}\n`; + } + /** + * getLastOperationsOrDefault() is a helper method that will attempt + * to query the lastUserOperations map for a specified username. If the + * username is not in the map, an empty UserLastOperations object is returned. + * @param username + * @returns the user's UserLastOperations structure or an empty + * UserLastOperations object (All values set to 0) if the username is not found. + */ + function getLastOperationsOrDefault(username: string): UserLastOperations { + if (lastUserOperations.get(username) === undefined) { + const initializeOperationsQueue = []; + for (let i = 0; i < RATE_INTERVAL; i++) { + initializeOperationsQueue.push(0); + } + return { + sampleOperations: 0, + lastSampleOperations: 0, + previousOperationsQueue: initializeOperationsQueue, + }; + } + return lastUserOperations.get(username)!; + } + + /** + * updateLastOperations updates a specific user's UserLastOperations information + * for the current sampling cycle. The method removes old/outdated counts for + * operations from the queue and adds new data for the current sampling + * cycle to the queue, updating the total count as it goes. + * @param lastOperationData the old UserLastOperations data that must be updated + * @param currentOperations the total number of operations measured for this sampling cycle. + * @returns the udpated UserLastOperations structure. + */ + function updateLastOperations(lastOperationData: UserLastOperations, currentOperations: number): UserLastOperations { + // create a copy of the UserLastOperations to modify + const newLastOperationData: UserLastOperations = { + sampleOperations: lastOperationData.sampleOperations, + lastSampleOperations: lastOperationData.lastSampleOperations, + previousOperationsQueue: lastOperationData.previousOperationsQueue.slice(), + }; + + let newSampleOperations = newLastOperationData.sampleOperations; + newSampleOperations -= newLastOperationData.previousOperationsQueue.shift()!; // removes and returns the first element of the queue + const operationsThisCycle = currentOperations - lastOperationData.lastSampleOperations; + newSampleOperations += operationsThisCycle; // add the operations this cycle to find out what our count for the interval should be (e.g operations in the last 10 seconds) + + // update values for the copy object + newLastOperationData.sampleOperations = newSampleOperations; + + newLastOperationData.previousOperationsQueue.push(operationsThisCycle); + newLastOperationData.lastSampleOperations = currentOperations; + + return newLastOperationData; + } + + /** + * getUserOperationsOrDefault() is a helper method to get the user's total + * operations for the CURRENT sampling interval. The method will return 0 + * if the username is not in the userOperations map. + * @param username the username to search the map for + * @returns the total number of operations recorded up to this sampling cycle. + */ + function getUserOperationsOrDefault(username: string): number { + return userOperations.get(username) === undefined ? 0 : userOperations.get(username)!; + } + + /** + * getCurrentStats() calculates the total stats for this cycle. In this case, + * getCurrentStats() returns an Array of UserStats[] objects describing + * the stats for each user + * @returns an array of UserStats storing data for each user at the current moment. + */ + function getCurrentStats(): UserStats[] { + const socketPairs: UserStats[] = []; + Array.from(socketMap.entries()).forEach(([key, value]) => { + const username = value.split(' ')[0]; + const connectionTime = new Date(timeMap[username]); + + const connectionTimeString = connectionTime.toLocaleDateString() + ' ' + connectionTime.toLocaleTimeString(); + + if (!key.disconnected) { + const lastRecordedOperations = getLastOperationsOrDefault(username); + const currentUserOperationCount = getUserOperationsOrDefault(username); + + socketPairs.push({ + socketId: key.id, + username: username, + time: connectionTimeString.includes('Invalid Date') ? '' : connectionTimeString, + operations: userOperations.get(username) ? userOperations.get(username)! : 0, + rate: lastRecordedOperations.sampleOperations, + }); + lastUserOperations.set(username, updateLastOperations(lastRecordedOperations, currentUserOperationCount)); + } + }); + return socketPairs; + } + + /** * handleStats is called when the /stats route is called, providing a JSON * object with relevant stats. In this case, we return the number of * current connections and * @param res Response object from Express */ export function handleStats(res: Response) { - let current = getCurrentStats(); - const results: CSVStore[] = []; + const current = getCurrentStats(); res.json({ currentConnections: current.length, socketMap: current, @@ -99,7 +201,7 @@ export namespace DashStats { * @returns a StatsDataBundle that is sent to the frontend view on each websocket update */ export function getUpdatedStatsBundle(): StatsDataBundle { - let current = getCurrentStats(); + const current = getCurrentStats(); return { connectedUsers: current, @@ -113,11 +215,8 @@ export namespace DashStats { * @param res */ export function handleStatsView(res: Response) { - let current = getCurrentStats(); - - let connectedUsers = current.map(socketPair => { - return socketPair.time + ' - ' + socketPair.username + ' Operations: ' + socketPair.operations; - }); + const current = getCurrentStats(); + const connectedUsers = current.map(({ time, username, operations }) => time + ' - ' + username + ' Operations: ' + operations); let serverTraffic = ServerTraffic.NOT_BUSY; if (current.length < BUSY_SERVER_BOUND) { @@ -145,17 +244,17 @@ export namespace DashStats { */ export function logUserLogin(username: string | undefined, socket: SocketIO.Socket) { if (!(username === undefined)) { - let currentDate = new Date(); + const currentDate = new Date(); console.log(magenta(`User ${username.split(' ')[0]} logged in at: ${currentDate.toISOString()}`)); - let toWrite: CSVStore = { + const toWrite: CSVStore = { USERNAME: username, ACTION: 'loggedIn', TIME: currentDate.toISOString(), }; if (!fs.existsSync(statsCSVDirectory)) fs.mkdirSync(statsCSVDirectory); - let statsFile = fs.createWriteStream(statsCSVFilename, { flags: 'a' }); + const statsFile = fs.createWriteStream(statsCSVFilename, { flags: 'a' }); statsFile.write(convertToCSV(toWrite)); statsFile.end(); console.log(cyan(convertToCSV(toWrite))); @@ -170,10 +269,10 @@ export namespace DashStats { */ export function logUserLogout(username: string | undefined, socket: SocketIO.Socket) { if (!(username === undefined)) { - let currentDate = new Date(); + const currentDate = new Date(); - let statsFile = fs.createWriteStream(statsCSVFilename, { flags: 'a' }); - let toWrite: CSVStore = { + const statsFile = fs.createWriteStream(statsCSVFilename, { flags: 'a' }); + const toWrite: CSVStore = { USERNAME: username, ACTION: 'loggedOut', TIME: currentDate.toISOString(), @@ -182,110 +281,4 @@ export namespace DashStats { statsFile.end(); } } - - /** - * getLastOperationsOrDefault() is a helper method that will attempt - * to query the lastUserOperations map for a specified username. If the - * username is not in the map, an empty UserLastOperations object is returned. - * @param username - * @returns the user's UserLastOperations structure or an empty - * UserLastOperations object (All values set to 0) if the username is not found. - */ - function getLastOperationsOrDefault(username: string): UserLastOperations { - if (lastUserOperations.get(username) === undefined) { - let initializeOperationsQueue = []; - for (let i = 0; i < RATE_INTERVAL; i++) { - initializeOperationsQueue.push(0); - } - return { - sampleOperations: 0, - lastSampleOperations: 0, - previousOperationsQueue: initializeOperationsQueue, - }; - } - return lastUserOperations.get(username)!; - } - - /** - * updateLastOperations updates a specific user's UserLastOperations information - * for the current sampling cycle. The method removes old/outdated counts for - * operations from the queue and adds new data for the current sampling - * cycle to the queue, updating the total count as it goes. - * @param lastOperationData the old UserLastOperations data that must be updated - * @param currentOperations the total number of operations measured for this sampling cycle. - * @returns the udpated UserLastOperations structure. - */ - function updateLastOperations(lastOperationData: UserLastOperations, currentOperations: number): UserLastOperations { - // create a copy of the UserLastOperations to modify - let newLastOperationData: UserLastOperations = { - sampleOperations: lastOperationData.sampleOperations, - lastSampleOperations: lastOperationData.lastSampleOperations, - previousOperationsQueue: lastOperationData.previousOperationsQueue.slice(), - }; - - let newSampleOperations = newLastOperationData.sampleOperations; - newSampleOperations -= newLastOperationData.previousOperationsQueue.shift()!; // removes and returns the first element of the queue - let operationsThisCycle = currentOperations - lastOperationData.lastSampleOperations; - newSampleOperations += operationsThisCycle; // add the operations this cycle to find out what our count for the interval should be (e.g operations in the last 10 seconds) - - // update values for the copy object - newLastOperationData.sampleOperations = newSampleOperations; - - newLastOperationData.previousOperationsQueue.push(operationsThisCycle); - newLastOperationData.lastSampleOperations = currentOperations; - - return newLastOperationData; - } - - /** - * getUserOperationsOrDefault() is a helper method to get the user's total - * operations for the CURRENT sampling interval. The method will return 0 - * if the username is not in the userOperations map. - * @param username the username to search the map for - * @returns the total number of operations recorded up to this sampling cycle. - */ - function getUserOperationsOrDefault(username: string): number { - return WebSocket.userOperations.get(username) === undefined ? 0 : WebSocket.userOperations.get(username)!; - } - - /** - * getCurrentStats() calculates the total stats for this cycle. In this case, - * getCurrentStats() returns an Array of UserStats[] objects describing - * the stats for each user - * @returns an array of UserStats storing data for each user at the current moment. - */ - function getCurrentStats(): UserStats[] { - let socketPairs: UserStats[] = []; - for (let [key, value] of WebSocket.socketMap) { - let username = value.split(' ')[0]; - let connectionTime = new Date(timeMap[username]); - - let connectionTimeString = connectionTime.toLocaleDateString() + ' ' + connectionTime.toLocaleTimeString(); - - if (!key.disconnected) { - let lastRecordedOperations = getLastOperationsOrDefault(username); - let currentUserOperationCount = getUserOperationsOrDefault(username); - - socketPairs.push({ - socketId: key.id, - username: username, - time: connectionTimeString.includes('Invalid Date') ? '' : connectionTimeString, - operations: WebSocket.userOperations.get(username) ? WebSocket.userOperations.get(username)! : 0, - rate: lastRecordedOperations.sampleOperations, - }); - lastUserOperations.set(username, updateLastOperations(lastRecordedOperations, currentUserOperationCount)); - } - } - return socketPairs; - } - - /** - * convertToCSV() is a helper method that stringifies a CSVStore object - * that can be written to the CSV file later. - * @param dataObject the object to stringify - * @returns the object as a string. - */ - function convertToCSV(dataObject: CSVStore): string { - return `${dataObject.USERNAME},${dataObject.ACTION},${dataObject.TIME}\n`; - } } |
