From 79d0297f7ce6f6564afe8f043bf62ab50c4672b5 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 1 Sep 2024 17:21:02 -0400 Subject: [PATCH] trying out cursor --- backend/index.js | 2 + backend/sys_stats.js | 21 +++++- frontend/src/Main/Main.js | 103 +++++++++++++++++++++++++- frontend/src/features/api/apiSlice.js | 7 ++ 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/backend/index.js b/backend/index.js index 98ce947..1ef08f8 100644 --- a/backend/index.js +++ b/backend/index.js @@ -8,6 +8,7 @@ var systems = require("./controllers/systems"); var talkgroups = require("./controllers/talkgroups"); var groups = require("./controllers/groups"); var stats = require("./controllers/stats"); +var sys_stats = require("./sys_stats"); var events = require("./controllers/events"); var config = require('./config/config.json'); let db = require('./db') @@ -134,6 +135,7 @@ app.get('/events/:id', events.getEvent); /*------ STATS ----------*/ app.get('/:shortName/stats', stats.get_stats); +app.get('/stats', sys_stats.siteStats) function get_clients(req, res) { diff --git a/backend/sys_stats.js b/backend/sys_stats.js index bde70bc..490ef1a 100644 --- a/backend/sys_stats.js +++ b/backend/sys_stats.js @@ -9,12 +9,20 @@ var uploadErrors = {}; let decodeErrorsFreq = {}; const timePeriod = 15; // in minutes var spots = (24 * 60) / timePeriod; // the number of spots needed to keep track of 24 hours of stats +let uploadsPerMin = new Array(spots).fill(0); +let activeSystems = 0; +function updateUploadsPerMin(totalUploads) { + uploadsPerMin.push(totalUploads / timePeriod); + uploadsPerMin.shift(); +} + + async function updateActiveSystems() { // Go through all of the Systems let siteTotal = 0; - let activeSystems = 0; + activeSystems = 0; for await (let item of System.find()) { // go through all the systems @@ -33,6 +41,7 @@ async function updateActiveSystems() { await item.save(); }; + updateUploadsPerMin(siteTotal); console.log("Site average uploads per minute: " + siteTotal / timePeriod); console.log("Active Systems: " + activeSystems); } @@ -324,6 +333,16 @@ exports.shiftStats = async function () { updateActiveSystems(); console.log("Finished Updating Active Systems at: " + new Date()); } + +exports.siteStats = function() { + const siteStats ={ + uploadsPerMin, + activeSystems + } + res.contentType('json'); + res.send(JSON.stringify(siteStats)); +}; + exports.callTotals = function (shortName) { return callTotals[shortName]; } diff --git a/frontend/src/Main/Main.js b/frontend/src/Main/Main.js index 1d6f565..64269ad 100644 --- a/frontend/src/Main/Main.js +++ b/frontend/src/Main/Main.js @@ -21,7 +21,7 @@ import { } from 'semantic-ui-react' import { useDispatch } from 'react-redux' -import { useGetSystemsQuery } from "../features/api/apiSlice"; +import { useGetStatsQuery, useGetSystemsQuery } from "../features/api/apiSlice"; /* Responsive component was removed from Semantic UI. This is discussed here: https://github.com/Semantic-Org/Semantic-UI-React/pull/4008 */ @@ -213,6 +213,86 @@ const ResponsiveContainer = ({ children }) => ( ) + +import React from "react"; +import { Group } from '@visx/group'; +import { curveStep } from '@visx/curve'; +import { LinePath } from '@visx/shape'; +import { scaleLinear, scaleTime } from '@visx/scale'; +import { timeParse, timeFormat } from 'd3-time-format'; +import { AxisBottom, AxisLeft } from '@visx/axis'; +import { Loader } from "semantic-ui-react"; + +const SiteStatsChart = ({ siteStats }) => { + if (!siteStats) { + return Loading; + } + + const width = 500; + const height = 250; + const margin = { top: 20, bottom: 40, left: 40, right: 20 }; + + const xMax = width - margin.left - margin.right; + const yMax = height - margin.top - margin.bottom; + + const now = new Date(); + const MS_PER_MINUTE = 60000; + + const data = siteStats.map((stat, index) => ({ + y: stat, + x: new Date(now - index * 15 * MS_PER_MINUTE) + })).reverse(); + + const xScale = scaleTime({ + range: [0, xMax], + domain: [data[0].x, data[data.length - 1].x], + }); + + const yScale = scaleLinear({ + range: [yMax, 0], + domain: [0, Math.max(...data.map(d => d.y))], + nice: true, + }); + + return ( + + + xScale(d.x)} + y={d => yScale(d.y)} + stroke="#1a85ff" + strokeWidth={2} + curve={curveStep} + /> + + + + + ); +}; + +const SiteStatsContainer = () => { + const { data: siteStats, isSuccess: siteStatsSuccess } = useGetStatsQuery(); + + if (!siteStatsSuccess) { + return Loading Site Stats; + } + + return ( +
+

Site Activity

+ +
+ ); +}; + + // ---------------------------------------------------- const Main = (props) => { @@ -220,6 +300,8 @@ const Main = (props) => { const [currentSystem, setCurrentSystem] = useState(0); const navigate = useNavigate(); const { data: systems, isSuccess } = useGetSystemsQuery(); //= selectAllSystems(); + const { data: siteStats, isSuccess: siteStatsSuccess } = useGetStatsQuery(); + function useInterval(callback, delay) { const savedCallback = useRef(); @@ -255,7 +337,7 @@ const Main = (props) => { useInterval(() => { advanceSystem() }, 3000) - + //https://stackoverflow.com/questions/36559661/how-can-i-dispatch-from-child-components-in-react-redux //https://stackoverflow.com/questions/42597602/react-onclick-pass-event-with-parameter @@ -316,6 +398,23 @@ const Main = (props) => { + + + + + + + + + + {siteStats?.activeSystems || 0} + + Active Systems + + + + + diff --git a/frontend/src/features/api/apiSlice.js b/frontend/src/features/api/apiSlice.js index 5e36386..03239be 100644 --- a/frontend/src/features/api/apiSlice.js +++ b/frontend/src/features/api/apiSlice.js @@ -40,6 +40,13 @@ export const apiSlice = createApi({ return responseData; } }), + getSiteStats: builder.query({ + query: () => ({ url: '/stats' }), + transformResponse: responseData => { + // You can add any data transformation here if needed + return responseData; + } + }), getTalkgroups: builder.query({ // The URL for the request is '/fakeApi/posts' query: (shortName) => ({ url: `/${shortName}/talkgroups` })