From 51eb9dff4e4a8b4d60ad9e701fdfa9fab7ef6c77 Mon Sep 17 00:00:00 2001 From: Luke-Schreiber <131496683+Luke-Schreiber@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:28:07 -0600 Subject: [PATCH 1/4] 'Only' procedure fix --- .../Utilities/LeftToolBox/LeftToolBox.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx b/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx index 7c12af64e..c36882e06 100644 --- a/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx +++ b/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx @@ -26,11 +26,17 @@ function LeftToolBox() { // Process the result into the data type required. const result = procedureInput.result.map((procedure) => { - const procedureOverlapList = Object.keys(procedure.overlapList).map((subProcedureName) => ({ - procedureName: subProcedureName, - count: procedure.overlapList[subProcedureName], - codes: procedureInput.result.find((p) => p.procedureName === subProcedureName)?.procedureCodes || [], - })); + console.log('Procedure:', procedure); + + // Overlap List + const procedureOverlapList = Object.keys(procedure.overlapList).map((subProcedureName) => { + // Strip "Only " prefix for lookup + const baseName = subProcedureName.startsWith('Only ') ? subProcedureName.replace(/^Only\s+/, '') : subProcedureName; + return { + procedureName: subProcedureName, + count: procedure.overlapList[subProcedureName], + codes: procedureInput.result.find((p) => p.procedureName === baseName)?.procedureCodes || [], + }}); procedureOverlapList.sort((a, b) => { if (a.procedureName.includes('Only')) { return -1; From 0339f5e016a28b33bad1950a9b91588acb5460a8 Mon Sep 17 00:00:00 2001 From: Luke-Schreiber <131496683+Luke-Schreiber@users.noreply.github.com> Date: Tue, 1 Jul 2025 22:23:06 +0000 Subject: [PATCH 2/4] Clean-up, commenting --- .../Utilities/LeftToolBox/LeftToolBox.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx b/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx index c36882e06..1dcc0cc57 100644 --- a/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx +++ b/frontend/src/Components/Utilities/LeftToolBox/LeftToolBox.tsx @@ -24,19 +24,20 @@ function LeftToolBox() { const procedureFetch = await fetch(`${import.meta.env.VITE_QUERY_URL}get_procedure_counts`); const procedureInput = await procedureFetch.json() as { result: { procedureName: string, procedureCodes: string[], count: number, overlapList: { [key: string]: number } }[] }; - // Process the result into the data type required. - const result = procedureInput.result.map((procedure) => { - console.log('Procedure:', procedure); - - // Overlap List + // For each procedure, create a ProcedureEntry object + const procedures = procedureInput.result.map((procedure) => { + // Overlap List (Co-occurences of procedures) const procedureOverlapList = Object.keys(procedure.overlapList).map((subProcedureName) => { // Strip "Only " prefix for lookup - const baseName = subProcedureName.startsWith('Only ') ? subProcedureName.replace(/^Only\s+/, '') : subProcedureName; + const baseSubProcedureName = subProcedureName.startsWith('Only ') ? subProcedureName.replace(/^Only\s+/, '') : subProcedureName; return { procedureName: subProcedureName, count: procedure.overlapList[subProcedureName], - codes: procedureInput.result.find((p) => p.procedureName === baseName)?.procedureCodes || [], - }}); + // Access the codes for this sub-procedure by looking up the procedure's codes for that name + codes: procedureInput.result.find((p) => p.procedureName === baseSubProcedureName)?.procedureCodes || [], + }; + }); + // Sort the co-occurrence list by count, and prioritize "Only" procedures procedureOverlapList.sort((a, b) => { if (a.procedureName.includes('Only')) { return -1; @@ -46,6 +47,7 @@ function LeftToolBox() { return b.count - a.count; }); + // Return the ProcedureEntry object return { procedureName: procedure.procedureName, count: procedure.count, @@ -53,8 +55,9 @@ function LeftToolBox() { overlapList: procedureOverlapList, }; }); - const tempSurgeryList: ProcedureEntry[] = result; - let tempMaxCaseCount = (max(result, (d) => d.count)) || 0; + + const tempSurgeryList: ProcedureEntry[] = procedures; + let tempMaxCaseCount = (max(procedures, (d) => d.count)) || 0; tempMaxCaseCount *= 1.1; setMaxCaseCount(tempMaxCaseCount); From d32245dfcf51f49885ec3864ef7c2c819c910bdb Mon Sep 17 00:00:00 2001 From: Luke-Schreiber <131496683+Luke-Schreiber@users.noreply.github.com> Date: Thu, 3 Jul 2025 19:52:59 +0000 Subject: [PATCH 3/4] Current store logic --- frontend/src/Interfaces/Store.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/frontend/src/Interfaces/Store.ts b/frontend/src/Interfaces/Store.ts index a7cfc61d1..923cdeb42 100644 --- a/frontend/src/Interfaces/Store.ts +++ b/frontend/src/Interfaces/Store.ts @@ -65,12 +65,12 @@ export class RootStore { const surgeons = this._allCases.map((d) => [d.SURGEON_PROV_ID, d.SURGEON_PROV_NAME]); const anesths = this._allCases.map((d) => [d.ANESTH_PROV_ID, d.ANESTH_PROV_NAME]); return Object.fromEntries( - [...surgeons, ...anesths].filter((d) => d[0] !== null && d[1] !== null) + [...surgeons, ...anesths].filter((d) => d[0] !== null && d[1] !== null), ); } get filteredCases() { - return this._allCases.filter((d) => { + const cases = this._allCases.filter((d) => { // Date range filter if (!(d.CASE_DATE >= this.provenanceState.rawDateRange[0] && d.CASE_DATE <= this.provenanceState.rawDateRange[1])) { return false; @@ -117,15 +117,28 @@ export class RootStore { // If there are selected procedures, filter cases based on the selected procedures const procedureFiltered = hasAnyProcedure && !this.provenanceState.proceduresSelection.some((procedure) => { + // This case's procedure codes. Example: [A code matching ECMO, A code matching CABG, ...] const patientCodes = d.ALL_CODES.split(','); - // Sub-procedures selected (overlapList) + // If sub-procedures selected (overlapList) Example: ["ECMO" is selected as the sub-procedure of "CABG"] if (procedure.overlapList && procedure.overlapList.length > 0) { - return procedure.codes.some((code) => patientCodes.includes(code)) - && procedure.overlapList.some((subProcedure) => subProcedure.codes.some((code) => patientCodes.includes(code))); + return procedure.overlapList.some((subProcedure) => { + // Handle "Only " sub-procedure logic + if (subProcedure.procedureName.startsWith('Only ')) { + // Only include cases where the patients codes is the same or subset of the sub-procedure codes + // Example: Patients codes are ["123"] and subProcedure codes are ["123", "456", "789"] (All ECMO codes) + return patientCodes.length > 0 && patientCodes.every((code) => subProcedure.codes.includes(code)); + } + // Otherwise, check if the patient has at least one of both the main procedure and the sub-procedure codes + // (Example: At least one from both "ECMO" codes & "CABG" codes) + return ( + procedure.codes.some((code) => patientCodes.includes(code)) + && subProcedure.codes.some((code) => patientCodes.includes(code)) + ); + }); } - // Main procedure selected + // Only the Main procedure selected: Check if the patient has at least one of the main procedure codes return procedure.codes.some((code) => patientCodes.includes(code)); }); if (procedureFiltered) { @@ -134,6 +147,8 @@ export class RootStore { return true; }); + console.log('Filtered cases:', cases); + return cases; } get filterRange() { From 8f61caac72f534a8db61f7417aa469bc7bf3bbcf Mon Sep 17 00:00:00 2001 From: Luke-Schreiber <131496683+Luke-Schreiber@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:16:27 +0000 Subject: [PATCH 4/4] Occasionally make one surgery --- .../commands/fill_db_mock_patient_data.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/api/management/commands/fill_db_mock_patient_data.py b/backend/api/management/commands/fill_db_mock_patient_data.py index 550e817ff..2b885ff60 100644 --- a/backend/api/management/commands/fill_db_mock_patient_data.py +++ b/backend/api/management/commands/fill_db_mock_patient_data.py @@ -169,9 +169,14 @@ def handle(self, *args, **kwargs): # Generate mock data for SURGERY_CASE for pat, visit in visits: - surg1_start = visit.ADM_DTM + timedelta(hours=2) - surg2_start = visit.ADM_DTM + timedelta(days=1) - for start_time in [surg1_start, surg2_start]: + if random.random() < 0.3: # ~30% of visits get only one surgery + surgery_starts = [visit.ADM_DTM + timedelta(hours=2)] + else: + surgery_starts = [ + visit.ADM_DTM + timedelta(hours=2), + visit.ADM_DTM + timedelta(days=1) + ] + for start_time in surgery_starts: surg_end = make_aware(fake.date_time_between(start_date=start_time, end_date=start_time + timedelta(hours=5))) surgeon = fake.random_element(elements=surgeons) @@ -201,7 +206,6 @@ def handle(self, *args, **kwargs): # Generate mock data for BILLING_CODES codes, _, _ = get_all_cpt_code_filters() for surg in surgeries: - for rank in range(random.randint(1, 5)): BILLING_CODES.objects.create( VISIT_NO=surg.VISIT_NO, CODE_TYPE_DESC=fake.sentence(), @@ -211,8 +215,9 @@ def handle(self, *args, **kwargs): PROV_ID=surg.SURGEON_PROV_ID, PROV_NAME=surg.SURGEON_PROV_NAME, PRESENT_ON_ADM_F=fake.random_element(elements=('Y', None)), - CODE_RANK=rank, + CODE_RANK=1, ) + self.stdout.write(self.style.SUCCESS('Successfully generated billing codes data')) # Generate mock data for VISIT_LABS