aboutsummaryrefslogtreecommitdiff
path: root/src/server/DashStats.ts
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-04-17 12:27:21 -0400
committerbobzel <zzzman@gmail.com>2024-04-17 12:27:21 -0400
commit2a313f28fcb8675223708b0657de7517a3281095 (patch)
treeed6db226cc7d323aee378eddee43dc5f3bdb1ef9 /src/server/DashStats.ts
parent62937027183dc8acf14e489fbb4590aff6fce2cd (diff)
restoring eslint - updates not complete yet
Diffstat (limited to 'src/server/DashStats.ts')
-rw-r--r--src/server/DashStats.ts241
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`;
- }
}