2020-08-19 21:31:25 +02:00
|
|
|
|
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',
|
2020-10-06 03:27:36 +02:00
|
|
|
|
'StudyInstanceUID',
|
|
|
|
|
|
'SeriesInstanceUID',
|
|
|
|
|
|
'SOPInstanceUID',
|
2020-08-19 21:31:25 +02:00
|
|
|
|
'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();
|
2021-12-29 19:51:29 +01:00
|
|
|
|
document.body.removeChild(link);
|
2020-08-19 21:31:25 +02:00
|
|
|
|
}
|