feat: 🎸 DICOM SR STOW on MeasurementAPI (#954)

* feat: 🎸 DICOM SR STOW on MeasurementAPI

Closes: #758

* feat: 🎸 Adding the dataExchance from DICOM SR

* feat: 🎸 Fix some issues and get the retrieve measurements working

* feat: 🎸 Preventing measurement tool helpers to broke because the data is not Number (it comes as string from dcmjs)

* feat: 🎸 Adding a button to start testing SATE functionality

* feat: 🎸Small fixes into the code

* feat: 🎸 MAke sure we get latest SR in the study, even on lazyLoad mode

* Revert "feat: 🎸 Preventing measurement tool helpers to broke because the data is not Number (it comes as string from dcmjs)"

This reverts commit 4b3a1efb5810487d2319826fd59c03d779882b5e.

* feat: 🎸 Make sure after sync we update the viewports and also that annotation tools are set to passive, so it can be drawn in the viewports

* feat: 🎸 Remove console logs and start using OHIF.log

* feat: 🎸 Refactor measurement table to use Snackbar service of messages and make sure we only show save button if server type is dicomWeb

* feat: 🎸 Refactor to remove mocked wadoRoot and use server

* Fix to expose error once we can't save the SR

* Update DCMJS into latest dev version

* Update dcmjs with latest fixes

* Fix PR callouts

* Logging error on MeasurementReport creation

* Preventing from breaking on non supported tools and also warning that some toolType is not sopported

* Small error message update

* Expose better the errors and when we have unsopported tools into the measurements

* Renaming variable

* Expose dcmjs tool support check

* Change the way we warn the unsupportedTools

* Adding style to the button

* Small change on button style

* Fix git conflicts

* Fix after merge with latest master

* Fix after merge with master, btn was not clickable

* Fixing callouts

* Adding util functions into files and refactor with JSDocs

* Refactoring code, adding some basic JSDocs

* Bump dcmjs with latest version and fixes

* JSDocs and small refactors

* yarn lock file after dcmjs update

* Remove unused parameters

* Registering a plain copy of server into measurementAPI

* Small refactor

* Start changing from toolType to toolName

* Rename a function to get instanceMetadata

* Adding error if server is not DICOMWeb on retrieve

* Move snackbar outside of UI component and use UINotificationService

* Refactor and JSDocs

* Small fixes on JSDocs (style)

* Remove unused import

* Improve JSDocs on MeasurementApi

* Remove refactor leftover

* Improve JSDocs and propTypes

* JSDocs and small refactors
This commit is contained in:
Gustavo André Lelis authored and Danny Brown committed 2019-12-11 11:14:01 -05:00
1 parent 5cda5159da
commit ebe1af8d4f
25 files changed
+772 -133

No files matched your search

@@ -3,12 +3,20 @@ import { MeasurementTable } from '@ohif/ui';
import OHIF from '@ohif/core';
import moment from 'moment';
import cornerstone from 'cornerstone-core';
import DICOMSR from './../../lib/DICOMSR';
import jumpToRowItem from './jumpToRowItem.js';
const { setViewportSpecificData } = OHIF.redux.actions;
const { MeasurementApi } = OHIF.measurements;
/**
* Takes a list of objects and a property and return the list grouped by the property
*
* @param {Array} list - The objects to be grouped by
* @param {string} props - The property to group the objects
* @returns {Object}
*/
function groupBy(list, props) {
return list.reduce((a, b) => {
(a[b[props]] = a[b[props]] || []).push(b);
@@ -16,16 +24,29 @@ function groupBy(list, props) {
}, {});
}
function getAllTools() {
const config = OHIF.measurements.MeasurementApi.getConfiguration();
/**
* Takes a list of tools grouped and return all tools separately
*
* @param {Array} [toolGroups=[]] - The grouped tools
* @returns {Array} - The list of all tools on all groups
*/
function getAllTools(toolGroups = []) {
let tools = [];
config.measurementTools.forEach(
toolGroups.forEach(
toolGroup => (tools = tools.concat(toolGroup.childTools))
);
return tools;
}
/**
* Takes measurementData and build the measurement text to be used into the table
*
* @param {Object} [measurementData={}]
* @param {string} measurementData.location - The measurement location
* @param {string} measurementData.description - The measurement description
* @returns {string}
*/
function getMeasurementText(measurementData = {}) {
const defaultText = '...';
const { location = '', description = '' } = measurementData;
@@ -34,6 +55,14 @@ function getMeasurementText(measurementData = {}) {
return result || defaultText;
}
/**
* Takes a list of measurements grouped by measurement numbers and return each measurement data by available timepoint
*
* @param {Array} measurementNumberList - The list of measurements
* @param {Array} timepoints - The list of available timepoints
* @param {Function} displayFunction - The function that builds the display text by each tool
* @returns
*/
function getDataForEachMeasurementNumber(
measurementNumberList,
timepoints,
@@ -56,10 +85,35 @@ function getDataForEachMeasurementNumber(
return data;
}
/**
* Take a measurement toolName and return if any warnings
*
* @param {string} toolName - The tool name
* @returns {string}
*/
function getWarningsForMeasurement(toolName) {
const isToolSupported = DICOMSR.isToolSupported(toolName);
return {
hasWarnings: !isToolSupported,
warningTitle: isToolSupported ? '' : 'Unsupported Tool',
warningList: isToolSupported
? []
: [`${toolName} cannot be persisted at this time`],
};
}
/**
* Take measurements from MeasurementAPI structure and convert into a measurementTable structure
*
* @param {Object} toolCollections - The list of all measurement grouped by groupTool and toolName
* @param {Array} timepoints - The list of available timepoints
* @returns
*/
function convertMeasurementsToTableData(toolCollections, timepoints) {
const config = OHIF.measurements.MeasurementApi.getConfiguration();
const toolGroups = config.measurementTools;
const tools = getAllTools();
const tools = getAllTools(toolGroups);
const tableMeasurements = toolGroups.map(toolGroup => {
return {
@@ -88,6 +142,12 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
} = measurementData;
const measurementId = measurementData._id;
const {
hasWarnings,
warningTitle,
warningList,
} = getWarningsForMeasurement(toolType);
//check if all measurements with same measurementNumber will have same LABEL
const tableMeasurement = {
itemNumber: lesionNamingNumber,
@@ -96,10 +156,10 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
measurementNumber,
lesionNamingNumber,
toolType,
hasWarnings: false, //TODO
warningTitle: '', //TODO
hasWarnings,
warningTitle,
warningList,
isSplitLesion: false, //TODO
warningList: [], //TODO
data: getDataForEachMeasurementNumber(
measurementNumberList,
timepoints,
@@ -126,6 +186,13 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
return tableMeasurements;
}
/**
* Take a list of available timepoints and return a list header information for each timepoint
*
* @param {Array} timepoints - The list of available timepoints
* @param {string} timepoints[].latestDate - The date of the last study taken on the timepoint
* @returns {{label: string, key: string, date: string}[]}
*/
function convertTimepointsToTableData(timepoints) {
if (!timepoints || !timepoints.length) {
return [];
@@ -140,8 +207,28 @@ function convertTimepointsToTableData(timepoints) {
];
}
/**
* Takes server type and return a function or undefined
*
* @param {string} serverType - The server type
* @returns {undefined|Function}
*/
function getSaveFunction(serverType) {
if (serverType === 'dicomWeb') {
return () => {
const measurementApi = OHIF.measurements.MeasurementApi.Instance;
const promise = measurementApi.storeMeasurements();
return promise;
};
}
}
const mapStateToProps = state => {
const { timepoints, measurements } = state.timepointManager;
const { timepointManager, servers } = state;
const { timepoints, measurements } = timepointManager;
const activeServer = servers.servers.find(a => a.active === true);
const saveFunction = getSaveFunction(activeServer.type);
return {
timepoints: convertTimepointsToTableData(timepoints),
measurementCollection: convertMeasurementsToTableData(
@@ -150,6 +237,7 @@ const mapStateToProps = state => {
),
timepointManager: state.timepointManager,
viewports: state.viewports,
saveFunction,
};
};
@@ -265,10 +353,15 @@ const mapDispatchToProps = (dispatch, ownProps) => {
};
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
const { timepoints, saveFunction, measurementCollection } = propsFromState;
const { onSaveComplete, selectedMeasurementNumber } = ownProps;
return {
timepoints: propsFromState.timepoints,
measurementCollection: propsFromState.measurementCollection,
selectedMeasurementNumber: ownProps.selectedMeasurementNumber,
timepoints,
saveFunction,
measurementCollection,
onSaveComplete,
selectedMeasurementNumber,
...propsFromDispatch,
onItemClick: (event, measurementData) => {
// TODO: Add timepointId to .data for measurementData?