LT-115: Considering timepoint ID for measurement numbers
This commit is contained in:
parent
d6aeebe712
commit
c06043483d
@ -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);
|
||||
|
||||
@ -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'
|
||||
}
|
||||
});
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user