diff options
-rw-r--r-- | src/server/Client.ts | 13 | ||||
-rw-r--r-- | src/server/DashStats.ts | 198 | ||||
-rw-r--r-- | src/server/Message.ts | 2 | ||||
-rw-r--r-- | src/server/stats/userLoginStats.csv | 82 | ||||
-rw-r--r-- | src/server/websocket.ts | 23 | ||||
-rw-r--r-- | views/resources/statsviewcontroller.js | 114 | ||||
-rw-r--r-- | views/stats.pug | 18 |
7 files changed, 320 insertions, 130 deletions
diff --git a/src/server/Client.ts b/src/server/Client.ts index be1ffc2ba..e6f953712 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -1,22 +1,11 @@ -import { action, computed } from "mobx"; +import { computed } from "mobx"; export class Client { private _guid: string; - // private operations: number; constructor(guid: string) { this._guid = guid; - // this.operations = 0; } - // @computed public get OPERATIONS(): number { - // return this.operations; - // } - - // @action - // public setOperations(newOperations: number): void { - // this.operations = newOperations; - // } - @computed public get GUID(): string { return this._guid; } }
\ No newline at end of file diff --git a/src/server/DashStats.ts b/src/server/DashStats.ts index a10b28608..8d341db63 100644 --- a/src/server/DashStats.ts +++ b/src/server/DashStats.ts @@ -1,35 +1,72 @@ import { cyan, magenta } from 'colors'; -import { Request, Response } from 'express'; -import { Server } from 'http'; +import { Response } from 'express'; import SocketIO from 'socket.io'; import { timeMap } from './ApiManagers/UserManager'; import { WebSocket } from './websocket'; const fs = require('fs'); -const csv = require('csv-parser'); +/** + * DashStats focuses on tracking user data for each session. + * + * This includes time connected, number of operations, and + * the rate of their operations + */ export namespace DashStats { + export const SAMPLING_INTERVAL = 1000; // in milliseconds (ms) - Time interval to update the frontend. + export const RATE_INTERVAL = 10; // in seconds (s) - Used to calculate rate + const statsCSVFilename = './src/server/stats/userLoginStats.csv'; const columns = ['USERNAME', 'ACTION', 'TIME']; - interface SocketPair { + /** + * UserStats holds the stats associated with a particular user. + */ + interface UserStats { socketId: string; username: string; time: string; operations: number; + rate: number; + } + + /** + * UserLastOperations is the queue object for each user + * storing their past operations. + */ + interface UserLastOperations { + sampleOperations: number; // stores how many operations total are in this rate section (10 sec, for example) + lastSampleOperations: number; // stores how many total operations were recorded at the last sample + previousOperationsQueue: number[]; // stores the operations to calculate rate. } + /** + * StatsDataBundle represents an object that will be sent to the frontend view + * on each websocket update. + */ + interface StatsDataBundle { + connectedUsers: UserStats[]; + } + + /** + * CSVStore represents how objects will be stored in the CSV + */ interface CSVStore { USERNAME: string; ACTION: string; TIME: string; } + /** + * ServerTraffic describes the current traffic going to the backend. + */ enum ServerTraffic { NOT_BUSY, BUSY, VERY_BUSY } + // These values can be changed after further testing how many + // users correspond to each traffic level in Dash. const BUSY_SERVER_BOUND = 2; const VERY_BUSY_SERVER_BOUND = 3; @@ -39,31 +76,47 @@ export namespace DashStats { "Very Busy" ] + // lastUserOperations maps each username to a UserLastOperations + // structure + export const lastUserOperations = new Map<string, UserLastOperations>(); + + /** + * 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 = getCurrentConnections(); + let current = getCurrentStats(); const results: CSVStore[] = []; res.json({ - message: 'welcome to stats', currentConnections: current.length, socketMap: current, }); + } + + /** + * getUpdatedStatesBundle() sends an updated copy of the current stats to the + * frontend /statsview route via websockets. + * + * @returns a StatsDataBundle that is sent to the frontend view on each websocket update + */ + export function getUpdatedStatsBundle(): StatsDataBundle { + let current = getCurrentStats(); - // fs.createReadStream(statsCSVFilename) - // .pipe(csv()) - // .on('data', (data: any) => results.push(data)) - // .on('end', () => { - // console.log(results); - // res.json({ - // message: 'welcome to stats', - // currentConnections: current.length, - // socketMap: current, - // results: results, - // }); - // }); + return { + connectedUsers: current, + } } + /** + * handleStatsView() is called when the /statsview route is called. This + * will use pug to render a frontend view of the current stats + * + * @param res + */ export function handleStatsView(res: Response) { - let current = getCurrentConnections(); + let current = getCurrentStats(); let connectedUsers = current.map((socketPair) => { return socketPair.time + " - " + socketPair.username + " Operations: " + socketPair.operations; @@ -80,17 +133,23 @@ export namespace DashStats { res.render("stats.pug", { title: "Dash Stats", - numConnections: current.length, + numConnections: connectedUsers.length, serverTraffic: serverTraffic, serverTrafficMessage : serverTrafficMessages[serverTraffic], connectedUsers: connectedUsers }); } + /** + * logUserLogin() writes a login event to the CSV file. + * + * @param username the username in the format of "username@domain.com logged in" + * @param socket the websocket associated with the current connection + */ export function logUserLogin(username: string | undefined, socket: SocketIO.Socket) { if (!(username === undefined)) { let currentDate = new Date(); - // console.log(magenta(`User ${username.split(' ')[0]} logged in at: ${currentDate.toISOString()}`)); + console.log(magenta(`User ${username.split(' ')[0]} logged in at: ${currentDate.toISOString()}`)); let toWrite: CSVStore = { USERNAME : username, @@ -105,10 +164,15 @@ export namespace DashStats { } } + /** + * logUserLogout() writes a logout event to the CSV file. + * + * @param username the username in the format of "username@domain.com logged in" + * @param socket the websocket associated with the current connection. + */ export function logUserLogout(username: string | undefined, socket: SocketIO.Socket) { if (!(username === undefined)) { let currentDate = new Date(); - // console.log(magenta(`User ${username.split(' ')[0]} logged out at: ${currentDate.toISOString()}`)); let statsFile = fs.createWriteStream(statsCSVFilename, { flags: "a"}); let toWrite: CSVStore = { @@ -121,10 +185,79 @@ export namespace DashStats { } } - function getCurrentConnections(): SocketPair[] { - console.log("timeMap: " + timeMap); - console.log("clients:" + WebSocket.clients); - let socketPairs: SocketPair[] = []; + /** + * 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]); @@ -132,19 +265,28 @@ export namespace DashStats { 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)); } } - console.log(socketPairs); - // console.log([...WebSocket.clients.entries()]); 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`; } diff --git a/src/server/Message.ts b/src/server/Message.ts index d87ae5027..8f0af08bc 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -94,4 +94,6 @@ export namespace MessageStore { export const YoutubeApiQuery = new Message<YoutubeQueryInput>("Youtube Api Query"); export const DeleteField = new Message<string>("Delete field"); export const DeleteFields = new Message<string[]>("Delete fields"); + + export const UpdateStats = new Message<string>("updatestats"); } diff --git a/src/server/stats/userLoginStats.csv b/src/server/stats/userLoginStats.csv index d5e56502c..23bcef885 100644 --- a/src/server/stats/userLoginStats.csv +++ b/src/server/stats/userLoginStats.csv @@ -1,75 +1,7 @@ -USER,ACTION,TIME -aaa@gmail.com,loggedIn,2023-03-18T20:10:29.928Z -guest,loggedIn,2023-03-18T20:10:47.384Z -aaa@gmail.com,loggedOut,2023-03-18T20:10:55.364Z -aaa@gmail.com,loggedIn,2023-03-18T20:11:15.879Z -guest,loggedIn,2023-03-18T20:18:03.724Z -aaa@gmail.com,loggedIn,2023-03-18T20:18:03.860Z -guest,loggedOut,2023-03-18T20:19:00.211Z -guest,loggedIn,2023-03-18T20:19:03.087Z -guest,loggedOut,2023-03-18T20:19:50.668Z -boo15869@gmail.com,loggedIn,2023-03-18T20:20:17.890Z -boo15869@gmail.com,loggedOut,2023-03-18T20:21:03.542Z -boo15869@gmail.com,loggedIn,2023-03-18T20:21:06.149Z -boo15869@gmail.com,loggedOut,2023-03-18T20:21:51.874Z -a@gmail.com,loggedIn,2023-03-18T20:22:02.122Z -aaa@gmail.com,loggedOut,2023-03-18T20:22:42.882Z -aaa@gmail.com,loggedIn,2023-03-18T20:22:45.631Z -aaa@gmail.com,loggedIn,2023-03-18T20:25:34.658Z -a@gmail.com,loggedIn,2023-03-18T20:25:34.681Z -aaa@gmail.com,loggedIn,2023-03-18T20:29:04.297Z -a@gmail.com,loggedIn,2023-03-18T20:29:08.701Z -a@gmail.com,loggedIn,2023-03-18T20:29:18.565Z -aaa@gmail.com,loggedIn,2023-03-18T20:29:21.974Z -aaa@gmail.com,loggedIn,2023-03-18T20:29:51.477Z -a@gmail.com,loggedIn,2023-03-18T20:29:51.489Z -aaa@gmail.com,loggedIn,2023-03-18T20:30:15.011Z -aaa@gmail.com,loggedIn,2023-03-18T20:30:57.818Z -a@gmail.com,loggedIn,2023-03-18T20:30:57.838Z -aaa@gmail.com,loggedIn,2023-03-18T20:31:12.061Z -a@gmail.com,loggedIn,2023-03-18T20:31:12.080Z -a@gmail.com,loggedIn,2023-03-18T20:31:19.447Z -aaa@gmail.com,loggedIn,2023-03-18T20:31:22.738Z -aaa@gmail.com,loggedIn,2023-03-18T20:32:36.919Z -a@gmail.com,loggedIn,2023-03-18T20:32:36.929Z -a@gmail.com,loggedIn,2023-03-18T20:32:56.212Z -aaa@gmail.com,loggedIn,2023-03-18T20:32:59.300Z -aaa@gmail.com,loggedIn,2023-03-18T20:34:27.543Z -a@gmail.com,loggedIn,2023-03-18T20:34:27.570Z -a@gmail.com,loggedIn,2023-03-18T20:34:35.299Z -aaa@gmail.com,loggedIn,2023-03-18T20:34:35.302Z -a@gmail.com,loggedIn,2023-03-18T20:34:51.579Z -aaa@gmail.com,loggedIn,2023-03-18T20:34:52.392Z -a@gmail.com,loggedIn,2023-03-18T20:35:08.509Z -aaa@gmail.com,loggedIn,2023-03-18T20:35:15.202Z -a@gmail.com,loggedIn,2023-03-18T20:36:46.796Z -aaa@gmail.com,loggedIn,2023-03-18T20:36:51.756Z -a@gmail.com,loggedIn,2023-03-18T20:36:55.286Z -a@gmail.com,loggedIn,2023-03-18T20:40:06.226Z -a@gmail.com,loggedIn,2023-03-18T20:40:18.474Z -aaa@gmail.com,loggedIn,2023-03-18T20:40:31.894Z -a@gmail.com,loggedIn,2023-03-18T20:40:31.903Z -a@gmail.com,loggedIn,2023-03-18T20:42:25.301Z -aaa@gmail.com,loggedIn,2023-03-18T20:42:31.182Z -aaa@gmail.com,loggedOut,2023-03-18T20:43:05.741Z -aaa@gmail.com,loggedIn,2023-03-18T20:43:09.203Z -a@gmail.com,loggedOut,2023-03-18T20:45:24.343Z -a@gmail.com,loggedIn,2023-03-18T20:45:27.207Z -aaa@gmail.com,loggedIn,2023-03-18T20:46:15.618Z -a@gmail.com,loggedIn,2023-03-18T20:46:56.163Z -a@gmail.com,loggedOut,2023-03-18T20:48:37.464Z -a@gmail.com,loggedIn,2023-03-18T20:48:40.562Z -a@gmail.com,loggedOut,2023-03-18T20:50:32.478Z -a@gmail.com,loggedIn,2023-03-18T20:50:40.384Z -a@gmail.com,loggedOut,2023-03-18T20:51:34.159Z -a@gmail.com,loggedIn,2023-03-18T20:51:49.206Z -a@gmail.com,loggedOut,2023-03-18T20:52:04.673Z -qw@gmail.com,loggedIn,2023-03-18T20:52:36.270Z -qw@gmail.com,loggedIn,2023-03-18T20:53:58.175Z -aaa@gmail.com,loggedIn,2023-03-18T20:53:58.204Z -aaa@gmail.com,loggedIn,2023-03-18T20:54:20.518Z -qw@gmail.com,loggedIn,2023-03-18T20:54:24.225Z -qw@gmail.com,loggedIn,2023-03-18T20:54:37.007Z -aaa@gmail.com,loggedIn,2023-03-18T20:54:39.959Z -aaa@gmail.com,loggedOut,2023-03-18T20:55:55.199Z -qw@gmail.com,loggedOut,2023-03-18T20:55:59.010Z +USER,ACTION,TIMEaaa@gmail.com,loggedIn,2023-04-08T20:08:17.533Z +aaa@gmail.com,loggedIn,2023-04-08T20:14:32.460Z +aaa@gmail.com,loggedIn,2023-04-08T20:14:44.884Z +aaa@gmail.com,loggedIn,2023-04-08T20:14:56.854Z +aaa@gmail.com,loggedIn,2023-04-08T20:16:59.747Z +aaa@gmail.com,loggedIn,2023-04-08T20:56:50.759Z +aaa@gmail.com,loggedIn,2023-04-08T20:56:58.175Z diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 54944e944..5d1390d37 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -1,4 +1,4 @@ -import { blue, magenta, yellow } from 'colors'; +import { blue } from 'colors'; import * as express from 'express'; import { createServer, Server } from 'https'; import { networkInterfaces } from 'os'; @@ -22,7 +22,6 @@ import { resolvedPorts } from './server_Initialization'; export namespace WebSocket { export let _socket: Socket; export const clients: { [key: string]: Client } = {}; - // export const clients = new Map<string, Client>(); export const socketMap = new Map<SocketIO.Socket, string>(); export const userOperations = new Map<string, number>(); export let disconnect: Function; @@ -52,6 +51,8 @@ export namespace WebSocket { next(); }); + socket.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle()) + // convenience function to log server messages on the client function log(message?: any, ...optionalParams: any[]) { socket.emit('log', ['Message from server:', message, ...optionalParams]); @@ -109,6 +110,8 @@ export namespace WebSocket { } }); + + Utils.Emit(socket, MessageStore.Foo, 'handshooken'); Utils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid)); @@ -142,6 +145,12 @@ export namespace WebSocket { socket.disconnect(true); }; }); + + setInterval(function() { + // Utils.Emit(socket, MessageStore.UpdateStats, DashStats.getUpdatedStatsBundle()); + + io.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle()) + }, DashStats.SAMPLING_INTERVAL); } function processGesturePoints(socket: Socket, content: GestureContent) { @@ -185,13 +194,9 @@ export namespace WebSocket { function barReceived(socket: SocketIO.Socket, userEmail: string) { clients[userEmail] = new Client(userEmail.toString()); - // clients.set(userEmail, new Client(userEmail.toString())); const currentdate = new Date(); const datetime = currentdate.getDate() + '/' + (currentdate.getMonth() + 1) + '/' + currentdate.getFullYear() + ' @ ' + currentdate.getHours() + ':' + currentdate.getMinutes() + ':' + currentdate.getSeconds(); console.log(blue(`user ${userEmail} has connected to the web socket at: ${datetime}`)); - // console.log(magenta(`currently connected: ${[...clients.entries()]}`)); - // console.log(magenta('socket map below')); - // console.log([...socketMap.entries()]); printActiveUsers(); timeMap[userEmail] = Date.now(); @@ -321,7 +326,8 @@ export namespace WebSocket { const remListItems = diff.diff.$set[updatefield].fields; const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((f: any) => f !== null) || []; diff.diff.$set[updatefield].fields = curList?.filter( - (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem)) + (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : + remItem.heading ? remItem.heading === curItem.heading : remItem === curItem)) ); const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length; delete diff.diff.length; @@ -367,11 +373,8 @@ export namespace WebSocket { var CurUser: string | undefined = undefined; function UpdateField(socket: Socket, diff: Diff) { - console.log(magenta(`1 OP ${socketMap.get(socket)}`)); - let currentUsername = socketMap.get(socket)!.split(' ')[0]; userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0); - console.log(yellow("Total Operations: " + userOperations.get(currentUsername))); if (CurUser !== socketMap.get(socket)) { CurUser = socketMap.get(socket); diff --git a/views/resources/statsviewcontroller.js b/views/resources/statsviewcontroller.js new file mode 100644 index 000000000..090e112e7 --- /dev/null +++ b/views/resources/statsviewcontroller.js @@ -0,0 +1,114 @@ +/** + * statsviewcontroller.js stores the JavaScript functions to update the stats page + * when the websocket updates. + */ + +const BUSY_SERVER_BOUND = 2; +const VERY_BUSY_SERVER_BOUND = 3; + +const MEDIUM_USE_BOUND = 100; //operations per 10 seconds +const HIGH_USE_BOUND = 300; + +const serverTrafficMessages = { + 0 : "Not Busy", + 1 : "Busy", + 2: "Very Busy" +}; + +/** + * userDataComparator sorts the users based on the rate + * + * @param {*} user1 the first user to compare + * @param {*} user2 the second user to comapre + * @returns an integer indiciating which user should come first + */ +function userDataComparator(user1, user2) { + if(user1.rate < user2.rate) { + return 1; + } else if(user1.rate > user2.rate) { + return -1; + } else { + return 0; + } +} + +/** + * calculateServerTraffic() returns an integer corresponding + * to the current traffic that can be used to get the message + * from "serverTrafficMessages" + * + * @param {*} data the incoming data from the backend + * @returns an integer where 0 is not busy, 1 is busy, and 2 is very busy. + */ +function calculateServerTraffic(data) { + let currentTraffic = data.connectedUsers.length; + + let serverTraffic = 0; + if(currentTraffic < BUSY_SERVER_BOUND) { + serverTraffic = 0; + } else if(currentTraffic >= BUSY_SERVER_BOUND && currentTraffic < VERY_BUSY_SERVER_BOUND) { + serverTraffic = 1; + } else { + serverTraffic = 2; + } + + return serverTraffic; +} + +/** + * getUserRateColor determines what color the user's rate should + * be on the front end + * @param {*} rate the operations per time interval for a specific user + * @returns a string representing the color to make the user rate + */ +function getUserRateColor(rate) { + if(rate < MEDIUM_USE_BOUND) { + return "black"; + } else if(rate >= MEDIUM_USE_BOUND && rate < HIGH_USE_BOUND) { + return "orange"; + } else if(rate >= HIGH_USE_BOUND){ + return "red"; + } else { + return "black"; + } +} + +/** + * handleStatsUpdats() is called when new data is received from the backend + * from a websocket event. The method updates the HTML site to reflect the + * updated data + * + * @param {*} data the data coming from the backend. + */ +function handleStatsUpdate(data) { + let userListInnerHTML = ""; + data.connectedUsers.sort(userDataComparator); + data.connectedUsers.map((userData, index) => { + let userRateColor = getUserRateColor(userData.rate); + let userEntry = `<p>${userData.time}</p> + <p>${userData.username}</p> + <p>Operations: ${userData.operations}</p> + <p style="color:${userRateColor}">Rate: ${userData.rate} operations per last 10 seconds</p> + `; // user data comes as last 10 seconds but it can be adjusted in DastStats.ts and websocket.ts + userListInnerHTML += "<li class=\"none\">" + userEntry + "</li>"; + }) + + document.getElementById("connection-count").innerHTML = `Current Connections: ${data.connectedUsers.length}` + document.getElementById("connected-user-list").innerHTML = userListInnerHTML; + + let serverTraffic = calculateServerTraffic(data); + let serverTrafficMessage = "Not Busy"; + switch(serverTraffic) { + case 0: + serverTrafficMessage = "Not Busy"; + break; + case 1: + serverTrafficMessage = "Busy"; + break; + case 2: + serverTrafficMessage = "Very Busy"; + break; + } + document.getElementById("stats-traffic-message").className="stats-server-status-item stats-server-status-" + serverTraffic; + document.getElementById("stats-traffic-message").innerHTML = `<p>${serverTrafficMessage}</p>`; +}
\ No newline at end of file diff --git a/views/stats.pug b/views/stats.pug index 54c017e70..16c28087e 100644 --- a/views/stats.pug +++ b/views/stats.pug @@ -1,22 +1,30 @@ extends ./layout +//- stats.pug is the frontend for the stats page block content style include ./stylesheets/authentication.css include ./stylesheets/statsview.css + script(src=`http://localhost:4321/socket.io/socket.io.js`) + script + include ./resources/statsviewcontroller.js + script. + var socket = io.connect("http://localhost:4321"); + socket.on("connect", () => console.log("connected to socket")); + + socket.on("a2cf757f-abd7-537b-953e-ef2f4f798f7e", (data) => handleStatsUpdate(data)); .outermost .stats-container h1 Dash Stats - p(class="stats-content") Current Connections: #{numConnections} + + p(class="stats-content" id="connection-count") Current Connections: #{numConnections} div(class="stats-content stats-server-status-container") p Server Status: - div(class="stats-server-status-item stats-server-status-" + serverTraffic) + div(id="stats-traffic-message" class="stats-server-status-item stats-server-status-" + serverTraffic) p #{serverTrafficMessage} div(class="stats-content stats-connected-users") p Connected Users: - ul - each username in connectedUsers - li(class="none")= username + ul(id="connected-user-list")
\ No newline at end of file |