OHIF-326, export as CSV. (#1976)
This commit is contained in:
1 parent
68332701ab
commit
42b02a62d3
14 files changed
+211
-50
No files matched your search
@@ -0,0 +1,164 @@
|
||||
import { DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
export default function downloadCSVReport(
|
||||
measurementData,
|
||||
StudyInstanceUID,
|
||||
referencedSeriesInstanceUIDs,
|
||||
MeasurementService
|
||||
) {
|
||||
const { VALUE_TYPES } = MeasurementService;
|
||||
|
||||
if (measurementData.length === 0) {
|
||||
// Prevent download of report with no measurements.
|
||||
return;
|
||||
}
|
||||
|
||||
const seriesMetadataMap = {};
|
||||
|
||||
referencedSeriesInstanceUIDs.forEach(SeriesInstanceUID => {
|
||||
seriesMetadataMap[SeriesInstanceUID] = DicomMetadataStore.getSeries(
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
});
|
||||
|
||||
const rows = [
|
||||
[
|
||||
'Patient ID',
|
||||
'Patient Name',
|
||||
'Study UID',
|
||||
'Series UID',
|
||||
'SOP Instance UID',
|
||||
'Label',
|
||||
'Long Axis (mm)',
|
||||
'Short Axis (mm)',
|
||||
'Length (mm)',
|
||||
'Area (mmˆ2)',
|
||||
'Mean',
|
||||
'StDev',
|
||||
'Units',
|
||||
],
|
||||
];
|
||||
|
||||
measurementData.forEach(measurement => {
|
||||
switch (measurement.type) {
|
||||
case VALUE_TYPES.BIDIRECTIONAL:
|
||||
rows.push(_getBidirectionalRow(measurement, seriesMetadataMap));
|
||||
break;
|
||||
case VALUE_TYPES.ELLIPSE:
|
||||
rows.push(_getEllipseRow(measurement, seriesMetadataMap));
|
||||
break;
|
||||
case VALUE_TYPES.POINT:
|
||||
rows.push(_getAdditionalFindingRow(measurement, seriesMetadataMap));
|
||||
break;
|
||||
case VALUE_TYPES.POLYLINE:
|
||||
rows.push(_getLengthRow(measurement, seriesMetadataMap));
|
||||
}
|
||||
});
|
||||
|
||||
let csvContent =
|
||||
'data:text/csv;charset=utf-8,' + rows.map(row => row.join(',')).join('\n');
|
||||
|
||||
_createAndDownloadFile(csvContent);
|
||||
}
|
||||
|
||||
function _getBidirectionalRow(measurement, seriesMetadataMap) {
|
||||
const commonRowItems = _getCommonRowItems(measurement, seriesMetadataMap);
|
||||
|
||||
return [
|
||||
...commonRowItems,
|
||||
measurement.longestDiameter, // Long Axis (mm)
|
||||
measurement.shortestDiameter, // Short Axis (mm)
|
||||
];
|
||||
}
|
||||
|
||||
function _getCommonRowItems(measurement, seriesMetadataMap) {
|
||||
const seriesMetadata = seriesMetadataMap[measurement.referenceSeriesUID];
|
||||
const firstInstance = seriesMetadata.instances[0];
|
||||
|
||||
const row = [
|
||||
firstInstance.PatientID, // Patient ID
|
||||
firstInstance.PatientName.Alphabetic, // PatientName
|
||||
measurement.referenceStudyUID, // StudyInstanceUID
|
||||
measurement.referenceSeriesUID, // SeriesInstanceUID
|
||||
measurement.SOPInstanceUID, // SOPInstanceUID
|
||||
measurement.label || '', // Label
|
||||
];
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
function _getAdditionalFindingRow(measurement, seriesMetadataMap) {
|
||||
const commonRowItems = _getCommonRowItems(measurement, seriesMetadataMap);
|
||||
|
||||
return [...commonRowItems];
|
||||
}
|
||||
|
||||
function _getEllipseRow(measurement, seriesMetadataMap) {
|
||||
const commonRowItems = _getCommonRowItems(measurement, seriesMetadataMap);
|
||||
const blankColumns = ['', '', ''];
|
||||
|
||||
const { area, mean, stdDev, unit } = _getEllipseStatsWithUnits(
|
||||
measurement,
|
||||
seriesMetadataMap
|
||||
);
|
||||
|
||||
return [
|
||||
...commonRowItems,
|
||||
...blankColumns,
|
||||
area, // Area (mmˆ2)
|
||||
mean, // Mean
|
||||
stdDev, // StDev
|
||||
unit,
|
||||
];
|
||||
}
|
||||
|
||||
function _getEllipseStatsWithUnits(measurement, seriesMetadataMap) {
|
||||
const seriesMetadata = seriesMetadataMap[measurement.referenceSeriesUID];
|
||||
const firstInstance = seriesMetadata.instances[0];
|
||||
const modality = firstInstance.Modality;
|
||||
|
||||
switch (modality) {
|
||||
case 'CT':
|
||||
return {
|
||||
mean: `${measurement.mean.toFixed(1)}`,
|
||||
stdDev: `${measurement.stdDev.toFixed(1)}`,
|
||||
area: measurement.area.toFixed(1),
|
||||
unit: 'HU',
|
||||
};
|
||||
case 'PT':
|
||||
if (measurement.meanSUV) {
|
||||
return {
|
||||
mean: `${measurement.meanSUV.toFixed(1)}`,
|
||||
stdDev: `${measurement.stdDevSUV.toFixed(1)}`,
|
||||
area: measurement.area.toFixed(1),
|
||||
unit: 'SUV',
|
||||
};
|
||||
}
|
||||
// If no SUV calculation then fallback to default.
|
||||
default:
|
||||
return {
|
||||
mean: `${measurement.mean.toFixed(1)}`,
|
||||
stdDev: `${measurement.stdDev.toFixed(1)}`,
|
||||
area: measurement.area.toFixed(1),
|
||||
unit: 'Pixel Values',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _getLengthRow(measurement, seriesMetadataMap) {
|
||||
const commonRowItems = _getCommonRowItems(measurement, seriesMetadataMap);
|
||||
const blankColumns = ['', ''];
|
||||
|
||||
return [...commonRowItems, ...blankColumns, measurement.length.toFixed(1)];
|
||||
}
|
||||
|
||||
function _createAndDownloadFile(csvContent) {
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', encodedUri);
|
||||
link.setAttribute('download', 'MeasurementReport.csv');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
}
|
||||
+3
-9
@@ -6,15 +6,9 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ButtonGroup color="black" size="inherit">
|
||||
<Button className="text-base px-2 py-2" onClick={onExportClick}>Export</Button>
|
||||
<IconButton
|
||||
className="px-2"
|
||||
color="black"
|
||||
size="initial"
|
||||
variant="outlined"
|
||||
>
|
||||
<Icon name="arrow-down" />
|
||||
</IconButton>
|
||||
<Button className="text-base px-2 py-2" onClick={onExportClick}>
|
||||
Export
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Button
|
||||
className="ml-2 text-base"
|
||||
|
||||
@@ -7,12 +7,13 @@ import {
|
||||
Input,
|
||||
useViewportGrid,
|
||||
} from '@ohif/ui';
|
||||
import { DicomMetadataStore, DICOMSR, utils } from '@ohif/core';
|
||||
import { DicomMetadataStore, utils } from '@ohif/core';
|
||||
import { useDebounce } from '@hooks';
|
||||
import ActionButtons from './ActionButtons';
|
||||
import { useTrackedMeasurements } from '../../getContextModule';
|
||||
import createReportDialogPrompt from '../../_shared/createReportDialogPrompt';
|
||||
import RESPONSES from '../../_shared/PROMPT_RESPONSES';
|
||||
import downloadCSVReport from '../../_shared/downloadCSVReport';
|
||||
|
||||
const { formatDate } = utils;
|
||||
|
||||
@@ -144,21 +145,12 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
||||
trackedSeries.includes(m.referenceSeriesUID)
|
||||
);
|
||||
|
||||
const promptResult = await createReportDialogPrompt(UIDialogService);
|
||||
|
||||
if (promptResult.action === RESPONSES.CREATE_REPORT) {
|
||||
const additionalFindings = ['ArrowAnnotate'];
|
||||
|
||||
const SeriesDescription =
|
||||
// isUndefinedOrEmpty
|
||||
promptResult.value === undefined || promptResult.value === ''
|
||||
? 'Research Derived Series' // default
|
||||
: promptResult.value; // provided value
|
||||
|
||||
DICOMSR.downloadReport(trackedMeasurements, additionalFindings, {
|
||||
SeriesDescription,
|
||||
});
|
||||
}
|
||||
downloadCSVReport(
|
||||
trackedMeasurements,
|
||||
trackedStudy,
|
||||
trackedSeries,
|
||||
MeasurementService
|
||||
);
|
||||
}
|
||||
|
||||
const jumpToImage = ({ id, isActive }) => {
|
||||
|
||||
Reference in new issue
Block a user