Skip to content

Commit

Permalink
medical dashboard now calculates length of stay instead of using mock…
Browse files Browse the repository at this point in the history
… data (#89)

* fixed the bug in the authentication.ts file

Problem
The getUserGroupKey() function was checking
the stored user group key against the object's
keys instead of the object's values. This caused
the function to always return null and
broke the protected navbar.

Solution
- Changed the function to check the object's
values instead of the keys.

Ticket
N/A

Documentation
N/A

Testing
- Tested the protected navbar and it is now
working as expected.

* medical dashboard now calculates length of stay instead of using mock data

Problem
The medical dashboard was using mock data for length of stay.

Solution
- Created a new function in `postfestivalDashboard.ts` to calculate length of stay
- Integrated the new function into the `postevent-summary.tsx` page

Ticket URL
N/A

Documentation
N/A

Tests Run
- Locally executed `yarn build` and built with no errors.
- Checked local dev server and confirmed that the medical dashboard was displaying calculated data.
    + localhost:3000/medical/dashboards/postevent-summary
  • Loading branch information
critch646 authored Feb 7, 2024
1 parent f4e753c commit 64904a8
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 52 deletions.
10 changes: 5 additions & 5 deletions app/web/components/dashboard/LengthOfStayCountsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material";
import { LengthOfStayCountsTableProps } from "./LengthOfStayCountsTableProps";
import { LengthOfStayCountsTableProps } from "../../interfaces/LengthOfStayCountsTableProps";


/**
Expand Down Expand Up @@ -28,11 +28,11 @@ export const LengthOfStayCountsTable: React.FC<LengthOfStayCountsTableProps> = (
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, index) => (
{rows.map((row: (string | number)[], index: number) => (
<TableRow key={index}>
{row.map((cell, cellIndex) => (
<TableCell key={cellIndex} align="center">
{cell}
{typeof cell === "number" ? cell.toFixed(0) : cell}
</TableCell>
))}
</TableRow>
Expand All @@ -42,11 +42,11 @@ export const LengthOfStayCountsTable: React.FC<LengthOfStayCountsTableProps> = (
<TableCell colSpan={6} align="left">Quartiles</TableCell>
</TableRow>
{/* Quantiles, Mean, etc. */}
{summaryRows.map((row, index) => (
{summaryRows.map((row: (string | number)[], index: number) => (
<TableRow key={index}>
{row.map((cell, cellIndex) => (
<TableCell key={cellIndex} align="center">
{cell}
{row[0] === "Mean" && typeof cell === "number" ? cell.toFixed(2) : cell}
</TableCell>
))}
</TableRow>
Expand Down
7 changes: 7 additions & 0 deletions app/web/constants/medicalForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ export const chiefComplaints = [
"Shortness of Breath",
"Trauma",
];

export const TriageAcuities = {
Red: "red",
Yellow: "yellow",
Green: "green",
White: "white",
};
20 changes: 11 additions & 9 deletions app/web/pages/medical/dashboards/postevent-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ChiefComplaintCountsTable } from "../../../components/dashboard/ChiefCo
import { YearSelectionField } from "../../../components/dashboard/YearSelectionField";
import { PatientEncounterAcuityBarChart } from "../../../components/dashboard/PatientEncounterAcuityChart";
import { ChiefComplaintEncounterCountsTable } from "../../../components/dashboard/ChiefComplaintEncounterCountsTable";
// import { LengthOfStayCountsTable } from "../../../components/dashboard/LengthOfStayCountsTable";
import { LengthOfStayCountsTable } from "../../../components/dashboard/LengthOfStayCountsTable";
import { calculateChiefComplaintEncounterCountsData, calculateChiefComplaintEncounterCountsSummary } from "../../../utils/postfestivalDashboard";
// import { CommonPresentationsAndTransportsTables } from "../../../components/dashboard/TopTenCommonPresentationsTable";
import { fetchPatientEncountersData } from "../../../utils/postfestivalDashboard";
Expand All @@ -18,6 +18,8 @@ import { SubmitAlert } from "../../../interfaces/SubmitAlert";
import { PatientEncounterRow } from "../../../interfaces/PatientEncounterRow";
import { ChiefComplaintCountsTableRowData } from "../../../interfaces/ChiefComplaintCountsTableProps";
// import { ChiefComplaintEncounterCountsTableProps } from "../../../interfaces/ChiefComplaintEncounterCountsTableProps";
import { calculatePostFestivalLengthOfStayData } from "../../../utils/postfestivalDashboard";
import { LengthOfStayCountsTableProps } from "../../../interfaces/LengthOfStayCountsTableProps";


/**
Expand All @@ -40,9 +42,9 @@ const MedicalPostEventSummaryDashboard = () => {
const selectedYear = watch("selectedYear");

// Calculated data
const [chiefComplaintEncounterCountsData, setChiefComplaintEncounterCountsData] = useState<number[]>([]); // TODO: Replace with actual data when API integration is complete
const [chiefComplaintCountRows, setChiefComplaintCountRows] = useState<ChiefComplaintCountsTableRowData[]>([]); // TODO: Replace with actual data when API integration is complete
// const [lengthOfStayData, setLengthOfStayData] = useState([]);
const [chiefComplaintEncounterCountsData, setChiefComplaintEncounterCountsData] = useState<number[]>([]);
const [chiefComplaintCountRows, setChiefComplaintCountRows] = useState<ChiefComplaintCountsTableRowData[]>([]);
const [lengthOfStayData, setLengthOfStayData] = useState<LengthOfStayCountsTableProps>({ rows: [], summaryRows: [] });
// const [commonPresentationData, setCommonPresentationData] = useState([]);

// When the year is selected, fetch the patient encounters for that year
Expand All @@ -64,8 +66,8 @@ const MedicalPostEventSummaryDashboard = () => {
const chiefComplaintCountRows = calculateChiefComplaintEncounterCountsSummary(patientEncounters);
setChiefComplaintCountRows(chiefComplaintCountRows);

// const lengthOfStayData = generatePostFestivalLengthOfStayData(patientEncounters);
// setLengthOfStayData(lengthOfStayData);
const lengthOfStayData = calculatePostFestivalLengthOfStayData(patientEncounters);
setLengthOfStayData(lengthOfStayData);

// const commonPresentationData = generatePostFestivalCommonPresentationsData(patientEncounters);
// setCommonPresentationData(commonPresentationData);
Expand Down Expand Up @@ -103,10 +105,10 @@ const MedicalPostEventSummaryDashboard = () => {
<Grid item xs={12} md={8} lg={8}>
<ChiefComplaintCountsTable rows={chiefComplaintCountRows} />
</Grid>
{/* <Grid item xs={12} md={8} lg={8}>
<LengthOfStayCountsTable rows={rowsDataCCCount} summaryRows={summaryRowsCCCount} />
<Grid item xs={12} md={8} lg={8}>
<LengthOfStayCountsTable {...lengthOfStayData} />
</Grid>
<Grid item xs={12} md={4} lg={4}>
{/* <Grid item xs={12} md={4} lg={4}>
<CommonPresentationsAndTransportsTables
commonPresentationsDataRed={commonPresentationsDataRed}
transportsDataRed={transportsDataRed}
Expand Down
208 changes: 170 additions & 38 deletions app/web/utils/postfestivalDashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,8 @@ import { PresentationGroup } from "../interfaces/PresentationGroup";
import { buildBearerTokenHeader } from "./authentication";
import { SubmitAlert } from "../interfaces/SubmitAlert";
import { ChiefComplaintCountsTableRowData } from "../interfaces/ChiefComplaintCountsTableProps";

/**
* Generates data for the Post Festival Length of Stay table
*
* @param data The patient encounters data to generate the table from
*
* @returns An object containing the data for the Post Festival Length of Stay table
*/
export const generatePostFestivalLengthOfStayData = (
data: PatientEncounterRow[]
) => {
// TODO: Implement actual generation logic
console.log("TODO: Implement actual generation logic:", data);

const rowsDataCCCount = [
["Unknown", 0, 0, 0, 0, 0],
["0 - 15 mins", 0, 0, 0, 0, 0],
["16 - 30 mins", 0, 0, 0, 0, 0],
["31 - 45 mins", 0, 0, 0, 0, 0],
["46 - 60 mins", 0, 0, 0, 0, 0],
["61 - 75 mins", 0, 0, 0, 0, 0],
["76 - 90 mins", 0, 0, 0, 0, 0],
["91 - 105 mins", 0, 0, 0, 0, 0],
["106 - 120 mins", 0, 0, 0, 0, 0],
["121 - 135 mins", 0, 0, 0, 0, 0],
["136 - 150 mins", 0, 0, 0, 0, 0],
[">150 mins", 0, 0, 0, 0, 0],
];

const summaryRowsCCCount = [
["Q1", 0, 0, 0, 0, 0],
["Mean", 0, 0, 0, 0, 0],
["Q3", 0, 0, 0, 0, 0],
["Max", 0, 0, 0, 0, 0],
];

return { rowsDataCCCount, summaryRowsCCCount };
};
import { TriageAcuities } from "../constants/medicalForm";
import { LengthOfStayCountsTableProps } from "../interfaces/LengthOfStayCountsTableProps";

/**
* Generates data for the Post Festival Common Presentations table
Expand Down Expand Up @@ -275,3 +239,171 @@ export function calculateChiefComplaintEncounterCountsSummary(

return summaryData;
}

export function calculatePostFestivalLengthOfStayData(
patientEncounters: PatientEncounterRow[]
): LengthOfStayCountsTableProps {
const rowsDataCCCount = [
["Unknown", 0, 0, 0, 0, 0],
["0 - 15 mins", 0, 0, 0, 0, 0],
["16 - 30 mins", 0, 0, 0, 0, 0],
["31 - 45 mins", 0, 0, 0, 0, 0],
["46 - 60 mins", 0, 0, 0, 0, 0],
["61 - 75 mins", 0, 0, 0, 0, 0],
["76 - 90 mins", 0, 0, 0, 0, 0],
["91 - 105 mins", 0, 0, 0, 0, 0],
["106 - 120 mins", 0, 0, 0, 0, 0],
["121 - 135 mins", 0, 0, 0, 0, 0],
["136 - 150 mins", 0, 0, 0, 0, 0],
[">150 mins", 0, 0, 0, 0, 0],
];

const summaryRowsCCCount = [
["Q1", 0, 0, 0, 0, 0],
["Mean", 0, 0, 0, 0, 0],
["Q3", 0, 0, 0, 0, 0],
["Max", 0, 0, 0, 0, 0],
];

const losDurations = patientEncounters.map((encounter) => {
const arrivalDateTime = new Date(encounter.arrival_date);
arrivalDateTime.setHours(encounter.arrival_time.getHours());
arrivalDateTime.setMinutes(encounter.arrival_time.getMinutes());
arrivalDateTime.setSeconds(encounter.arrival_time.getSeconds());

const departureDateTime = new Date(encounter.departure_date);
departureDateTime.setHours(encounter.departure_time.getHours());
departureDateTime.setMinutes(encounter.departure_time.getMinutes());
departureDateTime.setSeconds(encounter.departure_time.getSeconds());

const durationMinutes =
(departureDateTime.getTime() - arrivalDateTime.getTime()) / (1000 * 60);

return { durationMinutes, acuity: encounter.triage_acuity };
});

losDurations.forEach(({ durationMinutes, acuity }) => {
let rowIndex;
if (durationMinutes <= 15) {
rowIndex = 1;
} else if (durationMinutes <= 30) {
rowIndex = 2;
} else if (durationMinutes <= 45) {
rowIndex = 3;
} else if (durationMinutes <= 60) {
rowIndex = 4;
} else if (durationMinutes <= 75) {
rowIndex = 5;
} else if (durationMinutes <= 90) {
rowIndex = 6;
} else if (durationMinutes <= 105) {
rowIndex = 7;
} else if (durationMinutes <= 120) {
rowIndex = 8;
} else if (durationMinutes <= 135) {
rowIndex = 9;
} else if (durationMinutes <= 150) {
rowIndex = 10;
} else {
rowIndex = 11;
}

(rowsDataCCCount[rowIndex][1] as number)++;

console.log("acuity", acuity);
// Increment the count for the specific triage acuity
switch (acuity) {
case TriageAcuities.Red:
(rowsDataCCCount[rowIndex][2] as number)++;
break;
case TriageAcuities.Yellow:
(rowsDataCCCount[rowIndex][3] as number)++;
break;
case TriageAcuities.Green:
(rowsDataCCCount[rowIndex][4] as number)++;
break;
case TriageAcuities.White:
(rowsDataCCCount[rowIndex][5] as number)++;
break;
}
});

// Step 1: Aggregate duration minutes into separate arrays
const totalDurations: number[] = [];
const redDurations: number[] = [];
const yellowDurations: number[] = [];
const greenDurations: number[] = [];
const whiteDurations: number[] = [];

losDurations.forEach(({ durationMinutes, acuity }) => {
totalDurations.push(durationMinutes);
switch (acuity) {
case TriageAcuities.Red:
redDurations.push(durationMinutes);
break;
case TriageAcuities.Yellow:
yellowDurations.push(durationMinutes);
break;
case TriageAcuities.Green:
greenDurations.push(durationMinutes);
break;
case TriageAcuities.White:
whiteDurations.push(durationMinutes);
break;
}
});

// Helper function to calculate mean, quartiles, and max
const calculateStatistics = (durations: number[]) => {
const sorted = durations.sort((a, b) => a - b);
const mean = sorted.reduce((acc, val) => acc + val, 0) / sorted.length;
const Q1 = sorted[Math.floor(sorted.length / 4)];
const Q3 = sorted[Math.floor(sorted.length * (3 / 4))];
const max = sorted[sorted.length - 1];

return { mean, Q1, Q3, max };
};

// Step 2, 3, 4, 5: Calculate statistics for each category
const totalStats = calculateStatistics(totalDurations);
const redStats = calculateStatistics(redDurations);
const yellowStats = calculateStatistics(yellowDurations);
const greenStats = calculateStatistics(greenDurations);
const whiteStats = calculateStatistics(whiteDurations);

// Populate summaryRowsCCCount with calculated statistics
summaryRowsCCCount[0] = [
"Q1",
totalStats.Q1,
redStats.Q1,
yellowStats.Q1,
greenStats.Q1,
whiteStats.Q1,
];
summaryRowsCCCount[1] = [
"Mean",
totalStats.mean,
redStats.mean,
yellowStats.mean,
greenStats.mean,
whiteStats.mean,
];
summaryRowsCCCount[2] = [
"Q3",
totalStats.Q3,
redStats.Q3,
yellowStats.Q3,
greenStats.Q3,
whiteStats.Q3,
];
summaryRowsCCCount[3] = [
"Max",
totalStats.max,
redStats.max,
yellowStats.max,
greenStats.max,
whiteStats.max,
];

return { rows: rowsDataCCCount, summaryRows: summaryRowsCCCount };
}

0 comments on commit 64904a8

Please sign in to comment.