LT-115: Considering timepoint ID for measurement numbers

This commit is contained in:
Bruno Alves de Faria 2016-12-30 09:17:00 -02:00
parent d6aeebe712
commit c06043483d
8 changed files with 62 additions and 205 deletions

View File

@ -101,7 +101,7 @@ Template.viewer.onCreated(() => {
Session.set('TimepointsReady', true);
const timepointIds = timepoints.map(t => t.timepointId);
instance.data.measurementApi = new OHIF.measurements.MeasurementApi(instance.data.currentTimepointId);
instance.data.measurementApi = new OHIF.measurements.MeasurementApi(instance.data.timepointApi);
const measurementsPromise = instance.data.measurementApi.retrieveMeasurements(patientId, timepointIds);
measurementsPromise.then(() => {
Session.set('MeasurementsReady', true);

View File

@ -0,0 +1,23 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
export const ToolGroupBaseSchema = new SimpleSchema({
toolId: {
type: String,
label: 'Tool ID'
},
toolItemId: {
type: String,
label: 'Tool Item ID'
},
createdAt: {
type: Date
},
studyInstanceUid: {
type: String,
label: 'Study Instance UID'
},
timepointId: {
type: String,
label: 'Timepoint ID'
}
});

View File

@ -1,23 +1,9 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { ToolGroupBaseSchema } from './baseSchema';
import { nonTarget } from '../tools/nonTarget';
const NonTargetSchema = new SimpleSchema({
toolId: {
type: String,
label: 'Tool ID'
},
toolItemId: {
type: String,
label: 'Tool Item ID'
},
createdAt: {
type: Date
}
});
export const nonTargets = {
id: 'nonTargets',
name: 'Non-Targets',
childTools: [nonTarget],
schema: NonTargetSchema
schema: ToolGroupBaseSchema
};

View File

@ -1,25 +1,11 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { ToolGroupBaseSchema } from './baseSchema';
import { bidirectional } from '../tools/bidirectional';
import { targetCR } from '../tools/targetCR';
import { targetUN } from '../tools/targetUN';
const TargetSchema = new SimpleSchema({
toolId: {
type: String,
label: 'Tool ID'
},
toolItemId: {
type: String,
label: 'Tool Item ID'
},
createdAt: {
type: Date
}
});
export const targets = {
id: 'targets',
name: 'Targets',
childTools: [bidirectional, targetCR, targetUN],
schema: TargetSchema
schema: ToolGroupBaseSchema
};

View File

@ -1,24 +1,10 @@
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { ToolGroupBaseSchema } from './baseSchema';
import { length } from '../tools/length';
import { ellipse } from '../tools/ellipse';
const TempSchema = new SimpleSchema({
toolId: {
type: String,
label: 'Tool ID'
},
toolItemId: {
type: String,
label: 'Tool Item ID'
},
createdAt: {
type: Date
}
});
export const temp = {
id: 'temp',
name: 'Temporary',
childTools: [length, ellipse],
schema: TempSchema
schema: ToolGroupBaseSchema
};

View File

@ -70,7 +70,7 @@ Template.measurementLocationDialog.onCreated(() => {
setMeasurementNumberCallback: getSetMeasurementNumberCallbackFunction(measurementTypeId, measurementApi, timepointApi),
// TODO: Check the position for these, the Add Label button position seems very awkward
getMeasurementLocationCallback: toggleLabel,
changeMeasurementLocationCallback: toggleLabel
changeMeasurementLocationCallback: toggleLabel,
};

View File

@ -14,9 +14,9 @@ class MeasurementApi {
return configuration;
}
constructor(currentTimepointId) {
if (currentTimepointId) {
this.currentTimepointId = currentTimepointId;
constructor(timepointApi) {
if (timepointApi) {
this.timepointApi = timepointApi;
}
this.toolGroups = {};
@ -35,14 +35,24 @@ class MeasurementApi {
this.tools[tool.id] = collection;
collection.find().observe({
added(measurement) {
added: measurement => {
groupCollection.insert({
toolId: tool.id,
toolItemUid: measurement._id,
toolItemId: measurement._id,
timepointId: measurement.timepointId,
studyInstanceUid: measurement.studyInstanceUid,
createdAt: measurement.createdAt
});
const measurementCount = groupCollection.find().count();
const timepoint = this.timepointApi.timepoints.findOne({
timepointId: measurement.timepointId
});
const measurementCount = groupCollection.find({
studyInstanceUid: {
$in: timepoint.studyInstanceUids
}
}).count();
collection.update(measurement._id, {
$set: {
measurementNumber: measurementCount
@ -50,13 +60,20 @@ class MeasurementApi {
});
},
removedAt(measurement, atIndex) {
removedAt: (measurement, atIndex) => {
groupCollection.remove({
toolItemUid: measurement._id
toolItemId: measurement._id
});
const timepoint = this.timepointApi.timepoints.findOne({
timepointId: measurement.timepointId
});
toolGroup.childTools.forEach(childTool => {
this.tools[childTool.id].update({
studyInstanceUid: {
$in: timepoint.studyInstanceUids
},
measurementNumber: {
$gt: atIndex
}
@ -100,7 +117,7 @@ class MeasurementApi {
});
}
storeMeasurements(timepoints) {
storeMeasurements() {
const storeFn = configuration.dataExchange.store;
if (!_.isFunction(storeFn)) {
return;
@ -117,6 +134,7 @@ class MeasurementApi {
});
});
const timepoints = this.timepointApi.all();
const timepointIds = timepoints.map(t => t.timepointId);
const patientId = timepoints[0].patientId;
const filter = {
@ -126,6 +144,7 @@ class MeasurementApi {
}
};
OHIF.log.info('Saving Measurements for timepoints:', timepoints);
storeFn(measurementData, filter).then(() => {
OHIF.log.info('Measurement storage completed');
});
@ -149,144 +168,20 @@ class MeasurementApi {
});
}
// TODO: Create a better function to combine hasDataAtTimepoint and hasNoDataAtTimepoint
// because this doesn't seem very elegant...
hasDataAtTimepoint(collection, timepointId) {
// Retrieve all the data for this Measurement type (e.g. 'targets')
// which was recorded at baseline.
const dataAtTimepoint = collection.find({timepointId});
// Obtain a list of the Measurement Numbers from the
// measurements which have data at this timepoint
const numbers = dataAtTimepoint.map(m => m.measurementNumber);
// Retrieve all the data for this Measurement type which
// match the Measurement Numbers obtained above
const filter = {
measurementNumber: {
$in: numbers
}
};
return collection.find(filter).fetch();
}
hasNoDataAtTimepoint(collection, timepointId) {
// Retrieve all the data for this Measurement type (e.g. 'targets')
// which was recorded at baseline.
const dataAtTimepoint = collection.find({timepointId});
// Obtain a list of the Measurement Numbers from the
// measurements which have data at this timepoint
const numbers = dataAtTimepoint.map(m => m.measurementNumber);
// Retrieve all the data for this Measurement type which
// match the Measurement Numbers obtained above
const filter = {
measurementNumber: {
$nin: numbers
}
};
return collection.find(filter).fetch();
}
sortMeasurements(baselineTimepointId) {
const tools = configuration.measurementTools;
const hasDataAtTimepoint = this.hasDataAtTimepoint;
const hasNoDataAtTimepoint = this.hasNoDataAtTimepoint;
const includedTools = tools.filter(tool => {
return (tool.options && tool.options.caseProgress && tool.options.caseProgress.include);
});
let overallMeasurementNumber = 1;
let specificToolMeasurementNumber = 1;
const updateMeasurementNumber = (collection, toolType) => {
return data => {
const filter = {
measurementNumber: data.measurementNumber,
toolType
}
collection.update(filter, {
$set: {
measurementNumber: specificToolMeasurementNumber
}
});
// Increment the overall measurement number
specificToolMeasurementNumber += 1;
};
};
const updateMeasurementNumberOverall = (collection, toolType) => {
return data => {
const filter = {
measurementNumber: data.measurementNumber,
toolType
}
collection.update(filter, {
$set: {
measurementNumberOverall: overallMeasurementNumber
}
});
// Increment the overall measurement number
overallMeasurementNumber += 1;
};
};
const summarizeMeasurement = (groupObject, toolType) => {
return key => {
return {
measurementNumber: parseInt(key, 10),
entries: groupObject[key],
toolType
};
};
};
// First, update Measurement Number and the displayed Measurements
// Update Measurement the displayed Measurements
includedTools.forEach(tool => {
const collection = this.tools[tool.id];
const toolType = tool.cornerstoneToolType;
const measurements = collection.find({toolType}).fetch();
const groupObject = _.groupBy(measurements, m => m.measurementNumber);
const sortedByMeasurementNumber = Object.keys(groupObject).map(summarizeMeasurement(groupObject, toolType));
sortedByMeasurementNumber.forEach(updateMeasurementNumber(collection, toolType))
const measurements = collection.find().fetch();
measurements.forEach(measurement => {
OHIF.measurements.syncMeasurementAndToolData(measurement);
});
// Reset specificToolMeasurementNumber
specificToolMeasurementNumber = 1;
});
// Next, handle the overall measurement number.
// First, handle data that has a measurement at baseline
includedTools.forEach(tool => {
const collection = this.tools[tool.id];
const toolType = tool.cornerstoneToolType;
const measurements = hasDataAtTimepoint(collection, baselineTimepointId);
const groupObject = _.groupBy(measurements, m => m.measurementNumber);
const sortedByMeasurementNumber = Object.keys(groupObject).map(summarizeMeasurement(groupObject, toolType));
sortedByMeasurementNumber.forEach(updateMeasurementNumberOverall(collection, toolType))
});
// Next, handle New Measurements (i.e. no baseline data)
// Note that this cannot be combined with the loop above due to the incrementing of the overallMeasurementNumber
includedTools.forEach(tool => {
const collection = this.tools[tool.id];
const toolType = tool.cornerstoneToolType;
const measurements = hasNoDataAtTimepoint(collection, baselineTimepointId);
const groupObject = _.groupBy(measurements, m => m.measurementNumber);
const sortedByMeasurementNumber = Object.keys(groupObject).map(summarizeMeasurement(groupObject, toolType));
sortedByMeasurementNumber.forEach(updateMeasurementNumberOverall(collection, toolType));
});
}
@ -322,19 +217,6 @@ class MeasurementApi {
}
});
// Update the measurement numbers for the remaining measurements
const updateFilter = _.clone(filter);
updateFilter.measurementNumber = {
$gt: measurementNumber
};
collection.update(updateFilter, {
$inc: {
measurementNumber: -1
}
}, {
multi: true
});
// Synchronize the updated measurements with Cornerstone Tools
// toolData to make sure the displayed measurements show 'Target X' correctly
const syncFilter = _.clone(updateFilter);

View File

@ -11,16 +11,10 @@ Template.caseProgress.onCreated(() => {
instance.isLocked = new ReactiveVar();
instance.saveData = () => {
const timepointApi = instance.data.timepointApi;
const timepoints = timepointApi.all();
OHIF.log.info('Saving Measurements for timepoints:');
OHIF.log.info(timepoints);
instance.data.measurementApi.storeMeasurements(timepoints);
instance.data.measurementApi.storeMeasurements();
// Clear signaled unsaved changes...
OHIF.ui.unsavedChanges.clear('viewer.studyViewer.measurements.*');
};
instance.unsavedChangesHandler = () => {