Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: group measurements by study #4617

5 changes: 2 additions & 3 deletions extensions/cornerstone-dicom-sr/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ const _generateReport = (measurementData, additionalFindingTypes, options = {})
};

const commandsModule = (props: withAppTypes) => {
const { servicesManager, extensionManager, commandsManager } = props;
const { customizationService, measurementService, viewportGridService, uiDialogService } =
servicesManager.services;
const { servicesManager, extensionManager } = props;
const { customizationService, displaySetService, viewportGridService } = servicesManager.services;

const actions = {
changeColorMeasurement: ({ uid }) => {
Expand Down
74 changes: 47 additions & 27 deletions extensions/cornerstone/src/panels/PanelMeasurement.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef } from 'react';
import { utils } from '@ohif/core';
import { MeasurementTable } from '@ohif/ui-next';
import debounce from 'lodash.debounce';
import { useMeasurements } from '../hooks/useMeasurements';

const { filterAdditionalFinding, filterOr, filterAny } = utils.MeasurementFilters;
const { filterAdditionalFinding, filterAny } = utils.MeasurementFilters;

export type withAppAndFilters = withAppTypes & {
measurementFilters: Record<string, (item) => boolean>;
groupBy: string;
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
title: string;
};

export default function PanelMeasurementTable({
servicesManager,
commandsManager,
customHeader,
measurementFilters = { measurementFilter: filterAny },
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
title,
}: withAppAndFilters): React.ReactNode {
const measurementsPanelRef = useRef(null);

const { measurementService, customizationService } = servicesManager.services;
const { measurementService, displaySetService } = servicesManager.services;

const displayMeasurements = useMeasurements(servicesManager, {
measurementFilter: measurementFilters.measurementFilter.bind(measurementFilters),
Expand Down Expand Up @@ -49,9 +52,22 @@ export default function PanelMeasurementTable({
const additionalFilter = filterAdditionalFinding(measurementService);

const { measurementFilter: trackedFilter } = measurementFilters;
const measurements = displayMeasurements.filter(
item => !additionalFilter(item) && trackedFilter(item)
);
const measurements = displayMeasurements
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
.filter(item => !additionalFilter(item) && trackedFilter(item))
.reduce((groupedMeasurements, item) => {
const displaySet = displaySetService.getDisplaySetByUID(item.displaySetInstanceUID);
const key = displaySet.instances[0].StudyDescription;
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved

if (!groupedMeasurements.has(key)) {
groupedMeasurements.set(key, [item]);
return groupedMeasurements;
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
}

const oldValues = groupedMeasurements.get(key);
oldValues.push(item);
return groupedMeasurements;
}, new Map()) as Map<string, object[]>;
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved

const additionalFindings = displayMeasurements.filter(
item => additionalFilter(item) && trackedFilter(item)
);
Expand All @@ -71,27 +87,31 @@ export default function PanelMeasurementTable({
ref={measurementsPanelRef}
data-cy={'trackedMeasurements-panel'}
>
<MeasurementTable
key="tracked"
title="Measurements"
data={measurements}
{...onArgs}
// onColor={changeColorMeasurement}
>
<MeasurementTable.Header>
{customHeader && (
<>
{typeof customHeader === 'function'
? customHeader({
additionalFindings,
measurements,
})
: customHeader}
</>
)}
</MeasurementTable.Header>
<MeasurementTable.Body />
</MeasurementTable>
{Array.from(measurements).map(([key, value]) => {
return (
<MeasurementTable
key={`tracked-${key}`}
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
title={title ? title : `Measurements for ${key}`}
data={value}
{...onArgs}
// onColor={changeColorMeasurement}
>
<MeasurementTable.Header>
{customHeader && (
<>
{typeof customHeader === 'function'
? customHeader({
additionalFindings,
measurements,
})
: customHeader}
</>
)}
</MeasurementTable.Header>
<MeasurementTable.Body />
</MeasurementTable>
);
})}
{additionalFindings.length > 0 && (
pedrokohler marked this conversation as resolved.
Show resolved Hide resolved
<MeasurementTable
key="additional"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useEffect, useState } from 'react';
import { DicomMetadataStore, utils } from '@ohif/core';
import { utils } from '@ohif/core';
import { useViewportGrid } from '@ohif/ui';
import { Button, Icons } from '@ohif/ui-next';
import { PanelMeasurement, StudySummaryFromMetadata } from '@ohif/extension-cornerstone';
import { useTrackedMeasurements } from '../getContextModule';
import { useTranslation } from 'react-i18next';

const { filterAny, filterNone, filterNot, filterTracked } = utils.MeasurementFilters;
const { filterAny, filterNot, filterTracked } = utils.MeasurementFilters;

function PanelMeasurementTableTracking({
servicesManager,
Expand All @@ -28,7 +28,7 @@ function PanelMeasurementTableTracking({
});

useEffect(() => {
let updatedMeasurementFilters = { ...measurementFilters };
const updatedMeasurementFilters = { ...measurementFilters };
if (trackedMeasurements.matches('tracking') && trackedStudy) {
updatedMeasurementFilters.measurementFilter = filterTracked(trackedStudy, trackedSeries);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix the measurementFilter to be just a single filter, and make it independent of the tracked study.

} else {
Expand All @@ -53,6 +53,7 @@ function PanelMeasurementTableTracking({
extensionManager={extensionManager}
commandsManager={commandsManager}
measurementFilters={measurementFilters}
title="Measurements"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grouping function should get passed in here to PanelMeasurements, and should be a grouping function that groups things into "Tracked Measurements" with a group header being the study header, and the body being the default PanelMeasurements sub-grouping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the default grouping function for PanelMeasurements is fine, just passing in the top level selector which selects only tracked measurements, and that should ONLY use series instance UID so that when we add multiple studies to PanelMeasurement it just automatically creates multiple sub-groups.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filtering measurements to include only tracked ones is understandable—that makes sense.

However, I don’t understand why, or even what it means, to 'group' measurements by 'tracked measurements.' Could you clarify?

customHeader={({ additionalFindings, measurements }) => {
const disabled = additionalFindings.length === 0 && measurements.length === 0;

Expand Down