From 651d3fa1de424d070e8a2b048ddc50ba9932a64b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Mon, 11 May 2020 19:05:13 -0400 Subject: [PATCH 01/59] Updated testing. Added Jest and Ajv. --- .gitignore | 10 +++++++ README.md | 14 ++++++++++ examples/viewer.example.json | 53 ++++++++++++++++++++++++++++++++++++ package.json | 25 +++++++++++++++-- tests/errorMessage.js | 9 ++++++ tests/jest.ajv.test.js | 50 ++++++++++++++++++++++++++++++++++ tests/setupTests.js | 13 +++++++++ tests/tool.schema.test.js | 32 ++++++++++++++++++++++ tests/viewer.schema.test.js | 19 +++++++++++++ 9 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 examples/viewer.example.json create mode 100644 tests/errorMessage.js create mode 100644 tests/jest.ajv.test.js create mode 100644 tests/setupTests.js create mode 100644 tests/tool.schema.test.js create mode 100644 tests/viewer.schema.test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..297a8b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# misc +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index f15e335..b693130 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,17 @@ If you make changes and tag this project make sure that the version of the ```pa 1. Create migration script for the database and save it the SPINE repository in the folder ```src/backedn/migrations_scripts/```. ***The migration script changes at least the document having the version of the json schema used.*** 1. Create a release for SPINE project including migration instructions from previous releases. Please refer to the migration script in the commit messages. +## Testing with Jest and Ajv +All schemas should be validated with tests, based on Ajv library. Since Jest can’t +specify the error message, in order to display a meaningful error message, +we will have to create a custom matcher to show our own message, returned by Ajv. +It is shown in [Example of matcher extension](/tests/jest.ajv.schema.test.js) + + +More "know-how" can be found here: + +[Test JSON schema with AJV and Jest](https://medium.com/@moshfeu/test-json-schema-with-ajv-and-jest-c1d2984234c9 ) + +[Getting started with JEST](https://jestjs.io/docs/en/getting-started) + +[Getting started with Ajv](https://github.com/ajv-validator/ajv) \ No newline at end of file diff --git a/examples/viewer.example.json b/examples/viewer.example.json new file mode 100644 index 0000000..328f9ef --- /dev/null +++ b/examples/viewer.example.json @@ -0,0 +1,53 @@ +{ + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "NUMBER", + "sliceValue": "middleSliceNumber_key" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayImages": { + "possibleImagesToDisplay": [ + "inputImage_key2" + ], + "hasDefaultImageToDisplay": true, + "defaultImageToDisplay": "inputImage_key2" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Axial", + "value": "AXIAL" + } + ] + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#ff0000", + "hasControlPanel": false + }, + "interactions": { + "activeViewer": { + "controlEnabled": false, + "defaultValue": false + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index f15549c..48e9e70 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,31 @@ "name": "SPINE-json-schema", "author": "Alfredo Morales Pinzon", "license": "MIT", - "keywords": ["SPINE", "schema", "model", "json", "Image Processing", "pipelines", "workflow"], + "keywords": [ + "SPINE", + "schema", + "model", + "json", + "Image Processing", + "pipelines", + "workflow" + ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", "version": "1.0.0", "dependencies": { - + "ajv": "^6.12.2" + }, + "devDependencies": { + "jest": "^26.0.1" + }, + "scripts": { + "test": "jest" + }, + "jest": { + "collectCoverage": true, + "coverageReporters": [ + "html" + ], + "setupFilesAfterEnv": ["/tests/setupTests.js"] } } diff --git a/tests/errorMessage.js b/tests/errorMessage.js new file mode 100644 index 0000000..d5e815e --- /dev/null +++ b/tests/errorMessage.js @@ -0,0 +1,9 @@ +export default function errorMessage (errors) { + return (errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path${error.dataPath}\n schemaPath: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); +} \ No newline at end of file diff --git a/tests/jest.ajv.test.js b/tests/jest.ajv.test.js new file mode 100644 index 0000000..eb86255 --- /dev/null +++ b/tests/jest.ajv.test.js @@ -0,0 +1,50 @@ +var Ajv = require('ajv'); +var ajv = new Ajv(); + +let { it, expect } = global; + +const validateSecuredUrl = function (schema, uri) { + validateSecuredUrl.errors = [{keyword: 'secured', message: 'avatar url must be "https" schema', params: {keyword: 'secured'}}]; + return uri.indexOf('https://') === 0; +}; + +ajv.addKeyword('securedUrl', { + validate: validateSecuredUrl, + errors: true +}); + + +test(`should user's schema be valid`, () => { + const shema = { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullname": { + "type": "string", + "minLength": 2 + }, + "avatar": { + "type": "string", + "format": "uri", + "securedUrl": true, + }, + }, + }, + }; + + const valid = ajv.validate(shema, [ + { + fullname: 'ab', + avatar: "https://mywebsite.com/path/to/avatar" + } + ]); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + expect(valid).toBeValid(errorMessage); +}); \ No newline at end of file diff --git a/tests/setupTests.js b/tests/setupTests.js new file mode 100644 index 0000000..a5ab7a0 --- /dev/null +++ b/tests/setupTests.js @@ -0,0 +1,13 @@ +/** + * Matcher for Ajv. + * Defined here, so can be used globally. + * Thi file has to be referred in package.json + */ +expect.extend({ + toBeValid(isValid, errorMessage) { + return { + message: () => isValid ? '' : errorMessage, + pass: isValid + } + } +}); diff --git a/tests/tool.schema.test.js b/tests/tool.schema.test.js new file mode 100644 index 0000000..6d34664 --- /dev/null +++ b/tests/tool.schema.test.js @@ -0,0 +1,32 @@ +const toolSchema = require('../schemas/tool.schema'); +const data = require( "../examples/segmentationMedullaOblongata/tool_sliceSelectorInAxialDirection"); +const taskSchema = require ("../schemas/task.schema"); +const coreSchema = require ("../schemas/core.schema"); +const viewerSchema = require ("../schemas/viewer.schema"); +const annotationSchema = require ("../schemas/annotation.schema"); +const annotationForm = require ("../schemas/annotationForm.schema"); +const roiSchema = require ("../schemas/roi.schema"); + +let { test, expect ,it} = global; + +var Ajv = require('ajv'); +var ajv = new Ajv(); +ajv.addSchema(coreSchema,'coreSchema'); +ajv.addSchema(taskSchema,'taskSchema'); +ajv.addSchema(annotationSchema,'annotationSchema'); +ajv.addSchema(annotationForm,'annotationForm'); +ajv.addSchema(roiSchema,'roiSchema'); +ajv.addSchema(toolSchema,'toolSchema'); +ajv.addSchema(viewerSchema,'viewerSchema'); + +test('tool schema for tool_sliceSelectorInAxialDirection', () => { + const valid = ajv.validate(toolSchema,data); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + expect(valid).toBeValid(errorMessage); +}); \ No newline at end of file diff --git a/tests/viewer.schema.test.js b/tests/viewer.schema.test.js new file mode 100644 index 0000000..78f414d --- /dev/null +++ b/tests/viewer.schema.test.js @@ -0,0 +1,19 @@ +const schema = require('../schemas/viewer.schema'); +const data = require( "../examples/viewer.example"); +let { test, expect ,it} = global; + +var Ajv = require('ajv'); +var ajv = new Ajv(); + + +test('viewer schema', () => { + const valid = ajv.validate(schema,data); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + expect(valid).toBeValid(errorMessage); +}); From c915d205a529f48653340f99385a874ee0bdae59 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 12 May 2020 11:41:26 -0400 Subject: [PATCH 02/59] Added Istanbul. Created suite for viewer schema test. --- README.md | 5 +- examples/viewer/leptostep1middle.example.json | 56 +++++++++++++++++++ examples/{ => viewer}/viewer.example.json | 6 +- package.json | 8 ++- tests/jest.ajv.test.js | 12 ++-- tests/setupTests.js | 25 +++++++++ tests/tool.schema.test.js | 16 ------ tests/viewer.schema.test.js | 47 +++++++++++----- 8 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 examples/viewer/leptostep1middle.example.json rename examples/{ => viewer}/viewer.example.json (91%) diff --git a/README.md b/README.md index b693130..d26b48a 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,7 @@ More "know-how" can be found here: [Getting started with JEST](https://jestjs.io/docs/en/getting-started) -[Getting started with Ajv](https://github.com/ajv-validator/ajv) \ No newline at end of file +[Getting started with Ajv](https://github.com/ajv-validator/ajv) + +TODO: +Set up coverage as in [Ajv-Istanbul](https://github.com/ajv-validator/ajv-istanbul) \ No newline at end of file diff --git a/examples/viewer/leptostep1middle.example.json b/examples/viewer/leptostep1middle.example.json new file mode 100644 index 0000000..5e6e2be --- /dev/null +++ b/examples/viewer/leptostep1middle.example.json @@ -0,0 +1,56 @@ +{ + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "SUPERIORMOST" + }, + "initialWindowLevel": { + "strategy": "dynamic", + "value": "MIDDLE-FULL" + } + }, + "displayImages": { + "possibleImagesToDisplay": [ + "inputImage_key" + ], + "hasDefaultImageToDisplay": true, + "defaultImageToDisplay": "inputImage_key" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } +} \ No newline at end of file diff --git a/examples/viewer.example.json b/examples/viewer/viewer.example.json similarity index 91% rename from examples/viewer.example.json rename to examples/viewer/viewer.example.json index 328f9ef..d832a2a 100644 --- a/examples/viewer.example.json +++ b/examples/viewer/viewer.example.json @@ -4,8 +4,8 @@ "initialState": { "orientationAndSliceNumber": { "orientation": "AXIAL", - "strategy": "NUMBER", - "sliceValue": "middleSliceNumber_key" + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" }, "initialWindowLevel": { "strategy": "DYNAMIC", @@ -20,7 +20,7 @@ "defaultImageToDisplay": "inputImage_key2" }, "displayControls": { - "orientation": { + "orientations": { "controlVisible": false, "controlEnabled": false, "options": [ diff --git a/package.json b/package.json index 48e9e70..e403886 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "ajv": "^6.12.2" }, "devDependencies": { + "ajv-istanbul": "^0.1.0", "jest": "^26.0.1" }, "scripts": { @@ -27,6 +28,11 @@ "coverageReporters": [ "html" ], - "setupFilesAfterEnv": ["/tests/setupTests.js"] + "globals": { + "ajv": null + }, + "setupFilesAfterEnv": [ + "/tests/setupTests.js" + ] } } diff --git a/tests/jest.ajv.test.js b/tests/jest.ajv.test.js index eb86255..0ecadef 100644 --- a/tests/jest.ajv.test.js +++ b/tests/jest.ajv.test.js @@ -1,5 +1,5 @@ -var Ajv = require('ajv'); -var ajv = new Ajv(); +// var Ajv = require('ajv'); +// var ajv = new Ajv(); let { it, expect } = global; @@ -8,10 +8,10 @@ const validateSecuredUrl = function (schema, uri) { return uri.indexOf('https://') === 0; }; -ajv.addKeyword('securedUrl', { - validate: validateSecuredUrl, - errors: true -}); +// ajv.addKeyword('securedUrl', { +// validate: validateSecuredUrl, +// errors: true +// }); test(`should user's schema be valid`, () => { diff --git a/tests/setupTests.js b/tests/setupTests.js index a5ab7a0..05a85cc 100644 --- a/tests/setupTests.js +++ b/tests/setupTests.js @@ -1,3 +1,15 @@ +const istanbul = require('istanbul'); + + +const taskSchema = require ("../schemas/task.schema"); +const coreSchema = require ("../schemas/core.schema"); +const viewerSchema = require ("../schemas/viewer.schema"); +const annotationSchema = require ("../schemas/annotation.schema"); +const annotationForm = require ("../schemas/annotationForm.schema"); +const roiSchema = require ("../schemas/roi.schema"); +const toolSchema = require('../schemas/tool.schema'); + + /** * Matcher for Ajv. * Defined here, so can be used globally. @@ -11,3 +23,16 @@ expect.extend({ } } }); + +beforeAll(() => { + const Ajv = require('ajv'); + global.ajv = new Ajv(); //setting global variable declared in package.json jest configuration + require('ajv-istanbul')(global.ajv); + ajv.addSchema(coreSchema,'coreSchema'); + ajv.addSchema(taskSchema,'taskSchema'); + ajv.addSchema(annotationSchema,'annotationSchema'); + ajv.addSchema(annotationForm,'annotationForm'); + ajv.addSchema(roiSchema,'roiSchema'); + ajv.addSchema(toolSchema,'toolSchema'); + ajv.addSchema(viewerSchema,'viewerSchema'); +}); diff --git a/tests/tool.schema.test.js b/tests/tool.schema.test.js index 6d34664..85ee7f1 100644 --- a/tests/tool.schema.test.js +++ b/tests/tool.schema.test.js @@ -1,24 +1,8 @@ const toolSchema = require('../schemas/tool.schema'); const data = require( "../examples/segmentationMedullaOblongata/tool_sliceSelectorInAxialDirection"); -const taskSchema = require ("../schemas/task.schema"); -const coreSchema = require ("../schemas/core.schema"); -const viewerSchema = require ("../schemas/viewer.schema"); -const annotationSchema = require ("../schemas/annotation.schema"); -const annotationForm = require ("../schemas/annotationForm.schema"); -const roiSchema = require ("../schemas/roi.schema"); let { test, expect ,it} = global; -var Ajv = require('ajv'); -var ajv = new Ajv(); -ajv.addSchema(coreSchema,'coreSchema'); -ajv.addSchema(taskSchema,'taskSchema'); -ajv.addSchema(annotationSchema,'annotationSchema'); -ajv.addSchema(annotationForm,'annotationForm'); -ajv.addSchema(roiSchema,'roiSchema'); -ajv.addSchema(toolSchema,'toolSchema'); -ajv.addSchema(viewerSchema,'viewerSchema'); - test('tool schema for tool_sliceSelectorInAxialDirection', () => { const valid = ajv.validate(toolSchema,data); const errorMessage = (ajv.errors || []).map(error => { diff --git a/tests/viewer.schema.test.js b/tests/viewer.schema.test.js index 78f414d..9de72ea 100644 --- a/tests/viewer.schema.test.js +++ b/tests/viewer.schema.test.js @@ -1,19 +1,36 @@ const schema = require('../schemas/viewer.schema'); -const data = require( "../examples/viewer.example"); -let { test, expect ,it} = global; +const data1 = require( "../examples/viewer/viewer.example"); +const data2 = require( "../examples/viewer/leptostep1middle.example"); +let { test, expect ,it, describe} = global; -var Ajv = require('ajv'); -var ajv = new Ajv(); +// Uncomment below if you want to override ajv for some reasons!!! +// var Ajv = require('ajv'); +// var ajv = new Ajv(); -test('viewer schema', () => { - const valid = ajv.validate(schema,data); - const errorMessage = (ajv.errors || []).map(error => { - try { - return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; - } catch (error) { - return error.message; - } - }).join('\n'); - expect(valid).toBeValid(errorMessage); -}); +describe('Viewer schemas', () => { + + test('Simple viewer', () => { + const valid = ajv.validate(schema, data1); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + expect(valid).toBeValid(errorMessage); + }); + + test('Lepto project, step1, Middle viewer', () => { + const valid = ajv.validate(schema, data2); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + expect(valid).toBeValid(errorMessage); + }); +}); \ No newline at end of file From fc862826462c4ada3985b2038297119c4144fd7a Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Wed, 13 May 2020 12:59:31 -0400 Subject: [PATCH 03/59] Added new matchers for quick tests. Ajv can be used globally and locally (example in jest.ajv.test) --- README.md | 7 +- examples/widgets/full.example.json | 84 ++++++++++++++++++++++++ examples/widgets/leptostep1.example.json | 62 +++++++++++++++++ examples/widgets/mutation.example.json | 13 ++++ schemas/widget.schema.json | 70 ++++++++++++++++++++ tests/jest.ajv.test.js | 26 +++++--- tests/setupTests.js | 32 ++++++++- tests/tool.schema.test.js | 11 +--- tests/viewer.schema.test.js | 20 +----- tests/widget.schema.test.js | 22 +++++++ 10 files changed, 308 insertions(+), 39 deletions(-) create mode 100644 examples/widgets/full.example.json create mode 100644 examples/widgets/leptostep1.example.json create mode 100644 examples/widgets/mutation.example.json create mode 100644 schemas/widget.schema.json create mode 100644 tests/widget.schema.test.js diff --git a/README.md b/README.md index d26b48a..46771f1 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,13 @@ If you make changes and tag this project make sure that the version of the ```pa All schemas should be validated with tests, based on Ajv library. Since Jest can’t specify the error message, in order to display a meaningful error message, we will have to create a custom matcher to show our own message, returned by Ajv. -It is shown in [Example of matcher extension](/tests/jest.ajv.schema.test.js) +It is shown in [Example of matcher extension](/tests/jest.ajv.schema.test.js). +### Test cases +Examples used for testing are defined in "examples" subfolders. To identify weak tests +[Mutation tests](https://en.wikipedia.org/wiki/Mutation_testing) are applied (json files with _mutation._ prefix). -More "know-how" can be found here: +### Links: [Test JSON schema with AJV and Jest](https://medium.com/@moshfeu/test-json-schema-with-ajv-and-jest-c1d2984234c9 ) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json new file mode 100644 index 0000000..80c9830 --- /dev/null +++ b/examples/widgets/full.example.json @@ -0,0 +1,84 @@ +{ + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": true, + "controlEnabled": true + }, + "subAnnotationsAvailable":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "centering": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "selectionOnly": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "constraints":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": {"criterion_key1": {"controlElement":"viewer","property":"sliceNumber","operator": "==="}, + "criterion_key2": {"controlElement":"viewer","property":"imageId","operator": "==="} + } + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationTableTool": { + "controlVisible": false, + "controlEnabled": false, + "properties": { + "fullTable": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + } + } + } +} \ No newline at end of file diff --git a/examples/widgets/leptostep1.example.json b/examples/widgets/leptostep1.example.json new file mode 100644 index 0000000..8aaac82 --- /dev/null +++ b/examples/widgets/leptostep1.example.json @@ -0,0 +1,62 @@ +{ + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": true, + "controlEnabled": true + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationTableTool": { + "controlVisible": false, + "controlEnabled": false, + "properties": { + "fullTable": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + } + } + } +} \ No newline at end of file diff --git a/examples/widgets/mutation.example.json b/examples/widgets/mutation.example.json new file mode 100644 index 0000000..8e86b19 --- /dev/null +++ b/examples/widgets/mutation.example.json @@ -0,0 +1,13 @@ +{ + "pinTool": { + "controlVisible": true, + "controlEnable": true, + "properties": { + "markerSize": { + "controlVisible": "true", + "controlEnabled": true, + "defaultValue": "4" + } + } + } +} \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json new file mode 100644 index 0000000..13f69d0 --- /dev/null +++ b/schemas/widget.schema.json @@ -0,0 +1,70 @@ +{ + "$id":"https://raw.githubusercontent.com/SPINEProject/SPINE-json-schema/master/schemas/widget.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Widget schema", + "description": "Schema for Widget Tools", + "type": "object", + "definitions":{ + "toolbarControls": { + "$id": "#toolbarControls", + "description": "Boolean flags defining controls for a given widget.", + "$comment": "So far control components for widgets are implemented with Toggleable Buttons. Model can be extended by adding other complex controls, eg. sets of buttons, dropdown etc.", + "properties": { + "controlVisible": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is visible." + }, + "controlEnabled": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is enabled, ie. can change a state. If not enabled, then works only as indicator." + } + }, + "required": ["controlEnabled", "controlVisible"] + }, + "markerSize": { + "$id":"#markerSize", + "description": "Property defining size of pin (marker).", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer" + } + }, + "required": ["defaultValue"] + }, + "fontSize": { + "$id":"#fontSize", + "description": "Property defining size of label for Pin.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer" + } + }, + "required": ["defaultValue"] + }, + "pinToolProperties": { + "$id":"#pinToolProperties", + "description": "Set of properties for Pin Tool.", + "type": "object", + "properties": { + "markerSize": {"$ref": "#/definitions/markerSize"}, + "fontSize": {"$ref": "#/definitions/fontSize"} + }, + "required": ["markerSize","fontSize"] + }, + "pinTool": { + "$id": "#pinTool", + "description": "Tool for pin management.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/pinToolProperties"} + }, + "required": ["properties"] + } + }, + "properties": { + "pinTool": { "$ref": "#/definitions/pinTool"} + } +} \ No newline at end of file diff --git a/tests/jest.ajv.test.js b/tests/jest.ajv.test.js index 0ecadef..66ea127 100644 --- a/tests/jest.ajv.test.js +++ b/tests/jest.ajv.test.js @@ -1,5 +1,11 @@ -// var Ajv = require('ajv'); -// var ajv = new Ajv(); +/** + *This test shows how to use Ajv variables locally - error messages, validate and others can be customized + * in files + * + */ + +var Ajv = require('ajv'); //We need to override ajv, if we want to customize it +var ajv = new Ajv(); let { it, expect } = global; @@ -8,10 +14,10 @@ const validateSecuredUrl = function (schema, uri) { return uri.indexOf('https://') === 0; }; -// ajv.addKeyword('securedUrl', { -// validate: validateSecuredUrl, -// errors: true -// }); +ajv.addKeyword('securedUrl', { + validate: validateSecuredUrl, + errors: true +}); test(`should user's schema be valid`, () => { @@ -36,9 +42,11 @@ test(`should user's schema be valid`, () => { const valid = ajv.validate(shema, [ { fullname: 'ab', - avatar: "https://mywebsite.com/path/to/avatar" + avatar: "http://mywebsite.com/path/to/avatar" } ]); + + // this function can be used globally const errorMessage = (ajv.errors || []).map(error => { try { return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; @@ -46,5 +54,7 @@ test(`should user's schema be valid`, () => { return error.message; } }).join('\n'); - expect(valid).toBeValid(errorMessage); + + + expect(valid).not.toBeValid(errorMessage); // we want to have error in "http" - uncomment "not" to see error and message }); \ No newline at end of file diff --git a/tests/setupTests.js b/tests/setupTests.js index 05a85cc..769f48c 100644 --- a/tests/setupTests.js +++ b/tests/setupTests.js @@ -13,7 +13,7 @@ const toolSchema = require('../schemas/tool.schema'); /** * Matcher for Ajv. * Defined here, so can be used globally. - * Thi file has to be referred in package.json + * This file has to be referred in package.json */ expect.extend({ toBeValid(isValid, errorMessage) { @@ -24,6 +24,36 @@ expect.extend({ } }); + +/** + * Matcher for Ajv. + * Defined here, so can be used globally. + * This file has to be referred in package.json + */ +expect.extend({ + toBeAjvValid(toBeValid, schema, example) { + const valid = ajv.validate(schema, example); + const errorMessage = (ajv.errors || []).map(error => { + try { + return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + } catch (error) { + return error.message; + } + }).join('\n'); + if(toBeValid) + return { + message: () => valid ? '' : errorMessage, + pass: valid + }; + else + return { + message: errorMessage, + pass: !valid + }; + } +}); + + beforeAll(() => { const Ajv = require('ajv'); global.ajv = new Ajv(); //setting global variable declared in package.json jest configuration diff --git a/tests/tool.schema.test.js b/tests/tool.schema.test.js index 85ee7f1..f212cb4 100644 --- a/tests/tool.schema.test.js +++ b/tests/tool.schema.test.js @@ -1,16 +1,7 @@ const toolSchema = require('../schemas/tool.schema'); -const data = require( "../examples/segmentationMedullaOblongata/tool_sliceSelectorInAxialDirection"); let { test, expect ,it} = global; test('tool schema for tool_sliceSelectorInAxialDirection', () => { - const valid = ajv.validate(toolSchema,data); - const errorMessage = (ajv.errors || []).map(error => { - try { - return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; - } catch (error) { - return error.message; - } - }).join('\n'); - expect(valid).toBeValid(errorMessage); + expect(true).toBeAjvValid(toolSchema,require( "../examples/segmentationMedullaOblongata/tool_sliceSelectorInAxialDirection")); }); \ No newline at end of file diff --git a/tests/viewer.schema.test.js b/tests/viewer.schema.test.js index 9de72ea..2f61fab 100644 --- a/tests/viewer.schema.test.js +++ b/tests/viewer.schema.test.js @@ -11,26 +11,10 @@ let { test, expect ,it, describe} = global; describe('Viewer schemas', () => { test('Simple viewer', () => { - const valid = ajv.validate(schema, data1); - const errorMessage = (ajv.errors || []).map(error => { - try { - return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; - } catch (error) { - return error.message; - } - }).join('\n'); - expect(valid).toBeValid(errorMessage); + expect(true).toBeAjvValid(schema, data1); }); test('Lepto project, step1, Middle viewer', () => { - const valid = ajv.validate(schema, data2); - const errorMessage = (ajv.errors || []).map(error => { - try { - return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; - } catch (error) { - return error.message; - } - }).join('\n'); - expect(valid).toBeValid(errorMessage); + expect(true).toBeAjvValid(schema, data2); }); }); \ No newline at end of file diff --git a/tests/widget.schema.test.js b/tests/widget.schema.test.js new file mode 100644 index 0000000..9cad653 --- /dev/null +++ b/tests/widget.schema.test.js @@ -0,0 +1,22 @@ +const schema = require('../schemas/widget.schema'); +let { test, expect ,describe} = global; + +// Uncomment below if you want to override ajv for some reasons!!! +// var Ajv = require('ajv'); +// var ajv = new Ajv(); + + +describe('Widget schemas', () => { + + test('Full widget configuration', () => { + expect(true).toBeAjvValid(schema, require( "../examples/widgets/full.example")); + }); + + test('Lepto project, step1, widgets', () => { + expect(true).toBeAjvValid(schema, require( "../examples/widgets/leptostep1.example")); + }); + + test('Mutation test for widgets3', () => { + expect(true).not.toBeAjvValid(schema, require( "../examples/widgets/mutation.example")); // mutation, so valid condition is negated + }); +}); \ No newline at end of file From 8aed122eb95881d79b2b430f00fd5cd1e637a80f Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Wed, 13 May 2020 15:31:02 -0400 Subject: [PATCH 04/59] Added AJV parameters to message for Jest. --- tests/setupTests.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/setupTests.js b/tests/setupTests.js index 769f48c..50282b6 100644 --- a/tests/setupTests.js +++ b/tests/setupTests.js @@ -35,7 +35,12 @@ expect.extend({ const valid = ajv.validate(schema, example); const errorMessage = (ajv.errors || []).map(error => { try { - return `AJV error: keyword: ${error.keyword}\n message: ${error.message} \n data path: ${error.dataPath}\n schema path: ${error.schemaPath}\n`; + const params = (Object.keys(error.params) || []).map(param => {return param.concat("=",error.params[param])}).join('\n'); + return `AJV: keyword: ${error.keyword}\n` + +`message: ${error.message} \n` + +`data path: ${error.dataPath}\n` + +`schema path: ${error.schemaPath}\n` + +`parameters: ${params}\n`; } catch (error) { return error.message; } From 285e8c674a951701cb0c43c712a015541ed6523e Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 09:16:49 -0400 Subject: [PATCH 05/59] Added widgets. --- examples/widgets/full.example.json | 25 +++- schemas/widget.schema.json | 190 ++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 4 deletions(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 80c9830..aeb53f6 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -35,7 +35,8 @@ "constraints":{ "controlVisible": false, "controlEnabled": false, - "defaultValue": {"criterion_key1": {"controlElement":"viewer","property":"sliceNumber","operator": "==="}, + "defaultValue": { + "criterion_key1": {"controlElement":"viewer","property":"sliceNumber","operator": "==="}, "criterion_key2": {"controlElement":"viewer","property":"imageId","operator": "==="} } } @@ -50,6 +51,28 @@ "controlVisible": true, "controlEnabled": true, "defaultValue": 1 + }, + "rangeForKey": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 5 + } + } + }, + "customCursorTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "whiskers" + }, + "diameter": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 3 } } }, diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 13f69d0..708f5a4 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -43,15 +43,179 @@ }, "required": ["defaultValue"] }, + "clearAllPins": { + "$id":"#clearAllPins", + "description": "Property defining whether Button for clearing pins should be visible.", + "allOf": [{ "$ref": "#toolbarControls" }] + }, + "subAnnotationsAvailable": { + "$id":"#subAnnotationsAvailable", + "description": "Property defining whether pins for subannotations should be visible.", + "$comment": "So far controlVisible/controlEnabled are ignored, since functionality can be set only in widget configuration. There is no control component in frontend to switch on/off.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + }, + "required": ["defaultValue"] + }, + "pinCentering": { + "$id":"#pinCentering", + "description": "Property defining whether view should be centered on an active pins when navigating forward/backward, or using ROI buttons in Annotation Table", + "$comment": "So far controlVisible/controlEnabled are ignored, since functionality can be set only in widget configuration. There is no control component in frontend to switch on/off.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + }, + "required": ["defaultValue"] + }, + "pinSelectionOnly": { + "$id":"#pinSelectionOnly", + "description": "Optional property defining whether pins are in read-only mode (selection). Default is false. If pins are in selectionOly mode, they cannot be added with mouse left click. This can be useful in situation when pinTool Toggle button is not visible, and tool is on.", + "$comment": "So far controlVisible/controlEnabled are ignored, since functionality can be set only in widget configuration. There is no control component in frontend to switch on/off.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + }, + "required": ["defaultValue"] + }, + "pinCriterion":{ + "$id":"#pinCriterion", + "type": "object", + "description": "Single criterion used to constrain visibility of Pins.", + "$comment": "Can be reorganized in a way that control elements will have their own specific syntax and schema.", + "additionalProperties": false, + "properties": { + "controlElement": { + "type": "string", + "description": "Tool element which state is used to calculate criterion.", + "enum": ["viewer"], + "$comment": "So far it is defined for viewer only. In future can be extended for tools, widgets state, etc." + }, + "property": { + "type": "string", + "description": "Property of control element which state is used to calculate criterion. For example viewer['sliceNumber'] can be calculated.", + "enum": ["sliceNumber","imageId","orientation"], + "$comment": "Can be easily extended to other viewer state properties." + }, + "operator": { + "type": "string", + "description": "Operator used to calculate Predicate.", + "enum": ["==="], + "$comment": "Should be merged with Experiment.schema definitions." + } + } + }, + "pinCriteria": { + "$id":"#pinCriteria", + "type": "object", + "patternProperties" : { + ".*" : { + "type": "object", + "$ref": "#/definitions/pinCriterion" + } + }, + "examples": [ + { + "criterion_key1": {"controlElement":"viewer","property":"sliceNumber","operator": "==="}, + "criterion_key2": {"controlElement":"viewer","property":"imageId","operator": "==="} + } + ] + }, + "pinConstraints": { + "$id":"#pinConstraints", + "description": "Optional property defining additional conditions for displaying pins. Default is turned off. See example below.", + "$comment": "So far controlVisible/controlEnabled are ignored, since functionality can be set only in widget configuration. There is no control component in frontend to switch on/off.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": {"$ref": "#pinCriteria"} + }, + "required": ["defaultValue"] + }, + "projectionRange": { + "$id":"#projectionRange", + "description": "Optional property defining range of projection, as number of surrounding slices.", + "$comment": "So far controlVisible/controlEnabled are ignored, since functionality can be set only in widget configuration. There is no control component in frontend to switch on/off.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": {"type": "integer"} + }, + "required": ["defaultValue"] + }, + "customCursorType": { + "$id":"#customCursorType", + "description": "Property defining type of cursor.", + "$comment": "Custom cursor might have different shape and implementation.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "string", + "enum": ["crosshair","circle","whiskers","checkered","twoEllipses","animatedEllipse","rotatedEllipse","fixedCrosshairRelativeCircle","dottedCrosshair"] + } + }, + "required": ["defaultValue"] + }, + "customCursorDiameter": { + "$id":"#customCursorDiameter", + "description": "Property defining diameter of cursor.", + "$comment": "This does not have to apply to all of the shapes.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer" + } + }, + "required": ["defaultValue"] + }, "pinToolProperties": { "$id":"#pinToolProperties", "description": "Set of properties for Pin Tool.", "type": "object", + "additionalProperties": false, "properties": { "markerSize": {"$ref": "#/definitions/markerSize"}, - "fontSize": {"$ref": "#/definitions/fontSize"} + "fontSize": {"$ref": "#/definitions/fontSize"}, + "clearAll": {"$ref":"#/definitions/clearAllPins"}, + "selectionOnly": {"$ref":"#/definitions/pinSelectionOnly"}, + "subAnnotationsAvailable": {"$ref":"#/definitions/subAnnotationsAvailable"}, + "centering": {"$ref":"#/definitions/pinCentering"}, + "constraints": {"$ref": "#/definitions/pinConstraints"} }, - "required": ["markerSize","fontSize"] + "required": ["markerSize","fontSize","clearAll"] + }, + "projectionToolProperties": { + "$id":"#projectionToolProperties", + "description": "Set of properties for Projection Tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "range": { + "$ref": "#/definitions/projectionRange" + }, + "rangeForKey": { + "$ref": "#/definitions/projectionRange", + "description": "As in /projectionRange description, with difference that applied only to set range when 'P' key is pressed." + } + } + }, + "customCursorToolProperties": { + "$id":"#customCursorToolProperties", + "description": "Set of properties for Custom Cursor Tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/customCursorType" + }, + "diameter": { + "$ref": "#/definitions/customCursorDiameter" + } + } }, "pinTool": { "$id": "#pinTool", @@ -62,9 +226,29 @@ "properties": { "$ref": "#/definitions/pinToolProperties"} }, "required": ["properties"] + }, + "projectionTool": { + "$id": "#projectionTool", + "description": "Tool for projection management.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/projectionToolProperties"} + } + }, + "customCursorTool": { + "$id": "#customCursorTool", + "description": "Tool for custom cursor management.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/customCursorToolProperties"} + } } }, "properties": { - "pinTool": { "$ref": "#/definitions/pinTool"} + "pinTool": { "$ref": "#/definitions/pinTool"}, + "projectionTool": { "$ref": "#/definitions/projectionTool"}, + "customCursorTool": { "$ref": "#/definitions/customCursorTool"} } } \ No newline at end of file From 791e0cbc5641ab6e5bf56401e5d6f6a0775567e9 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 11:50:32 -0400 Subject: [PATCH 06/59] Added pointerTool, crosshairTool and brushTool to widgets. --- examples/widgets/full.example.json | 11 ++++ schemas/widget.schema.json | 83 +++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index aeb53f6..63b46a3 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -93,6 +93,17 @@ "controlEnabled": true, "defaultValue": false }, + "brushTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "size": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, "annotationTableTool": { "controlVisible": false, "controlEnabled": false, diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 708f5a4..76187fb 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -172,6 +172,30 @@ }, "required": ["defaultValue"] }, + "pointerMode": { + "$id":"#pointerMode", + "description": "Property defining mode (type of interaction) of pointer tool.", + "$comment": "Modes are related to UI event types. There might be possible to define modeOptions in future.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "string", + "enum": ["onClick","onMove"] + } + }, + "required": ["defaultValue"] + }, + "brushSize": { + "$id":"#brushSize", + "description": "Property defining size of brush.", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer" + } + }, + "required": ["defaultValue"] + }, "pinToolProperties": { "$id":"#pinToolProperties", "description": "Set of properties for Pin Tool.", @@ -217,6 +241,29 @@ } } }, + "pointerToolProperties": { + "$id":"#pointerToolProperties", + "description": "Set of properties for Pointer Tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "$ref": "#/definitions/pointerMode" + } + } + }, + "brushToolProperties": { + "$id":"#brushToolProperties", + "description": "Set of properties for Brush Tool.", + "$comment": "Properties as threshold, colormap LUT, layer are moved out from here.", + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "$ref": "#/definitions/brushSize" + } + } + }, "pinTool": { "$id": "#pinTool", "description": "Tool for pin management.", @@ -244,11 +291,45 @@ "properties": { "properties": { "$ref": "#/definitions/customCursorToolProperties"} } + }, + "pointerTool": { + "$id": "#pointerTool", + "description": "Tool for pointer management. Pointer is changing slices in remaining viewers according to intersection of slices at pointed voxel.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/pointerToolProperties"} + } + }, + "crossHairTool": { + "$id": "#crossHairTool", + "description": "Tool for displaying indication of slices.", + "$comment": "It can be defined in viewer's interaction schema with a different syntax.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + } + }, + "brushTool": { + "$id": "#brushTool", + "description": "Tool for drawing in segmentation layer.", + "$comment": "TODO Design inputs and widget to handle state of colormap LUT, thresholding intensities when drawing and layers. There will be missing props here!", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/brushToolProperties"} + } } }, "properties": { "pinTool": { "$ref": "#/definitions/pinTool"}, "projectionTool": { "$ref": "#/definitions/projectionTool"}, - "customCursorTool": { "$ref": "#/definitions/customCursorTool"} + "customCursorTool": { "$ref": "#/definitions/customCursorTool"}, + "pointerTool": { "$ref": "#/definitions/pointerTool"}, + "crossHairTool": {"$ref": "#/definitions/crossHairTool"}, + "brushTool": {"$ref": "#/definitions/brushTool"} } } \ No newline at end of file From f7d3bc8139dbd620229108c3e514cdf9c060eb1f Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 16:20:34 -0400 Subject: [PATCH 07/59] Updated widgets. --- examples/widgets/full.example.json | 46 ++++++++- schemas/widget.schema.json | 145 ++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 6 deletions(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 63b46a3..258ad73 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -104,14 +104,52 @@ } } }, + "fillingTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "eraserTool":{ + "controlVisible":true, + "controlEnabled":true, + "properties":{ + "size":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "labelmapUndoRedoTool": { + "controlVisible": true, + "controlEnabled": true + }, + "labelmapOpacity": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, "annotationTableTool": { - "controlVisible": false, - "controlEnabled": false, + "controlVisible": true, + "controlEnabled": true, "properties": { "fullTable": { - "controlVisible": false, - "controlEnabled": false, + "controlVisible": true, + "controlEnabled": true, "defaultValue": false + }, + "editableROI": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": true + }, + "subAnnotationColumn":{ + "controlVisible": true, + "controlEnabled": false + }, + "csvExport": { + "controlVisible": false, + "controlEnabled": false } } } diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 76187fb..7d8c0c1 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -4,6 +4,7 @@ "title": "Widget schema", "description": "Schema for Widget Tools", "type": "object", + "additionalProperties": false, "definitions":{ "toolbarControls": { "$id": "#toolbarControls", @@ -187,7 +188,7 @@ }, "brushSize": { "$id":"#brushSize", - "description": "Property defining size of brush.", + "description": "Property defining size. Can be used to define brush or eraser tool size.", "allOf": [{ "$ref": "#toolbarControls" }], "properties": { "defaultValue": { @@ -264,6 +265,79 @@ } } }, + "eraserToolProperties": { + "$id":"#eraserToolProperties", + "description": "Set of properties for Eraser Tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "$ref": "#/definitions/brushSize", + "$comment": "Identical to brush size property." + } + } + }, + "annotationTableProperties": { + "$id":"#annotationTableProperties", + "description": "Set of properties for annotation table component.", + "type": "object", + "additionalProperties": false, + "properties": { + "fullTable": { + "description": "Property defining whether 'full table mode' should be visible initially. Full table mode shows both 'active row' and full table with annotations.", + "$comment": "Control enabled/visible properties define whether full table can be displayed. If they set to false,and defaultValue is false, then full table is not visible.", + "allOf": [ + { + "$ref": "#toolbarControls" + } + ], + "properties": { + "defaultValue": { + "type": "boolean" + } + }, + "required": [ + "defaultValue" + ] + }, + "editableROI": { + "description": "Property defining whether rows can be modified/deleted.", + "$comment": "If property not defined, default is true. Control visible/enabled defined but not used here.", + "allOf": [ + { + "$ref": "#toolbarControls" + } + ], + "properties": { + "defaultValue": { + "type": "boolean", + "default": true + } + }, + "required": [ + "defaultValue" + ] + }, + "subAnnotationColumn": { + "description": "Property defining whether column to set active roi to generate subannotations with mouse left click should be visible.", + "$comment": "controlVisible/enabled define full control here.", + "allOf": [ + { + "$ref": "#toolbarControls" + } + ] + }, + "csvExport": { + "description": "Property defining whether button with CSV Export functionality should be rendered and working.", + "$comment": "controlVisible/enabled define full control here.", + "allOf": [ + { + "$ref": "#toolbarControls" + } + ] + } + } + }, "pinTool": { "$id": "#pinTool", "description": "Tool for pin management.", @@ -320,8 +394,70 @@ "type": "object", "allOf": [{ "$ref": "#toolbarControls" }], "properties": { + "defaultValue": { + "type": "boolean", + "description": "Optional parameter defining whether widget should be on when view is initialised. This can be overwritten by other elements - brushTool, fillingTool, eraserTool are mutually exclusive - only one of them can be on at te same time.", + "default": false + }, "properties": { "$ref": "#/definitions/brushToolProperties"} } + }, + "eraserTool": { + "$id": "#eraserTool", + "description": "Tool for erasing in segmentation layer. By default it uses 0 to fill erased voxels.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean", + "description": "Optional parameter defining whether widget should be on when view is initialised. This can be overwritten by other elements - brushTool, fillingTool, eraserTool are mutually exclusive - only one of them can be on at te same time.", + "default": false + }, + "properties": { "$ref": "#/definitions/eraserToolProperties"} + } + }, + "fillingTool": { + "$id": "#fillingTool", + "description": "Tool for filling in closed shapes in segmentation layer.", + "$comment": "Comments as in #brushTool.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean", + "description": "Optional parameter defining whether widget should be on when view is initialised. This can be overwritten by other elements - brushTool, fillingTool, eraserTool are mutually exclusive - only one of them can be on at te same time.", + "default": false + } + } + }, + "labelmapUndoRedoTool": { + "$id": "#labelmapUndoRedoTool", + "description": "Widget displaying history controls for segmentation.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }] + }, + "labelmapOpacity": { + "$id": "#labelmapOpacity", + "description": "Widget for control over labelmap opacity.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "number", + "description": "Opacity value.", + "minimum": 0, + "maximum": 1 + } + } + }, + "annotationTableTool": { + "$id": "#annotationTableTool", + "description": "Widget for control over annotation table component", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/annotationTableProperties"} + } } }, "properties": { @@ -330,6 +466,11 @@ "customCursorTool": { "$ref": "#/definitions/customCursorTool"}, "pointerTool": { "$ref": "#/definitions/pointerTool"}, "crossHairTool": {"$ref": "#/definitions/crossHairTool"}, - "brushTool": {"$ref": "#/definitions/brushTool"} + "brushTool": {"$ref": "#/definitions/brushTool"}, + "eraserTool": {"$ref": "#/definitions/eraserTool"}, + "fillingTool": {"$ref": "#/definitions/fillingTool"}, + "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, + "labelmapOpacity": {"$ref": "#/definitions/labelmapOpacity"}, + "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"} } } \ No newline at end of file From fd7a7f9ba51fd221a5cf7ab3717253881b9679ba Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 16:36:20 -0400 Subject: [PATCH 08/59] Updated widgets. --- examples/widgets/full.example.json | 5 +++++ schemas/widget.schema.json | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 258ad73..78f58d8 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -129,6 +129,11 @@ "controlEnabled": true, "defaultValue": 1 }, + "annotationOpacity": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 1 + }, "annotationTableTool": { "controlVisible": true, "controlEnabled": true, diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 7d8c0c1..bae858c 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -450,6 +450,20 @@ } } }, + "annotationOpacity": { + "$id": "#annotationOpacity", + "description": "Widget for control over annotation opacity.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "number", + "description": "Opacity value.", + "minimum": 0, + "maximum": 1 + } + } + }, "annotationTableTool": { "$id": "#annotationTableTool", "description": "Widget for control over annotation table component", @@ -471,6 +485,7 @@ "fillingTool": {"$ref": "#/definitions/fillingTool"}, "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, "labelmapOpacity": {"$ref": "#/definitions/labelmapOpacity"}, + "annotationOpacity": {"$ref": "#/definitions/annotationOpacity"}, "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"} } } \ No newline at end of file From 00222ae4c87be96d3ef11fb4cafe0061193d058c Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 16:46:31 -0400 Subject: [PATCH 09/59] Updated widgets. --- examples/widgets/full.example.json | 5 +++++ schemas/widget.schema.json | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 78f58d8..debf463 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -157,5 +157,10 @@ "controlEnabled": false } } + }, + "echoTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index bae858c..5527982 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -402,6 +402,18 @@ "properties": { "$ref": "#/definitions/brushToolProperties"} } }, + "echoTool": { + "$id": "#echoTool", + "description": "Widget adding cursor echoes on remaining fields. Widget controls camera too - centering and zooming are projected fro main viewer to others.", + "$comment": "Widget works properly only if viewers are set in the same orientation and displaying co-registered images.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + } + }, "eraserTool": { "$id": "#eraserTool", "description": "Tool for erasing in segmentation layer. By default it uses 0 to fill erased voxels.", @@ -481,6 +493,7 @@ "pointerTool": { "$ref": "#/definitions/pointerTool"}, "crossHairTool": {"$ref": "#/definitions/crossHairTool"}, "brushTool": {"$ref": "#/definitions/brushTool"}, + "echoTool": {"$ref": "#/definitions/echoTool"}, "eraserTool": {"$ref": "#/definitions/eraserTool"}, "fillingTool": {"$ref": "#/definitions/fillingTool"}, "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, From 5b30f8e98bb92ff73f1ff681e55990fdb21cd7a3 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 17:04:48 -0400 Subject: [PATCH 10/59] Updated widgets. --- examples/widgets/full.example.json | 5 +++++ schemas/widget.schema.json | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index debf463..314df48 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -162,5 +162,10 @@ "controlVisible": true, "controlEnabled": true, "defaultValue": false + }, + "linkAllTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 5527982..8b815ce 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -462,6 +462,17 @@ } } }, + "linkAllTool": { + "$id": "#linkAllTool", + "description": "Widget linking all viewers (slice change is applied to all viewers). Turning on means turning on 'linked' property in all viewers. It can be implemented with toggleable button or checkbox.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + } + }, "annotationOpacity": { "$id": "#annotationOpacity", "description": "Widget for control over annotation opacity.", @@ -498,6 +509,7 @@ "fillingTool": {"$ref": "#/definitions/fillingTool"}, "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, "labelmapOpacity": {"$ref": "#/definitions/labelmapOpacity"}, + "linkAllTool": {"$ref": "#/definitions/linkAllTool"}, "annotationOpacity": {"$ref": "#/definitions/annotationOpacity"}, "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"} } From ced6292e7c9f6bc2ada11e5ffc56a70a17c43b38 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 17:08:42 -0400 Subject: [PATCH 11/59] Updated widgets. --- examples/widgets/full.example.json | 5 +++++ schemas/widget.schema.json | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 314df48..9b45b21 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -167,5 +167,10 @@ "controlVisible": true, "controlEnabled": true, "defaultValue": false + }, + "smoothingAllTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 8b815ce..0985117 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -495,6 +495,17 @@ "properties": { "properties": { "$ref": "#/definitions/annotationTableProperties"} } + }, + "smoothingAllTool": { + "$id": "#smoothingAllTool", + "description": "Widget turning on/off smoothing in all viewers. It can be implemented with toggleable button or checkbox.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "boolean" + } + } } }, "properties": { @@ -511,6 +522,7 @@ "labelmapOpacity": {"$ref": "#/definitions/labelmapOpacity"}, "linkAllTool": {"$ref": "#/definitions/linkAllTool"}, "annotationOpacity": {"$ref": "#/definitions/annotationOpacity"}, - "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"} + "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"}, + "smoothingAllTool": {"$ref": "#/definitions/smoothingAllTool"} } } \ No newline at end of file From eaa0ae027b81e96b5f90dca0dfd0268f498ecebf Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 17:39:53 -0400 Subject: [PATCH 12/59] Updated widgets. --- examples/widgets/full.example.json | 27 ++++++++++++++++ schemas/widget.schema.json | 49 +++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 9b45b21..bd313ac 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -172,5 +172,32 @@ "controlVisible": true, "controlEnabled": true, "defaultValue": false + }, + "fiducialTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "circle" + }, + "diameter": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 5 + }, + "rotation": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "adjustableZoom": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 4 + } + } } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 0985117..6ea5d63 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -163,7 +163,7 @@ }, "customCursorDiameter": { "$id":"#customCursorDiameter", - "description": "Property defining diameter of cursor.", + "description": "Property defining diameter of cursor in millimeters. Size is calculated in image (real world) space.", "$comment": "This does not have to apply to all of the shapes.", "allOf": [{ "$ref": "#toolbarControls" }], "properties": { @@ -242,6 +242,42 @@ } } }, + "fiducialToolProperties": { + "$id":"#customCursorToolProperties", + "description": "Set of properties for Custom Cursor Tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/customCursorType", + "$comment": "identical as in custom cursor" + }, + "diameter": { + "$ref": "#/definitions/customCursorDiameter", + "$comment": "identical as in custom cursor" + }, + "rotation": { + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer", + "description": "Fiducial rotation in degrees.", + "minimum": 0 + } + } + }, + "adjustableZoom": { + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "number", + "description": "Ratio between display and fiducial size. Diameter of fiducial is equal to display width for ratio = 0.5", + "minimum": 0.01 + } + } + } + } + }, "pointerToolProperties": { "$id":"#pointerToolProperties", "description": "Set of properties for Pointer Tool.", @@ -506,6 +542,16 @@ "type": "boolean" } } + }, + "fiducialTool": { + "$id": "#fiducialTool", + "description": "Widget for responsible for displaying fiducial and modifying behavior of roi navigation. Widget 'on' causes that viewer is centered on image", + "$comment": "This widget is based on custom cursor.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "properties": { "$ref": "#/definitions/fiducialToolProperties"} + } } }, "properties": { @@ -516,6 +562,7 @@ "crossHairTool": {"$ref": "#/definitions/crossHairTool"}, "brushTool": {"$ref": "#/definitions/brushTool"}, "echoTool": {"$ref": "#/definitions/echoTool"}, + "fiducialTool": {"$ref": "#/definitions/fiducialTool"}, "eraserTool": {"$ref": "#/definitions/eraserTool"}, "fillingTool": {"$ref": "#/definitions/fillingTool"}, "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, From b64a14a40a48003f3ef727368539158106762b12 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 17:44:26 -0400 Subject: [PATCH 13/59] Updated widgets. --- examples/widgets/full.example.json | 4 ++++ schemas/widget.schema.json | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index bd313ac..a257af0 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -199,5 +199,9 @@ "defaultValue": 4 } } + }, + "pinUndoRedoTool":{ + "controlVisible":true, + "controlEnabled":true } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 6ea5d63..9e886c1 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -484,6 +484,12 @@ "type": "object", "allOf": [{ "$ref": "#toolbarControls" }] }, + "pinUndoRedoTool": { + "$id": "#pinUndoRedoTool", + "description": "Widget displaying history controls for pins. Save creating, deleting and selecting actions.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }] + }, "labelmapOpacity": { "$id": "#labelmapOpacity", "description": "Widget for control over labelmap opacity.", @@ -570,6 +576,7 @@ "linkAllTool": {"$ref": "#/definitions/linkAllTool"}, "annotationOpacity": {"$ref": "#/definitions/annotationOpacity"}, "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"}, - "smoothingAllTool": {"$ref": "#/definitions/smoothingAllTool"} + "smoothingAllTool": {"$ref": "#/definitions/smoothingAllTool"}, + "pinUndoRedoTool": {"$ref": "#/definitions/pinUndoRedoTool"} } } \ No newline at end of file From a5ea12bf21a9bc82c45b4b5b698a8107078783d0 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 14 May 2020 18:25:13 -0400 Subject: [PATCH 14/59] Updated widgets. --- examples/widgets/full.example.json | 35 ++++++++++++++++++ schemas/widget.schema.json | 58 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index a257af0..c4fa6e6 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -203,5 +203,40 @@ "pinUndoRedoTool":{ "controlVisible":true, "controlEnabled":true + }, + "labelmapLUT": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 1, + "properties": { + "options": [ + { + "value": 1, + "label": "Lesion", + "color": "#f00", + "iri": "http://moj.kod" + }, + { + "value": 2, + "label": "Plaque", + "color": "#0f0909" + }, + { + "value": 3, + "label": "Toxin", + "color": "#00f" + }, + { + "value": 4, + "label": "Tumor", + "color": "#f8f" + }, + { + "value": 5, + "label": "Aging", + "color": "#8ff" + } + ] + } } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 9e886c1..a3e0bc5 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -197,6 +197,34 @@ }, "required": ["defaultValue"] }, + "labelmapItem":{ + "$id":"#labelmapItem", + "type": "object", + "description": "Definition of labelmap value.", + "additionalProperties": false, + "properties": { + "value": { + "type": "integer", + "description": "Value assigned to voxel" + }, + "label": { + "type": "string", + "description": "Name to display. Can be a name of annotated region, pathological change, etc. Examples: lesion, tumor, medulla oblongata." + }, + "iri": { + "type": "string", + "description": "If segmentation refers to real world entity, iri should refer to Ontology Id, defined as IRI address.", + "format": "uri" + }, + "color": { + "type": "string", + "description": "Color in RGB space.", + "$comment": "Notation with 3 and six positions can be used.", + "pattern": "^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$" + } + }, + "required": ["color","value","label"] + }, "pinToolProperties": { "$id":"#pinToolProperties", "description": "Set of properties for Pin Tool.", @@ -313,6 +341,20 @@ } } }, + "labelmapLUTProperties": { + "$id":"#labelmapLUTProperties", + "description": "Set of properties for labelmap.", + "type": "object", + "additionalProperties": false, + "properties": { + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/labelmapItem" + } + } + } + }, "annotationTableProperties": { "$id":"#annotationTableProperties", "description": "Set of properties for annotation table component.", @@ -504,6 +546,21 @@ } } }, + "labelmapLUT": { + "$id": "#labelmapLUT", + "description": "Tool defining labelmap.", + "$comment": "TODO Labelmap should be provided by input", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "defaultValue": { + "type": "integer", + "description": "Parameter defining labelmap value to use when view is initialised.", + "default": false + }, + "properties": { "$ref": "#/definitions/labelmapLUTProperties"} + } + }, "linkAllTool": { "$id": "#linkAllTool", "description": "Widget linking all viewers (slice change is applied to all viewers). Turning on means turning on 'linked' property in all viewers. It can be implemented with toggleable button or checkbox.", @@ -571,6 +628,7 @@ "fiducialTool": {"$ref": "#/definitions/fiducialTool"}, "eraserTool": {"$ref": "#/definitions/eraserTool"}, "fillingTool": {"$ref": "#/definitions/fillingTool"}, + "labelmapLUT": {"$ref": "#/definitions/labelmapLUT"}, "labelmapUndoRedoTool": {"$ref": "#/definitions/labelmapUndoRedoTool"}, "labelmapOpacity": {"$ref": "#/definitions/labelmapOpacity"}, "linkAllTool": {"$ref": "#/definitions/linkAllTool"}, From d38d24af05e71c7bcd6325ff0be08af6f31f8782 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 15 May 2020 15:15:59 -0400 Subject: [PATCH 15/59] Updated widgets. --- schemas/widget.schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index a3e0bc5..adcba96 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -9,6 +9,7 @@ "toolbarControls": { "$id": "#toolbarControls", "description": "Boolean flags defining controls for a given widget.", + "type": "object", "$comment": "So far control components for widgets are implemented with Toggleable Buttons. Model can be extended by adding other complex controls, eg. sets of buttons, dropdown etc.", "properties": { "controlVisible": { @@ -613,6 +614,12 @@ "type": "object", "allOf": [{ "$ref": "#toolbarControls" }], "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean", + "description": "Optional parameter defining whether widget should be on when view is initialised." + }, "properties": { "$ref": "#/definitions/fiducialToolProperties"} } } From 35b369f003fc69df5639d7fd8f8866d09a9730eb Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Mon, 18 May 2020 18:04:31 -0400 Subject: [PATCH 16/59] Added new widget. --- examples/widgets/full.example.json | 5 +++++ schemas/widget.schema.json | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index c4fa6e6..55f0a2c 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -238,5 +238,10 @@ } ] } + }, + "windowLevelAllTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false } } \ No newline at end of file diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index adcba96..69b488d 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -622,6 +622,20 @@ }, "properties": { "$ref": "#/definitions/fiducialToolProperties"} } + }, + "windowLevelAllTool": { + "$id": "#windowLevelAllTool", + "description": "Widget linking all viewers for Window Level synchronization.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + } + } } }, "properties": { @@ -642,6 +656,7 @@ "annotationOpacity": {"$ref": "#/definitions/annotationOpacity"}, "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"}, "smoothingAllTool": {"$ref": "#/definitions/smoothingAllTool"}, - "pinUndoRedoTool": {"$ref": "#/definitions/pinUndoRedoTool"} + "pinUndoRedoTool": {"$ref": "#/definitions/pinUndoRedoTool"}, + "windowLevelAllTool": {"$ref": "#/definitions/windowLevelAllTool"} } } \ No newline at end of file From e42795fbb0ede2d6c236b3a597760b5b8b047d67 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 May 2020 12:36:21 -0400 Subject: [PATCH 17/59] Added full viewer test. --- examples/helpPanel/Identificationv4.json | 345 +++++++++++++++++++++++ examples/viewer/full.example.json | 83 ++++++ schemas/helpPanel.schema.json | 8 + tests/viewer.schema.test.js | 4 + 4 files changed, 440 insertions(+) create mode 100644 examples/helpPanel/Identificationv4.json create mode 100644 examples/viewer/full.example.json create mode 100644 schemas/helpPanel.schema.json diff --git a/examples/helpPanel/Identificationv4.json b/examples/helpPanel/Identificationv4.json new file mode 100644 index 0000000..8261293 --- /dev/null +++ b/examples/helpPanel/Identificationv4.json @@ -0,0 +1,345 @@ +{ + "_main": { + "goalAndDefinition": "

Goals:

- Identify each site of leptomeningeal metastasis (LM)\t

  • To be considered one lesion, the enhancing area must be continuous (all components must demonstrate connection in at least one projection).

- Select/pin each lesion only ONCE

  • Leptomeningeal metastasis are 3-dimensional objects.
  • Do not select the same lesion on multiple slices or projections.
  • If a lesion is “diffuse” in nature, only pin the lesion in one place. 

- Pin each lesion at its approximate center point.

- If a lesion has both a linear and nodular component, pin the nodular component.  

Definitions:

- Leptomeningeal enhancement: a continuous, contrast-enhancing lesions with less than 2 mm distance between the outer edge of the lesion and the leptomeninges.

", + "tools": "

= Turn on/off pin tool

= Undo/redo the most recently placed annotation

= Triangulate a focal area in all three projections

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected


", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=sBQKQ7t23mw&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=unxHdPwvoWs&feature=youtu.be" + }, + "images": [ + { + "name": "Definition of Leptomeningeal Metastasis (1/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1nKNP9s822TF6qW4aHopWIY9ec1dh9sTT", + "_$visited": true + }, + { + "name": "Definition of Leptomeningeal Metastasis (2/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tI2O7J4aTHFpDcJZ0U4kU2YIJ2iCKnN7", + "_$visited": true + }, + { + "name": "Ventricular nodular enhancement (1/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1YxGNw-AE_t0zPMwy5htz-7HDBcj7zsXa", + "_$visited": true + }, + { + "name": "Ventricular nodular enhancement (2/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1PDT29ASEmpajFHdadnDyAj90jAvl9p9R", + "_$visited": true + }, + { + "name": "Cerebral nodular enhancement", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1We29fGlH25hYmedNUbw71F2un51vNCg2", + "_$visited": true + }, + { + "name": "Cerebellar nodular enhancement", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1miRZ9CqAs_HnTSsG7kpFQ0jrww8iz5CV", + "_$visited": true + }, + { + "name": "Leptomeningeal lesion with linear and nodular component", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1NxtOmbXfFbLYa0jyqI0Hwj8ghLgFAYnt", + "_$visited": true + }, + { + "name": "Nodular enhancements in the lumbar spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=10Wq8oDibc_3CoAUfhjVvFMjVYyFt4Leg", + "_$visited": true + }, + { + "name": "Diffuse enhancement in the cervical spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1D3hVp-BhN_-4OK5eZj9XBXgPlzq0QyuW", + "_$visited": true + } + ], + "combos": "

= Place pins (annotations) on image when pin tool is toggled

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Move image

= Window image

= Window image when pin tool is toggled

= Move image

= Zoom in/out

= Zoom in/out when pin tool is toggled

= Delete annotation

", + "annotations": "

ANNOTATION TABLE

= Edit a cell

= Delete annotation

" + }, + "_goalDelta": { + "ops": [ + { + "attributes": { + "bold": true + }, + "insert": "Goals" + }, + { + "insert": ":\n- Identify each site of leptomeningeal metastasis (LM)\nTo be considered one lesion, the enhancing area must be continuous (all components must demonstrate connection in at least one projection)." + }, + { + "attributes": { + "list": "bullet" + }, + "insert": "\n" + }, + { + "insert": "- Select/pin each lesion only ONCE\nLeptomeningeal metastasis are 3-dimensional objects." + }, + { + "attributes": { + "list": "bullet" + }, + "insert": "\n" + }, + { + "insert": "Do not select the same lesion on multiple slices or projections." + }, + { + "attributes": { + "list": "bullet" + }, + "insert": "\n" + }, + { + "insert": "If a lesion is “diffuse” in nature, only pin the lesion in one place. " + }, + { + "attributes": { + "list": "bullet" + }, + "insert": "\n" + }, + { + "insert": "- Pin each lesion at its approximate center point.\n- If a lesion has both a linear and nodular component, pin the nodular component.  \n" + }, + { + "attributes": { + "bold": true + }, + "insert": "Definitions: " + }, + { + "insert": "\n-" + }, + { + "attributes": { + "bold": true + }, + "insert": " Leptomeningeal enhancement: " + }, + { + "insert": "a continuous," + }, + { + "attributes": { + "bold": true + }, + "insert": " " + }, + { + "insert": "c" + }, + { + "attributes": { + "color": "black" + }, + "insert": "ontrast-enhancing lesions with less than 2 mm distance between the outer edge of the lesion and the leptomeninges." + }, + { + "insert": "\n" + } + ] + }, + "_toolDelta": { + "ops": [ + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1HkVVlpY1r1p6YldlRhWmSYFqd5tDn1F-" + } + }, + { + "insert": "= Turn on/off pin tool\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1uyjb5n5ngjB9rzlKyyvbV_l9aWmiByx6" + } + }, + { + "insert": " = Undo/redo the most recently placed annotation\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1pd4vioZcOgcCkZJNxMiSrYSRtjIvIg27" + } + }, + { + "insert": " = Triangulate a focal area in all three projections\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1eHHxrtW3gMhlnT-m0fdyTodRazF3JCxq" + } + }, + { + "insert": " = Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=11bfY3nu1E2iro8B-zac92sNI1AxXyUs9" + } + }, + { + "insert": "= Project annotations to adjacent slices\nNeighborhood of projection = " + }, + { + "attributes": { + "color": "black" + }, + "insert": "Number of adjacent slices onto which the annotations are projected" + }, + { + "insert": "\n\n" + }, + { + "attributes": { + "header": 2 + }, + "insert": "\n" + } + ] + }, + "_comboDelta": { + "ops": [ + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=115RChWVB5vzscoAC05B1QR2SMH5_nVI8" + } + }, + { + "insert": " = Place pins (annotations) on image when pin tool is toggled\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1O8SkwIZgVGsD_U8JcdmKbg2tHKXtrj_h" + } + }, + { + "insert": " = " + }, + { + "attributes": { + "color": "black" + }, + "insert": "Move between slices" + }, + { + "insert": "\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=19MENfpFJoHZ4qzseaUM9IBSLMvMx_vA2" + } + }, + { + "insert": " = Move forward between annotations\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=14LBXTLC1ycm5zIe71BeMU8i11k5wJ4OZ" + } + }, + { + "insert": " = Move backward between annotations\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1dV1dOBSZ8y5p1wnvRtlKK9WBxaO9uXKL" + } + }, + { + "insert": " = Move image\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1NFjNlFSbIEtXnf17mbq2c76GWyEXAOil" + } + }, + { + "insert": " = Window image\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1uEUpX97BuBrxOkord4Ee-dpkTga_9p69" + } + }, + { + "insert": " = Window image when pin tool is toggled\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1dV1dOBSZ8y5p1wnvRtlKK9WBxaO9uXKL" + } + }, + { + "insert": " = Move image\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1_qdp5ozWi8MhCHIq1QIcDUSd2KQjajCV" + } + }, + { + "insert": " = Zoom in/out\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1XxlCwfZPbMQZe45D7KXSwelbjbVJ10mN" + } + }, + { + "insert": " = Zoom in/out when pin tool is toggled\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=1ixDZqFAniMhJnDWc6oxuUQjPP4Qbf0XA" + } + }, + { + "insert": " = Delete annotation\n" + } + ] + }, + "_annotationDelta": { + "ops": [ + { + "attributes": { + "underline": true + }, + "insert": "ANNOTATION TABLE" + }, + { + "attributes": { + "header": 2 + }, + "insert": "\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=115RChWVB5vzscoAC05B1QR2SMH5_nVI8" + } + }, + { + "insert": " = Edit a cell\n" + }, + { + "insert": { + "image": "https://drive.google.com/uc?export=download&id=13brFCX5hbLAXS_wMaSjQNxetuVQpiqsI" + } + }, + { + "insert": " = Delete annotation\n" + } + ] + } +} \ No newline at end of file diff --git a/examples/viewer/full.example.json b/examples/viewer/full.example.json new file mode 100644 index 0000000..04b6c85 --- /dev/null +++ b/examples/viewer/full.example.json @@ -0,0 +1,83 @@ +{ + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayImages": { + "possibleImagesToDisplay": [ + "inputImage_key2" + ], + "hasDefaultImageToDisplay": true, + "defaultImageToDisplay": "inputImage_key2" + }, + "displayLabelMaps": { + "possibleImagesToDisplay": [ + "inputImage_key2" + ], + "hasDefaultLabelMapToDisplay": true, + "defaultImageToDisplay": "inputImage_key2" + }, + "displayControls": { + "orientations": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Axial", + "value": "AXIAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Sagittal", + "value": "SAGITTAL" + } + ] + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#90ee90", + "hasRim": true, + "hasControlPanel":false + }, + "interactions": { + "activeViewer": { + "controlEnabled": false, + "defaultValue": false + }, + "crossHairTool": { + "controlEnabled": true, + "defaultValue": { + "viewers": { + "middle": { + "color": "#00FF00" + } + } + } + } + } +} \ No newline at end of file diff --git a/schemas/helpPanel.schema.json b/schemas/helpPanel.schema.json new file mode 100644 index 0000000..cab5a2a --- /dev/null +++ b/schemas/helpPanel.schema.json @@ -0,0 +1,8 @@ +{ + "$id": "https://raw.githubusercontent.com/SPINEProject/SPINE-json-schema/master/schemas/helpPanel.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "$comment": "TODO Finish me!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11", + "title": "Help Panel Schema", + "description": "Schema of help panel data TODO Finish me", + "type": "object" +} \ No newline at end of file diff --git a/tests/viewer.schema.test.js b/tests/viewer.schema.test.js index 2f61fab..bfb8bb8 100644 --- a/tests/viewer.schema.test.js +++ b/tests/viewer.schema.test.js @@ -14,6 +14,10 @@ describe('Viewer schemas', () => { expect(true).toBeAjvValid(schema, data1); }); + test('Full example viewer', () => { + expect(true).toBeAjvValid(schema, data1); + }); + test('Lepto project, step1, Middle viewer', () => { expect(true).toBeAjvValid(schema, data2); }); From b306f9025407b8f819b865766fedfa81858c542a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Bliault?= Date: Wed, 4 Aug 2021 14:02:43 +0200 Subject: [PATCH 18/59] Update annotation.schema.json --- schemas/annotation.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/annotation.schema.json b/schemas/annotation.schema.json index 9f25d1c..8c9a451 100644 --- a/schemas/annotation.schema.json +++ b/schemas/annotation.schema.json @@ -75,7 +75,7 @@ } }, "required": ["roiId", "workflowId", "materializedTaskId", "taskId", "questionId"], - "additionalProperties": false + "additionalProperties": true } ] } From af288a2881e9df3928266478209b6b25af877063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Bliault?= Date: Wed, 4 Aug 2021 14:21:01 +0200 Subject: [PATCH 19/59] Update annotation.schema.json --- schemas/annotation.schema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/schemas/annotation.schema.json b/schemas/annotation.schema.json index 8c9a451..9289195 100644 --- a/schemas/annotation.schema.json +++ b/schemas/annotation.schema.json @@ -204,8 +204,7 @@ "annotationProperties":{ "properties": { "value": { - "description": "Text in the open-ended question.", - "type": "string" + "description": "Text in the open-ended question." } }, "required": ["value"] From 7cc5d16c294cb58501baf9687df8f9406dad73aa Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 17:03:56 +0200 Subject: [PATCH 20/59] [Update] Updated examples of widgets --- examples/widgets/full.example.json | 130 +++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/examples/widgets/full.example.json b/examples/widgets/full.example.json index 55f0a2c..9262a74 100644 --- a/examples/widgets/full.example.json +++ b/examples/widgets/full.example.json @@ -243,5 +243,135 @@ "controlVisible": true, "controlEnabled": true, "defaultValue": false + }, + "samplerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "interactions": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": { + "intensityFilterTool": { + "type": "widget", + "tool": "intensityFilterTool", + "property": "maxValue", + "value": "midPoint" + } + } + }, + "histogram": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": true + }, + "onComplete": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": "SHOW_HISTOGRAM" + }, + "bucketing": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": "FREEDMAN-DIACONIS" + } + } + }, + "intensityFilterTool": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true, + "properties": { + "minValue": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "maxValue": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 4294967295 + }, + "showMask": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "clear": { + "controlVisible": false, + "controlEnabled": true + } + } + }, + "hideContoursTool": { + "controlVisible": true, + "controlEnabled": true, + "description": "This is optional setting for hiding contours functionality(key shortcut always on and optional button)." + }, + "hideLabelmapsTool": { + "controlVisible": true, + "controlEnabled": true, + "description": "This is optional setting for hiding segmentations functionality(key shortcut always on and optional button)." + }, + "levelTracingTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "levelFunction": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":"DARKER" + }, + "levelFunctionMargin": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":25 + }, + "lineWidth": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":0.1 + } + } + }, + "infoTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "showIntensity": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "showWL": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "showPosition": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + } + } + }, + "spawnViewersWindowTool": { + "controlVisible":true, + "controlEnabled":true + }, + "exportAnnotationsToServerTool": { + "controlVisible":true, + "controlEnabled":true + }, + "exportAnnotationsToDiskTool": { + "controlVisible":true, + "controlEnabled":true + }, + "importAnnotationsFromDiskTool": { + "controlVisible":true, + "controlEnabled":true } } \ No newline at end of file From b26164365d534b76e283061b2594cde61be34503 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 17:05:02 +0200 Subject: [PATCH 21/59] [Cleaning] Commented out missing parts. --- tests/setupTests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/setupTests.js b/tests/setupTests.js index 50282b6..8236b98 100644 --- a/tests/setupTests.js +++ b/tests/setupTests.js @@ -5,7 +5,7 @@ const taskSchema = require ("../schemas/task.schema"); const coreSchema = require ("../schemas/core.schema"); const viewerSchema = require ("../schemas/viewer.schema"); const annotationSchema = require ("../schemas/annotation.schema"); -const annotationForm = require ("../schemas/annotationForm.schema"); +// const annotationForm = require ("../schemas/annotationForm.schema"); const roiSchema = require ("../schemas/roi.schema"); const toolSchema = require('../schemas/tool.schema'); @@ -66,7 +66,7 @@ beforeAll(() => { ajv.addSchema(coreSchema,'coreSchema'); ajv.addSchema(taskSchema,'taskSchema'); ajv.addSchema(annotationSchema,'annotationSchema'); - ajv.addSchema(annotationForm,'annotationForm'); + // ajv.addSchema(annotationForm,'annotationForm'); ajv.addSchema(roiSchema,'roiSchema'); ajv.addSchema(toolSchema,'toolSchema'); ajv.addSchema(viewerSchema,'viewerSchema'); From c12b8e060e8e36ffd7960cd9ceb0a2d9acfa87b2 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 17:05:47 +0200 Subject: [PATCH 22/59] [Update] Updated list of widget tools --- schemas/tool.schema.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/schemas/tool.schema.json b/schemas/tool.schema.json index 6b9ba8a..6142d7b 100644 --- a/schemas/tool.schema.json +++ b/schemas/tool.schema.json @@ -31,7 +31,9 @@ "$id": "#toolType", "description": "Type of tool. This defines the layout to be used. SLICE_SELECTOR_TOOL refers to a tool where a slice is selected. SEGMENTATION_TOOL refers to a tool where a segmentation is created. CONFIRMATION_TOOL refers to a tool to confirm results, the output is only true/false without need for binding to anything.", "type": "string", - "enum": ["SLICE_SELECTOR_TOOL", "SEGMENTATION_TOOL", "CONFIRMATION_TOOL"] + "enum": ["SLICE_SELECTOR_TOOL", "SEGMENTATION_TOOL", "CONFIRMATION_TOOL", "ANNOTATION_TOOL", + "IDENTIFICATION_TOOL", "CHARACTERIZATION_TOOL", "SLICE_SELECTOR_PICKER_TOOL", "SLICE_SELECTOR_TOOL_RANGED", + "SLICE_SELECTOR_WITH_PICKER", "VISUALIZATION_TOOL"] }, "layoutViewersType":{ From a5dc7ce3ab12f0dbea8c556a9922a75268f6d5b2 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 17:06:08 +0200 Subject: [PATCH 23/59] Updated widget schema. --- schemas/widget.schema.json | 377 ++++++++++++++++++++++++++++++++++++- 1 file changed, 376 insertions(+), 1 deletion(-) diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index 69b488d..c5556be 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -417,6 +417,218 @@ } } }, + "samplerToolInteraction": { + "$id":"#samplerToolInteraction", + "description": "Definition of tool that is using sampled statistics calculated with sampler tool.", + "additionalProperties": false, + "required": ["type", "tool","property","value"], + "properties": { + "type": { + "type": "string", + "enum": ["widget"], + "$comment": "One option so far", + "description": "Type of interacting tool. If widget, other widget definition is required. TODO define interaction" + }, + "tool": { + "type": "string", + "enum": ["intensityFilterTool"], + "$comment": "One option so far", + "description": "Widget id." + }, + "property": { + "type": "string", + "enum": ["maxValue","minValue"], + "$comment": "This definition requires extension to other tools props.", + "description": "Name of property in destination tool (eg. intensityFilterTool.maxValue)" + }, + "value": { + "type": "string", + "enum": ["midPoint"], + "$comment": "One option so far. Needs to be expanded in code to others, ie. threshols, stats.minimum, stats. maximum, etc.", + "description": "Name of statistics calculated with sampler tool" + } + } + }, + "samplerToolProperties": { + "$id":"#samplerToolProperties", + "description": "Set of properties for Sampler Tool (Data Probe).", + "type": "object", + "additionalProperties": false, + "properties": { + "interactions": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Defines other tools that can be affected by sampler. For example, filter intensities can use statistics from sample like minimum, maximum, mid value etc.", + "$comment": "Control visible/enabled defined but not used here.", + "properties": { + "defaultValue": { + "maxProperties": 1, + "minProperties": 1, + "$comment": "Only one interaction is defined so far. FUTURE RELEASES: add another widgets into properties section if implemented.", + "properties": { + "intensityFilterTool": { + "type": "object", + "$ref": "#samplerToolInteraction" + } + } + } + } + }, + "histogram": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The control over displaying histogram. If visible, then button for displaying histogram in Widget Toolbar is displayed.", + "properties": { + "defaultValue": { + "type": "boolean", + "default": true + } + } + }, + "onComplete": { + "allOf": [{ "$ref": "#toolbarControls" }], + "$comment": "Control visible/enabled defined but not used here.", + "description": "Property defining what action should take place after setting the sample rectangle. For example, displaying histogram.", + "properties": { + "defaultValue": { + "type": "string", + "enum": ["SHOW_HISTOGRAM"], + "$comment": "One option so far." + } + } + }, + "bucketing": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Optional property defining the algorithm to be used for initialization of histogram bins.", + "$comment": "If not defined, number of bins is equal to 256.", + "properties": { + "defaultValue": { + "type": "string", + "enum": ["SQRT","STURGES","FREEDMAN-DIACONIS","RICE","SCOTT"], + "$comment": "Other options are available. This definition needs to be expanded with regular expressions. TODO" + } + } + } + } + }, + "intensityFilterToolProperties": { + "$id":"#intensityFilterToolProperties", + "description": "Set of properties for Intensity Filter", + "type": "object", + "additionalProperties": false, + "properties": { + "minValue": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The initial value for lower boundary of filter", + "properties": { + "defaultValue": { + "type": "number", + "default": 0 + } + } + }, + "maxValue": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The initial value for upper boundary of filter", + "properties": { + "defaultValue": { + "type": "number", + "default": 4294967295 + } + } + }, + "showMask": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The initial label value (to use with LUT) to display mask.", + "$comment": "Not implemented yet.", + "properties": { + "defaultValue": { + "type": "number", + "default": 0 + } + } + }, + "clear": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Control button for clearing fields" + } + + } + }, + "levelTracingToolProperties": { + "$id":"#levelTracingToolProperties", + "description": "Set of properties for Level Tracing Tool", + "type": "object", + "additionalProperties": false, + "properties": { + "levelFunction": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The type of level function", + "properties": { + "defaultValue": { + "type": "string", + "enum": ["DARKER","BRIGHTER","DARKER_WITH_MARGIN","BRIGHTER_WITH_MARGIN"] + } + } + }, + "levelFunctionMargin": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The optional margin of tolerance for level function", + "properties": { + "defaultValue": { + "type": "number", + "default": 25 + } + } + }, + "lineWidth": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "The line width for the contour renderer.", + "properties": { + "defaultValue": { + "type": "number", + "default": 0.1 + } + } + } + } + }, + "infoToolProperties": { + "$id":"#infoToolProperties", + "description": "Set of properties for Info Tool", + "type": "object", + "additionalProperties": false, + "properties": { + "showIntensity": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Defines whether intensity pointed by mouse cursor should be displayed", + "properties": { + "defaultValue": { + "type": "boolean", + "default": false + } + } + }, + "showWL": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Defines whether Window/Level range should be displayed", + "properties": { + "defaultValue": { + "type": "boolean", + "default": false + } + } + }, + "showPosition": { + "allOf": [{ "$ref": "#toolbarControls" }], + "description": "Defines whether position of cursor in image space IJK and real world space XYZ should be displayed", + "properties": { + "defaultValue": { + "type": "boolean", + "default": false + } + } + } + } + }, "pinTool": { "$id": "#pinTool", "description": "Tool for pin management.", @@ -636,6 +848,159 @@ "type": "boolean" } } + }, + "samplerTool": { + "$id": "#samplerTool", + "description": "Widget for sampling intensities in a given region in image. This tool is calculating statistics for a set of voxel intensities located inside red rectangle rendered in image slice. Optionally pixel histogram can be visualized", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + }, + "properties": { "$ref": "#/definitions/samplerToolProperties"} + } + }, + "intensityFilterTool": { + "$id": "#intensityFilterTool", + "description": "Widget for filtering voxel intensities during drawing (brush, filling tool, drawer, level tracing, etc.) and erasing. Can be interacting with sampler. ", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + }, + "properties": { "$ref": "#/definitions/intensityFilterToolProperties"} + } + }, + "hideContoursTool": { + "$id": "#hideContoursTool", + "description": "Widget for hiding the contours.", + "$comment": "This is out-dated", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "description": { + "type": "string", + "description": "This property can be used to describe tool." + }, + "defaultValue": { + "type": "boolean" + } + } + }, + "hideLabelmapsTool": { + "$id": "#hideLabelmapsTool", + "description": "This is an optional widget for hiding segmentations functionality (shortcut to hide segmentations is alwasy on - optional widget is just for additional button in Widget Toolbar).", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "description": { + "type": "string", + "description": "This property can be used to describe tool." + }, + "defaultValue": { + "type": "boolean" + } + } + }, + "levelTracingTool": { + "$id": "#levelTracingTool", + "description": "This is an optional widget for Level Tracing Tool.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + }, + "properties": { "$ref": "#/definitions/levelTracingToolProperties"} + } + }, + "infoTool": { + "$id": "#infoTool", + "description": "Widget displaying information about WL parameters and intensity of voxel pointed by mouse cursor. In case of multiple primary images, the information is provided for active (in control) layer only.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + }, + "properties": { "$ref": "#/definitions/infoToolProperties"} + } + }, + "spawnViewersWindowTool": { + "$id": "#spawnViewersWindowTool", + "description": "Widget for displaying viewers in separate (spawned) window which can be moved to any monitor.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + } + } + }, + "exportAnnotationsToServerTool": { + "$id": "#exportAnnotationsToServerTool", + "description": "Widget for saving temporary annotations to server.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + } + } + }, + "exportAnnotationsToDiskTool": { + "$id": "#exportAnnotationsToDiskTool", + "description": "Widget for saving temporary annotations to local disk as json file.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + } + } + }, + "importAnnotationsFromDiskTool": { + "$id": "#importAnnotationsFromDiskTool", + "description": "Widget for loading temporary annotations from local disk as json file.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean" + } + } } }, "properties": { @@ -657,6 +1022,16 @@ "annotationTableTool": {"$ref": "#/definitions/annotationTableTool"}, "smoothingAllTool": {"$ref": "#/definitions/smoothingAllTool"}, "pinUndoRedoTool": {"$ref": "#/definitions/pinUndoRedoTool"}, - "windowLevelAllTool": {"$ref": "#/definitions/windowLevelAllTool"} + "windowLevelAllTool": {"$ref": "#/definitions/windowLevelAllTool"}, + "samplerTool": {"$ref": "#/definitions/samplerTool"}, + "intensityFilterTool": {"$ref": "#/definitions/intensityFilterTool"}, + "hideContoursTool": {"$ref": "#/definitions/hideContoursTool"}, + "hideLabelmapsTool": {"$ref": "#/definitions/hideLabelmapsTool"}, + "levelTracingTool": {"$ref": "#/definitions/levelTracingTool"}, + "infoTool": {"$ref": "#/definitions/infoTool"}, + "spawnViewersWindowTool": {"$ref": "#/definitions/spawnViewersWindowTool"}, + "exportAnnotationsToServerTool": {"$ref": "#/definitions/exportAnnotationsToServerTool"}, + "exportAnnotationsToDiskTool": {"$ref": "#/definitions/exportAnnotationsToDiskTool"}, + "importAnnotationsFromDiskTool": {"$ref": "#/definitions/importAnnotationsFromDiskTool"} } } \ No newline at end of file From 6efc30c9535fb47b156b737762587dd04925e48d Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 17:09:56 +0200 Subject: [PATCH 24/59] [Update] Updated version in package.json. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e403886..4ca2ec1 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workflow" ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", - "version": "1.0.0", + "version": "2.2.0", "dependencies": { "ajv": "^6.12.2" }, From ce9a7678c4073e38eadd1f8ba26d4e48fdf62566 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 18:33:43 +0200 Subject: [PATCH 25/59] [Update] Updated displayControls and viewer layouts. Updated examples for testing. --- .../tool_annotationThreeViewers.json | 44 +++++++-- .../tool_segmentationBrushOneViewer.json | 31 ++++-- examples/viewer/full.example.json | 2 +- examples/viewer/viewer.example.json | 5 +- schemas/tool.schema.json | 2 +- schemas/viewer.schema.json | 96 ++++++++++++++----- 6 files changed, 133 insertions(+), 47 deletions(-) diff --git a/examples/annotationWorkflow/tool_annotationThreeViewers.json b/examples/annotationWorkflow/tool_annotationThreeViewers.json index 5f2a2bb..3f566b6 100644 --- a/examples/annotationWorkflow/tool_annotationThreeViewers.json +++ b/examples/annotationWorkflow/tool_annotationThreeViewers.json @@ -132,15 +132,41 @@ "possibleImagesToDisplay": ["imagesA"], "hasDefaultImageToDisplay": false }, - "displayControls":{ - "orientations": ["axial", "sagittal", "coronal"], - "defaultOrientation": "axial", - "smoothing": { - "smoothingControl": true, - "smoothingDefault": false - } - } - } + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": true, + "controlEnabled": true + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + } + } } }, diff --git a/examples/segmentationMedullaOblongata/tool_segmentationBrushOneViewer.json b/examples/segmentationMedullaOblongata/tool_segmentationBrushOneViewer.json index ea17056..15e4881 100644 --- a/examples/segmentationMedullaOblongata/tool_segmentationBrushOneViewer.json +++ b/examples/segmentationMedullaOblongata/tool_segmentationBrushOneViewer.json @@ -92,21 +92,36 @@ "hasDefaultImageToDisplay": true, "defaultImageToDisplay": "inputImage_key" }, - "displayControls":{ - "orientation":{ + "displayControls": { + "orientation": { "controlVisible": false, "controlEnabled": false, - "options":[ { - "label": "Axial", - "value": "AXIAL" - }] + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" }, - "smoothing":{ + "smoothing": { "controlVisible": false, "controlEnabled": false, "defaultValue": false }, - "sequence":{ + "sequence": { + "controlVisible": true, + "controlEnabled": true + }, + "linked": { "controlVisible": false, "controlEnabled": false } diff --git a/examples/viewer/full.example.json b/examples/viewer/full.example.json index 04b6c85..849bc49 100644 --- a/examples/viewer/full.example.json +++ b/examples/viewer/full.example.json @@ -27,7 +27,7 @@ "defaultImageToDisplay": "inputImage_key2" }, "displayControls": { - "orientations": { + "orientation": { "controlVisible": false, "controlEnabled": false, "options": [ diff --git a/examples/viewer/viewer.example.json b/examples/viewer/viewer.example.json index d832a2a..71dd956 100644 --- a/examples/viewer/viewer.example.json +++ b/examples/viewer/viewer.example.json @@ -20,7 +20,7 @@ "defaultImageToDisplay": "inputImage_key2" }, "displayControls": { - "orientations": { + "orientation": { "controlVisible": false, "controlEnabled": false, "options": [ @@ -28,7 +28,8 @@ "label": "Axial", "value": "AXIAL" } - ] + ], + "defaultValue": "AXIAL" }, "smoothing": { "controlVisible": false, diff --git a/schemas/tool.schema.json b/schemas/tool.schema.json index 6142d7b..7b9915b 100644 --- a/schemas/tool.schema.json +++ b/schemas/tool.schema.json @@ -40,7 +40,7 @@ "$id": "#layoutViewersType", "description": "Type of viewers layout.", "type": "string", - "enum": ["SINGLE", "FOUR_IN_A_ROW", "THREE_IRREGULAR", "GRID"] + "enum": ["SINGLE","1M1R_IN_ROW","SINGLE_WITH_WIDGET_PANEL", "1M4R", "1M2R", "1M2R_WIDGETS","1M1R_ARROWS", "ROW"] }, "possibleValueInAnnotationTable": { diff --git a/schemas/viewer.schema.json b/schemas/viewer.schema.json index 3611418..d8733fb 100644 --- a/schemas/viewer.schema.json +++ b/schemas/viewer.schema.json @@ -9,6 +9,24 @@ ], "definitions":{ + "toolbarControls": { + "$id": "#toolbarControls", + "description": "Boolean flags defining controls for a given property.", + "type": "object", + "$comment": "This defines control components for viewers.", + "properties": { + "controlVisible": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is visible." + }, + "controlEnabled": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is enabled, ie. can change a state. If not enabled, then works only as indicator." + } + }, + "required": ["controlEnabled", "controlVisible"] + }, + "orientationType":{ "$id": "#orientationType", "description": "Type of orientations", @@ -213,39 +231,65 @@ "description": "Display controls of the viewer.", "type": "object", "properties":{ - "orientations": { - "description": "Possible orientations of the images", - "type": "array", - "items": { - "type": "string", - "enum": ["axial", "sagittal", "coronal"] - }, - "minItems": 1 + "orientation": { + "description": "Orientation settings", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "options":{ + "description": "Orientation options for the viewer control components", + "type": "array", + "minItems": 1 + }, + "defaultValue": { + "description": "Default orientation of the viewer", + "type": "string", + "enum":["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["defaultValue", "controlVisible","controlEnabled"] }, - "defaultOrientation": { - "description": "Default orientation of the images", - "type": "string", - "enum": ["axial", "sagittal", "coronal"] - }, "smoothing":{ "description": "Smoothing control for the images", "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], "properties":{ - "smoothingControl": { - "description": "Smoothing control for the viewer", - "type": "boolean", - "default": true - }, - "smoothingDefault":{ - "description": "Smoothing default value for the viewer", - "type": "boolean", - "default": false - } + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean", + "description": "Initial setting (whether smoothing is on or off)" + } }, - "required": ["smoothingControl", "smoothingDefault"] - } + "required": ["defaultValue", "controlVisible","controlEnabled"] + }, + "sequence":{ + "description": "Control for change of the main image in viewer", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] + }, + "linked":{ + "description": "Control for linking of viewers", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] + } }, - "required": ["orientations", "defaultOrientation", "smoothing"] + "required": ["orientation", "smoothing"] } }, "required":["name", "type", "initialState"] From 2b077c58c409f95553c11b824632cd8b80448638 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 18:41:50 +0200 Subject: [PATCH 26/59] [Update] Updated orientation options. Updated version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ca2ec1..292b5d7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workflow" ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", - "version": "2.2.0", + "version": "2.2.1", "dependencies": { "ajv": "^6.12.2" }, From c5aa5ea2dfae6cea09b0b4040e6d5727e25024e7 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 22 Oct 2021 18:51:22 +0200 Subject: [PATCH 27/59] [Update] Updated displayControls array. --- schemas/viewer.schema.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/schemas/viewer.schema.json b/schemas/viewer.schema.json index d8733fb..2d9f05d 100644 --- a/schemas/viewer.schema.json +++ b/schemas/viewer.schema.json @@ -241,7 +241,22 @@ "options":{ "description": "Orientation options for the viewer control components", "type": "array", - "minItems": 1 + "minItems": 1, + "maxItems": 3, + "items": { + "type": "object", + "description": "Orientation option has mandatory enumerated value and mandatory label of any value", + "properties":{ + "label": { + "type":"string" + }, + "value": { + "type":"string", + "enum": ["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["label","value"] + } }, "defaultValue": { "description": "Default orientation of the viewer", From 111dacb53d23226c4de1323b4c8f38bd419b7fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:00:44 -0400 Subject: [PATCH 28/59] Removed "status" and "reference" as required --- schemas/roi.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/roi.schema.json b/schemas/roi.schema.json index 18806c2..3987e72 100644 --- a/schemas/roi.schema.json +++ b/schemas/roi.schema.json @@ -322,5 +322,5 @@ } }, "additionalProperties": false, - "required": ["typeROI", "status", "reference", "properties"] + "required": ["typeROI", "properties"] } From fbe9419d420ff3c80b99cb1f79fee32078b4b323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:03:15 -0400 Subject: [PATCH 29/59] Remove req. keys: "schema" & "roiInOut_FileFormat" --- schemas/core.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 32613cd..48cffc2 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -326,7 +326,7 @@ "$ref": "#/definitions/roiInOut_FileFormat" } }, - "required": ["type","schema","roiInOut_FileFormat"] + "required": ["type"] } ] }, From bce7f0949e0511178f5784324e553a5bdae00394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:14:30 -0400 Subject: [PATCH 30/59] Updated annotationTableDefinition type enum annotationTableDefinitionInputOutput instead of annotationTableDefinitionInOut --- schemas/core.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 48cffc2..e4885be 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -169,7 +169,7 @@ "properties": { "type":{ "type": "string", - "enum": ["annotationTableDefinitionInOut"] + "enum": ["annotationTableDefinitionInputOutput"] } }, "required": ["type"] From 2bcf2cc03785d39d99f5e39b2b2db6a72a533c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:18:02 -0400 Subject: [PATCH 31/59] Add annotationFormDefinitionInOut to the schema --- schemas/core.schema.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index e4885be..5b7b6f7 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -44,7 +44,7 @@ "type": "string", "enum": ["String","Number","imageFileInOut","imageEntityInOut", "auxiliaryImageFile","lookupTableInOut","roiInOut","annotationInOut", - "roiWithAnnotationsInOut","annotationTableDefinitionInOut"] + "roiWithAnnotationsInOut","annotationTableDefinitionInOut", "annotationFormDefinitionInOut"] }, "inputOutput": { @@ -160,6 +160,23 @@ ] }, + "annotationFormDefinitionInOut": { + "$id": "#annotationFormDefinitionInOut", + "description": "Schema defining an annotation form definition as input output", + "allOf":[ + {"$ref": "#/definitions/inputOutput_spine"}, + { + "properties": { + "type":{ + "type": "string", + "enum": ["annotationFormDefinitionInputOutput"] + } + }, + "required": ["type"] + } + ] + }, + "annotationTableDefinitionInOut": { "$id": "#annotationTableDefinitionInOut", "description": "Schema defining an annotation table definition as input output", @@ -400,7 +417,8 @@ {"$ref": "#/definitions/imageFileInOut"}, {"$ref": "#/definitions/lookupTableInOut"}, {"$ref": "#/definitions/annotationTableDefinitionInOut"}, - {"$ref": "#/definitions/annotationTableDataInOut"} + {"$ref": "#/definitions/annotationTableDataInOut"}, + {"$ref": "#/definitions/annotationFormDefinitionInOut"} ] } From dede598eecf9d184166d8cfb0cb86bdeab06941d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:24:17 -0400 Subject: [PATCH 32/59] Update core.schema.json --- schemas/core.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 5b7b6f7..68de9bd 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -44,7 +44,7 @@ "type": "string", "enum": ["String","Number","imageFileInOut","imageEntityInOut", "auxiliaryImageFile","lookupTableInOut","roiInOut","annotationInOut", - "roiWithAnnotationsInOut","annotationTableDefinitionInOut", "annotationFormDefinitionInOut"] + "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput"] }, "inputOutput": { From b02db81d784c73fd308d594fbf965107372b134a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:30:01 -0400 Subject: [PATCH 33/59] Fix to lookupTable and add lookUpTableDescription --- schemas/core.schema.json | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 68de9bd..aa832f8 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -43,7 +43,7 @@ "description": "SPINE Type", "type": "string", "enum": ["String","Number","imageFileInOut","imageEntityInOut", - "auxiliaryImageFile","lookupTableInOut","roiInOut","annotationInOut", + "auxiliaryImageFile","lookupTable","lookUpTableDescription","roiInOut","annotationInOut", "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput"] }, @@ -227,13 +227,30 @@ "properties": { "type":{ "type": "string", - "enum": ["lookupTableInOut"] + "enum": ["lookupTable"] }, "lookupTableInOut_FileFormat": { "$ref": "#/definitions/lookupTableInOut_FileFormat" } }, - "required": ["type","lookupTableInOut_FileFormat"] + "required": ["type"] + } + ] + }, + + "lookUpTableDescriptionInOut": { + "$id": "#lookUpTableDescriptionInOut", + "description": "Schema defining an lookup table description as input or output", + "allOf":[ + {"$ref": "#/definitions/inputOutput_spine"}, + { + "properties": { + "type":{ + "type": "string", + "enum": ["lookUpTableDescription"] + } + }, + "required": ["type"] } ] }, @@ -416,6 +433,7 @@ {"$ref": "#/definitions/Number"}, {"$ref": "#/definitions/imageFileInOut"}, {"$ref": "#/definitions/lookupTableInOut"}, + {"$ref": "#/definitions/lookUpTableDescriptionInOut"}, {"$ref": "#/definitions/annotationTableDefinitionInOut"}, {"$ref": "#/definitions/annotationTableDataInOut"}, {"$ref": "#/definitions/annotationFormDefinitionInOut"} From d05a8cb58591f6a11e44924fb9a2236b0e82b630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:32:16 -0400 Subject: [PATCH 34/59] Fix lookupTable to lookUpTable --- schemas/core.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index aa832f8..b6282a3 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -43,7 +43,7 @@ "description": "SPINE Type", "type": "string", "enum": ["String","Number","imageFileInOut","imageEntityInOut", - "auxiliaryImageFile","lookupTable","lookUpTableDescription","roiInOut","annotationInOut", + "auxiliaryImageFile","lookUpTable","lookUpTableDescription","roiInOut","annotationInOut", "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput"] }, @@ -227,7 +227,7 @@ "properties": { "type":{ "type": "string", - "enum": ["lookupTable"] + "enum": ["lookUpTable"] }, "lookupTableInOut_FileFormat": { "$ref": "#/definitions/lookupTableInOut_FileFormat" From 4a21d152e3495da446c986e9d41bc29e87bf371d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:37:06 -0400 Subject: [PATCH 35/59] Update annotationTableDataInOut --- schemas/core.schema.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index b6282a3..37e55be 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -205,6 +205,12 @@ "type": "string", "enum": ["annotationTableDataInOut"] } + "referenceInputKey":{ + "type": "string" + }, + "referenceAnnotationTableInputKey":{ + "type": "string" + } }, "required": ["type"] } From 1eb5332bca62b1192e1e9cbcac834cfb9145b379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:39:58 -0400 Subject: [PATCH 36/59] Add annotationFormDataInOut --- schemas/core.schema.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 37e55be..de95bb6 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -217,6 +217,29 @@ ] }, + "annotationFormDataInOut": { + "$id": "#annotationFormDataInOut", + "description": "Schema defining an annotation form data as input output", + "allOf":[ + {"$ref": "#/definitions/inputOutput_spine"}, + { + "properties": { + "type":{ + "type": "string", + "enum": ["annotationFormDataInOut"] + } + "referenceInputKey":{ + "type": "string" + }, + "referenceAnnotationTableInputKey":{ + "type": "string" + } + }, + "required": ["type"] + } + ] + }, + "lookupTableInOut_FileFormat": { "$id" : "#lookupTableInOut_Type", "description": "File format for lookup tables", @@ -442,7 +465,8 @@ {"$ref": "#/definitions/lookUpTableDescriptionInOut"}, {"$ref": "#/definitions/annotationTableDefinitionInOut"}, {"$ref": "#/definitions/annotationTableDataInOut"}, - {"$ref": "#/definitions/annotationFormDefinitionInOut"} + {"$ref": "#/definitions/annotationFormDefinitionInOut"}, + {"$ref": "#/definitions/annotationFormDataInOut"} ] } From 74cfe03d8fad2d93e488fe4d3d0d2acc11256c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:42:37 -0400 Subject: [PATCH 37/59] Fix missing commas --- schemas/core.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index de95bb6..f39464d 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -204,7 +204,7 @@ "type":{ "type": "string", "enum": ["annotationTableDataInOut"] - } + }, "referenceInputKey":{ "type": "string" }, @@ -227,7 +227,7 @@ "type":{ "type": "string", "enum": ["annotationFormDataInOut"] - } + }, "referenceInputKey":{ "type": "string" }, From 5d87629809e35509789c85d2d617cd00f2852fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Mon, 25 Oct 2021 12:45:38 -0400 Subject: [PATCH 38/59] Add missing spine output types Add "annotationTableDataInOut", "annotationFormDataInOut" in the possible output types --- schemas/core.schema.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index f39464d..162d80e 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -44,7 +44,8 @@ "type": "string", "enum": ["String","Number","imageFileInOut","imageEntityInOut", "auxiliaryImageFile","lookUpTable","lookUpTableDescription","roiInOut","annotationInOut", - "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput"] + "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput", + "annotationTableDataInOut", "annotationFormDataInOut"] }, "inputOutput": { From 22355c4463d73ae5e27911229300b418768f56d9 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Mon, 25 Oct 2021 19:11:26 +0200 Subject: [PATCH 39/59] [Update] Added comments, updated tool examples, modified examples. --- examples/tool/invisibleFilterDemo.json | 588 +++++++++++ examples/tool/sceneDemo.json | 1349 ++++++++++++++++++++++++ schemas/tool.schema.json | 1 + schemas/viewer.schema.json | 2 +- tests/tool.schema.test.js | 8 +- 5 files changed, 1946 insertions(+), 2 deletions(-) create mode 100644 examples/tool/invisibleFilterDemo.json create mode 100644 examples/tool/sceneDemo.json diff --git a/examples/tool/invisibleFilterDemo.json b/examples/tool/invisibleFilterDemo.json new file mode 100644 index 0000000..19dcf1f --- /dev/null +++ b/examples/tool/invisibleFilterDemo.json @@ -0,0 +1,588 @@ + +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Identification of Leptomeningeal metastases" + }, + { + "stepKey": "annotation", + "stepName": "Characterization of Leptomeningeal metastases" + } + ], + "currentStep": 1, + "currentTool": { + "id": "1dd174e57260e384c4f45e9c490004a9", + "name": "Characterization tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "CHARACTERIZATION_TOOL", + "inputs": { + "inputImage_key": { + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key2": { + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key3": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputImage_key4": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputImage_key5": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + + "inputLut_key1": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLutDescription_key1": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + } + }, + "outputs": { + "probingBox_key": { + "name": "Probing box", + "description": "Probing box drawn by the user", + "isList": false, + "type": "roiInOut", + "typeROI": "IMPLICIT" + }, + "otsuThreshold_key": { + "name": "Otsu threshold", + "description": "Otsu threshold calculate from the intentities inside the probing box.", + "isList": false, + "type": "Number" + } + }, + + "bindingWidgetOutputsToToolOutputs": [ + { + "outputTool": "otsuThreshold_key", + "widget": { + "widgetKey": "samplerTool", + "property": "threshold" + } + }, + { + "outputTool": "probingBox_key", + "widget": { + "widgetKey": "samplerTool", + "property": "points" + } + } + ], + "bindingSceneElementsToToolOutputs": [ + { + "outputTool": "segmentation_key", + "roiKeyName": "overlay_key_1" + } + ], + "configuration": { + "luts": { + "lut_key1": { + "fromInputs": true, + "lutInputKey": "inputLut_key1" + } + }, + "lutDescriptions": { + "lutDescription_key1": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key1" + } + }, + "rois": { + "overlays": { + "overlay_key_1": { + "fromInputs": false, + "lutKey": "lut_key1", + "lutDescriptionKey": "lutDescription_key1", + "label": "First layer" + } + }, + "masks": { + }, + "geometricalROIs": { + "roiList_key1": { + "fromInputs": true, + "roiInputKey": "inputROI_key1" + }, + "roiList_key2": { + "fromInputs": false + } + } + }, + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "overlays": [ + "overlay_key_1" + ] + } + } + }, + "annotationScenes": { + "annotationKey1": { + "roiInputKey": "inputROIDataList_key1", + "annotationDefinitionInputKey": "inputAnnotationTableDefinition_key1" + }, + "annotationKey2": { + "roiInputKey": "inputImage_key", + "annotationDefinitionInputKey": "inputAnnotationFormDefinition_key1" + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middleLeft": "1", + "middleRight": "2", + "right": "3" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": true, + "controlEnabled": true + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#90ee90" + } + }, + "3": { + "name": "3D viewer", + "type": "3D", + "displayControls": { + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayImageSlices": { + + } + } + } + }, + "widgets": { + "casesControl": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "labelmapLUT": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, + "brushTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "eraserTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fillingTool":{ + "controlVisible": true, + "controlEnabled": true + }, + + "labelmapUndoRedoTool": { + "controlVisible": true, + "controlEnabled": true + }, + "intensityFilterTool": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true, + "properties": { + "minValue": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "maxValue": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 4294967295 + }, + "showMask": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "clear": { + "controlVisible": false, + "controlEnabled": true + } + } + }, + "samplerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "interactions": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": { + "intensityFilterTool": { + "type": "widget", + "tool": "intensityFilterTool", + "property": "maxValue", + "value": "midPoint" + } + } + }, + "histogram": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": true + }, + "onComplete": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": "SHOW_HISTOGRAM" + }, + "bucketing": { + "controlVisible":false, + "controlEnabled":false, + "defaultValue": "FREEDMAN-DIACONIS" + } + } + }, + "labelmapOpacity":{ + "controlVisible":true, + "controlEnabled":true, + "defaultValue":1 + }, + "annotationTableTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "fullTable": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "editableROI": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "subAnnotationColumn": { + "controlVisible": true, + "controlEnabled": false + } + } + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "75c919ef4923cd7eef878de2930001c3", + "label": "T1", + "format": "nii.gz" + }, + "inputImage_key2": { + "value": "575bddd50492d394fe3e506e21134850", + "label": "Flair", + "format": "nii.gz" + }, + "inputImage_key3": { + "value": "cfc61966aa88162e1451a66ac6002491", + "format": "nii.gz" + }, + "inputImage_key4": { + "value": "cfc61966aa88162e1451a66ac6003b55", + "format": "nii.gz" + }, + "inputImage_key5": { + "value": "07adbebb4a07e7a707de2e339800dcba", + "format": "nii.gz" + }, + "inputLut_key1": { + "value": "c63c3c687bfeab4782a90290e1000260" + }, + "inputLutDescription_key1": { + "value": "c63c3c687bfeab4782a90290e1003c40" + } + }, + "status": "TODO", + "assignedTo": "amarciniak@bwh.harvard.edu", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "06bf3451e2d63c3bf86b439838081dc7" + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "NMO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "NMO2", + "caseStatus": "notstarted", + "caseCurrentStep": 0 + }, + { + "caseNumber": "NMO3", + "caseStatus": "started", + "caseCurrentStep": 1 + }, + { + "caseNumber": "NMO4", + "caseStatus": "done" + }, + { + "caseNumber": "NMO4", + "caseStatus": "done" + } + ], + "currentCase": "NMO1" +} diff --git a/examples/tool/sceneDemo.json b/examples/tool/sceneDemo.json new file mode 100644 index 0000000..2bf3029 --- /dev/null +++ b/examples/tool/sceneDemo.json @@ -0,0 +1,1349 @@ +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Identification of Leptomeningeal metastases" + }, + { + "stepKey": "annotation", + "stepName": "Characterization of Leptomeningeal metastases" + } + ], + "currentStep": 1, + "currentTool": { + "id": "1dd174e57260e384c4f45e9c490004a9", + "name": "Characterization tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "CHARACTERIZATION_TOOL", + "inputs": { + "inputImage_key": { + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key2": { + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key3": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputImage_key4": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputImage_key5": { + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputAnnotationTableDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationTableDefinitionInputOutput", + "required": true + }, + "inputAnnotationFormDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationFormDefinitionInputOutput", + "required": true + }, + "inputROIDataList_key1": { + "name": "Input ROis data", + "description": "Input ROI data for annotation table", + "isList": true, + "type": "roiInOut", + "typeROI": "IMPLICIT", + "required": true + }, + "inputLut_key1": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLut_key2": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLut_key3": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLutDescription_key1": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + }, + "inputLutDescription_key2": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + }, + "inputLutDescription_key3": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + } + }, + "outputs": { + "outputAnnotationTableData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation table", + "isList": false, + "type": "annotationTableDataInOut", + "required": true, + "referenceInputKey": "inputROIDataList_key1", + "referenceAnnotationTableInputKey": "inputAnnotationTableDefinition_key1" + }, + "outputAnnotationFormData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation form", + "isList": false, + "type": "annotationFormDataInOut", + "required": true, + "referenceInputKey": "inputImage_key", + "referenceAnnotationTableInputKey": "inputAnnotationFormDefinition_key1" + }, + "segmentation_key": { + "name": "Medulla oblongata segmentation", + "description": "The medulla oblongata segmentation from the user", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT" + } + }, + "bindingSceneElementsToToolOutputs": [ + { + "outputTool": "segmentation_key", + "roiKeyName": "overlay_key_1" + } + ], + "configuration": { + "luts": { + "lut_key1": { + "fromInputs": true, + "lutInputKey": "inputLut_key1" + }, + "lut_key2": { + "fromInputs": true, + "lutInputKey": "inputLut_key2" + }, + "lut_key3": { + "fromInputs": true, + "lutInputKey": "inputLut_key3" + } + }, + "lutDescriptions": { + "lutDescription_key1": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key1" + }, + "lutDescription_key2": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key2" + }, + "lutDescription_key3": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key3" + } + }, + "rois": { + "overlays": { + "overlay_key_1": { + "fromInputs": false, + "lutKey": "lut_key1", + "lutDescriptionKey": "lutDescription_key1", + "label": "First layer" + }, + "overlay_key_2": { + "fromInputs": true, + "imageInputKey": "inputImage_key3", + "lutKey": "lut_key2", + "lutDescriptionKey": "lutDescription_key2", + "label": "Layer invisible on Init", + "onInit": { + "visible": false + } + }, + "overlay_key_3": { + "fromInputs": true, + "imageInputKey": "inputImage_key4", + "lutKey": "lut_key3", + "lutDescriptionKey": "lutDescription_key3" + } + }, + "masks": { + "mask_key1": { + "fromInputs": true, + "imageInputKey": "inputImage_key5", + "lutKey": "lut_key1", + "lutDescriptionKey": "lutDescription_key1", + "label": "Rectangle mask", + "interactions": { + "PREVENT_FROM_ADDING_PIN": { + "type": "scalar", + "value": 1 + }, + "PREVENT_FROM_CHANGING_VOXEL_VALUE": { + "type": "vector", + "value": [ + 1, + 2, + 4 + ] + } + } + } + }, + "geometricalROIs": { + "roiList_key1": { + "fromInputs": true, + "roiInputKey": "inputROI_key1" + }, + "roiList_key2": { + "fromInputs": false + } + } + }, + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "overlays": [ + "overlay_key_1" + ], + "masks": ["mask_key1"] + } + }, + "sceneKey2": { + "primaryImageInputKey": "inputImage_key2", + "rois": { + "geometricalROIs": [ + "roiList_key1" + ], + "overlays": [ + "overlay_key_2", + "overlay_key_3" + ] + } + } + }, + "annotationScenes": { + "annotationKey1": { + "roiInputKey": "inputROIDataList_key1", + "annotationDefinitionInputKey": "inputAnnotationTableDefinition_key1" + }, + "annotationKey2": { + "roiInputKey": "inputImage_key", + "annotationDefinitionInputKey": "inputAnnotationFormDefinition_key1" + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middle": "1", + "right": "2" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + }, + "contoursOnInit": { + "visible": true + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1", + "sceneKey2" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey2" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": true, + "controlEnabled": true + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + }, + "contourMode": { + "controlVisible": false, + "controlEnabled": false, + "description": "This is optional setting responsible for displaying contour options (wireframe, offset). If not present then this contour options are visible by default. " + } + }, + "lookAndFeel": { + "color": "#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#90ee90" + } + } + } + }, + "widgets": { + "smoothingAllTool": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false, + "properties": { + "scope": { + "defaultValue": "PRIMARY_IMAGE" + } + } + }, + "casesControl": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "labelmapLUT": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, + "brushTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, + "contour": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": true + }, + "shape": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "square" + } + } + }, + "eraserTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, + "contour": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": true + }, + "shape": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "square" + } + } + }, + "fillingTool":{ + "controlVisible": true, + "controlEnabled": true + }, + "customCursorTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "crosshair" + }, + "diameter": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 3 + } + } + }, + "inversionTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "valueToInvert": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "labelmapUndoRedoTool": { + "controlVisible": true, + "controlEnabled": true + }, + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 2 + }, + "fontSize": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 12 + }, + "clearAll": { + "controlVisible": false, + "controlEnabled": false + }, + "subAnnotationsAvailable": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "centering": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "activeColor": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "#ff0000" + }, + "normalColor": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "#0000ff" + }, + "activeProjectionColor": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "#ffa500" + }, + "normalProjectionColor": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "#ffff00" + } + }, + "constraints": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": { + "criterion_key1": { + "controlElement": "viewer", + "property": "sliceNumber", + "operator": "===" + }, + "criterion_key2": { + "controlElement": "viewer", + "property": "sceneKey1", + "operator": "===" + } + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fiducialTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "circle" + }, + "diameter": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 5 + }, + "rotation": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "adjustableZoom": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 4 + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "labelmapOpacity":{ + "controlVisible":true, + "controlEnabled":true, + "defaultValue":1 + }, + "importAnnotationsFromDiskTool": { + "controlVisible":true, + "controlEnabled":true + }, + "exportAnnotationsToServerTool": { + "controlVisible":true, + "controlEnabled":true + }, + "exportAnnotationsToDiskTool": { + "controlVisible":true, + "controlEnabled":true + }, + "spawnViewersWindowTool": { + "controlVisible":true, + "controlEnabled":true + }, + "infoTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "showIntensity": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "showWL": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "showPosition": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + } + } + }, + "levelTracingTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "levelFunction": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":"DARKER" + }, + "levelFunctionMargin": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":25 + }, + "lineWidth": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue":0.1 + } + } + }, + "annotationTableTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "fullTable": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "editableROI": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "subAnnotationColumn": { + "controlVisible": true, + "controlEnabled": false + } + } + }, + "hideLabelmapsTool": { + "controlVisible": true, + "controlEnabled": true, + "description": "This is optional setting for hiding segmentations functionality(key shortcut always on and optional button)." + }, + "hideContoursTool": { + "controlVisible": true, + "controlEnabled": true, + "description": "This is optional setting for hiding contours functionality(key shortcut always on and optional button)." + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "75c919ef4923cd7eef878de2930001c3", + "label": "T1", + "format": "nii.gz" + }, + "inputImage_key2": { + "value": "575bddd50492d394fe3e506e21134850", + "label": "Flair", + "format": "nii.gz" + }, + "inputImage_key3": { + "value": "cfc61966aa88162e1451a66ac6002491", + "format": "nii.gz" + }, + "inputImage_key4": { + "value": "cfc61966aa88162e1451a66ac6003b55", + "format": "nii.gz" + }, + "inputImage_key5": { + "value": "07adbebb4a07e7a707de2e339800dcba", + "format": "nii.gz" + }, + "inputAnnotationTableDefinition_key1": { + "value": "7e0c58ae948decbb445f6c495e0042b6" + }, + "inputAnnotationFormDefinition_key1": { + "value": "b7fc97eb044d96113b87f38b2d000d93" + }, + "inputROIDataList_key1": { + "value": [ + "75c919ef4923cd7eef878de293001652", + "75c919ef4923cd7eef878de293002c70", + "75c919ef4923cd7eef878de2930038e0" + ] + }, + "inputLut_key1": { + "value": "c63c3c687bfeab4782a90290e1000260" + }, + "inputLut_key2": { + "value": "c63c3c687bfeab4782a90290e1001206" + }, + "inputLut_key3": { + "value": "c63c3c687bfeab4782a90290e1002fa9" + }, + "inputLutDescription_key1": { + "value": "c63c3c687bfeab4782a90290e1003c40" + }, + "inputLutDescription_key2": { + "value": "c63c3c687bfeab4782a90290e10050ed" + }, + "inputLutDescription_key3": { + "value": "c63c3c687bfeab4782a90290e1007642" + } + }, + "status": "TODO", + "assignedTo": "amarciniak@bwh.harvard.edu", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "06bf3451e2d63c3bf86b439838081dc7" + }, + "currentHelpPanel": { + "_main": { + "goalAndDefinition": "

Goals:

  • determine the location(s) of all leptomeningeal enhancements,
  • if you believe the lesion crosses two brain locations, you may choose more than one location option.
  • if you are unsure about a lesion’s location, you will be able to indicate this in the uncertainties column. 


Definitions:

Leptomeningeal enhancements can be seen in the following locations:

BRAIN:

  • Cerebrum: Lesion located within 2mm of the cerebral cortex. The lesion should involve a cerebral sulcus to ensure that it is leptomeningeal, rather than dural-based.  
  • Cerebellum: Lesion located within 2mm of the cerebellar cortex (along cerebellar folia).
  • Brainstem: Lesion along the surface of the brainstem, within 2mm of the brainstem parenchyma.·
  • Ventricle: Lesion located within the ventricular system (lateral ventricles, third ventricle, or fourth ventricle) or along the lining of a ventricle. A ventricular lesion may grow into the periventricular space, however the inner-most edge of the lesion should be less than 2mm from the ventricular border.
  • Cranial nerves: Lesion surrounding a cranial nerve.

SPINE:

  • Cervical spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of C1 and the superior end plate of T1.
  • Thoracic spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of T1 to the superior end plate of L1.
  • Lumbar spine: Lumbar spine will encompass the lumbosacral spine and will include remainder of the spinal cord extending below the superior end plate of L1. This will include intradural extramedullary lesions of the lower lumbar spinal cord/conus medullaris and cauda equina




", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=0kl5sOz7PEs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebrum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1EBsmK1qg-y2kU7mnrqi05E6g_T9u9bzr", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kg1Y2gSJAKYLa9h9V01QcZtK4_5PVAfZ", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1C9CjjAGy8L0utDhJFjz182-awGC1QTw_", + "_$visited": true + }, + { + "name": "Brainstem Midbrain", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18Ksu2i4SoVUvjxqD40jEA3s7qvy1bfoY", + "_$visited": true + }, + { + "name": "Brainstem Pons", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-T07qu0PvZb7Nov0KPA772MdXSan4lzD", + "_$visited": true + }, + { + "name": "Brainstem Medulla", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=172ZmQT05Wf6vBzOPlvadrAqhKN3J1uks", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1Kvg0CNL0LVY1ISDOlBGAGRwnbcgD7jBX", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=175gTnmem0gvNgSyP8YJQhpv-nmaAQqh1", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=158tgwe7O1q_okBRIYa_7BHSBx_YFsEA9", + "_$visited": true + }, + { + "name": "Cranial Nerve III", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=17CE85JuNWTwwaJWrkHp7_fK_GXTmV0ew", + "_$visited": true + }, + { + "name": "Cranial Nerve V", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kDjxWSCA9CuMXcwuRTXGiRa8hRMvMQ8I", + "_$visited": true + }, + { + "name": "Cranial Nerve VII-VIII", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1AMHAgyBSGeyRY9ESIC-8ThH_-qh1uk0N", + "_$visited": true + }, + { + "name": "Lesion involving the 4th ventricle and cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tbVv0ZmjsJM7Nlg-FI1UgTfqIzZBcsEi", + "_$visited": true + }, + { + "name": "Cervical Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1DuiZVwVEQWHd2uA9UkgkFj1rR6XCv3eW", + "_$visited": true + }, + { + "name": "Thoracic Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1XJDi9W-huc-NQsW7gwHdSdzhNFFpgCAB" + }, + { + "name": "Lumbar Spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1hpw9nZbxzG_ri8DxjCre3TUkOiHxuCVv", + "_$visited": true + }, + { + "name": "Lumbar Spine (Axial View through the Cauda Equina)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1ILgs4FlonrFNZ15OFmG3Z7AFo527_8r9", + "_$visited": true + } + ] + }, + "Location": { + "goalAndDefinition": "

Goals:

  • determine the location(s) of all leptomeningeal enhancements,
  • if you believe the lesion crosses two brain locations, you may choose more than one location option.
  • if you are unsure about a lesion’s location, you will be able to indicate this in the uncertainties column. 


Definitions:

Leptomeningeal enhancements can be seen in the following locations:

BRAIN:

  • Cerebrum: Lesion located within 2mm of the cerebral cortex. The lesion should involve a cerebral sulcus to ensure that it is leptomeningeal, rather than dural-based.  
  • Cerebellum: Lesion located within 2mm of the cerebellar cortex (along cerebellar folia).
  • Brainstem: Lesion along the surface of the brainstem, within 2mm of the brainstem parenchyma.·
  • Ventricle: Lesion located within the ventricular system (lateral ventricles, third ventricle, or fourth ventricle) or along the lining of a ventricle. A ventricular lesion may grow into the periventricular space, however the inner-most edge of the lesion should be less than 2mm from the ventricular border.
  • Cranial nerves: Lesion surrounding a cranial nerve.

SPINE:

  • Cervical spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of C1 and the superior end plate of T1.
  • Thoracic spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of T1 to the superior end plate of L1.
  • Lumbar spine: Lumbar spine will encompass the lumbosacral spine and will include remainder of the spinal cord extending below the superior end plate of L1. This will include intradural extramedullary lesions of the lower lumbar spinal cord/conus medullaris and cauda equina




", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=0kl5sOz7PEs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebrum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1EBsmK1qg-y2kU7mnrqi05E6g_T9u9bzr", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kg1Y2gSJAKYLa9h9V01QcZtK4_5PVAfZ", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1C9CjjAGy8L0utDhJFjz182-awGC1QTw_", + "_$visited": true + }, + { + "name": "Brainstem Midbrain", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18Ksu2i4SoVUvjxqD40jEA3s7qvy1bfoY", + "_$visited": true + }, + { + "name": "Brainstem Pons", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-T07qu0PvZb7Nov0KPA772MdXSan4lzD", + "_$visited": true + }, + { + "name": "Brainstem Medulla", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=172ZmQT05Wf6vBzOPlvadrAqhKN3J1uks", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1Kvg0CNL0LVY1ISDOlBGAGRwnbcgD7jBX", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=175gTnmem0gvNgSyP8YJQhpv-nmaAQqh1", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=158tgwe7O1q_okBRIYa_7BHSBx_YFsEA9", + "_$visited": true + }, + { + "name": "Cranial Nerve III", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=17CE85JuNWTwwaJWrkHp7_fK_GXTmV0ew", + "_$visited": true + }, + { + "name": "Cranial Nerve V", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kDjxWSCA9CuMXcwuRTXGiRa8hRMvMQ8I", + "_$visited": true + }, + { + "name": "Cranial Nerve VII-VIII", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1AMHAgyBSGeyRY9ESIC-8ThH_-qh1uk0N", + "_$visited": true + }, + { + "name": "Lesion involving the 4th ventricle and cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tbVv0ZmjsJM7Nlg-FI1UgTfqIzZBcsEi", + "_$visited": true + }, + { + "name": "Cervical Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1DuiZVwVEQWHd2uA9UkgkFj1rR6XCv3eW", + "_$visited": true + }, + { + "name": "Thoracic Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1XJDi9W-huc-NQsW7gwHdSdzhNFFpgCAB" + }, + { + "name": "Lumbar Spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1hpw9nZbxzG_ri8DxjCre3TUkOiHxuCVv", + "_$visited": true + }, + { + "name": "Lumbar Spine (Axial View through the Cauda Equina)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1ILgs4FlonrFNZ15OFmG3Z7AFo527_8r9", + "_$visited": true + } + ] + }, + "Comments": { + "goalAndDefinition": "

Instructions:

  • please enter any comments you may have separately for each leptomeningeal metastasis
", + "tools": "", + "combos": "", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=V3dcGsSfx-E&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [] + }, + "Is hydrocephalus present?": { + "goalAndDefinition": "

Instructions:

1. Record measurements that will be used to calculate the Evan’s Index (a quantitative tool to assess for ventriculomegaly).

2. Indicate whether there is qualitative hydrocephalus.

 

Definitions:


1. Evan’s index measurements:

Scroll through the axial images until you find the largest width of the frontal horns. Measure the largest frontal horn width in a plane that is perpendicular to midline (between the cerebral hemispheres). On the same axial slice, measure the largest left to right internal skull diameter (inner bone edge to inner bone edge). This measurement should also be perpendicular to midline. Enter these measurements into


2. Hydrocephalus: Subjective enlargement of the ventricles without proportional widening of the cortical sulci. Periventricular edema may be seen, but is not required. 

", + "tools": "

= Triangulate a focal area in all three projections

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows


", + "combos": "

= Move between slices

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Click on “yes” or “no” button

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=mQB0-yzc60Q&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Measurements for Evan’s Index", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=15bQgKjgVAjkXOupWeBPRbqqfjmmiVHIU", + "_$visited": true + }, + { + "name": "Measurements for Evan’s Index", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1f1j7YqQpCAIhqbHSGOh2oOrUOobt48ZC", + "_$visited": true + }, + { + "name": "Normal Ventricles", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1GpBcu4pAPk_wZTaszGpetrh5QwecMtIB", + "_$visited": true + }, + { + "name": "Normal Ventricles", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1_Qy8vIMyJ4g_JDRdJZjECqMjXY_x8tbk", + "_$visited": true + }, + { + "name": "Normal Ventrices", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1cy5IpurgfJAym_Lj4T-Wdsduo7Dr184a", + "_$visited": true + }, + { + "name": "Unclear", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lUqAO4xMrS_GC7w7nZ2iOvdnIR_VIF7-" + }, + { + "name": "Unclear", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1GOIiS3c_7PiTMbB8qAIdmuvPAxZ8Czk1", + "_$visited": true + }, + { + "name": "Hydrocephalus. Two axial slices from single brain MRI demonstrates enlarged ventricles (particularly in left temporal horn). There is periventricular edema which increases sensitivity", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18O6s2mZW6cGdzaLU9tQHKneLQD-1a2Ie" + }, + { + "name": "Hydrocephalus. Two axial slices from single brain MRI demonstrates enlarged ventricles (particularly in left temporal horn). There is periventricular edema which increases sensitivity", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1xxsIeIfdLWXIMuapXx_RCWrP2IlJiwFv" + } + ] + }, + "Shape": { + "goalAndDefinition": "

Goals:

  • determine the shape (i.e., nodular versus linear/curvilinear) of all leptomeningeal enhancements.
  • if a lesion has both nodular and linear components, select both.

Definitions:

Nodular enhancement: mass with rounded or convex polygonal contour.

  • a mass must have at least partially convex borders in three dimensions.
  • can be seen in the subarachnoid space, ventricles, periventricular areas and around the spinal cord

Linear enhancement: arranged along a straight or close-to-straight line

  • in three dimensions, a linear lesion is often “sheet-like”
  • can have a 2-D rounded/polygonal shape, but lacks convex borders in three dimensions.
  • can be seen in cranial and spinal nerve roots, cerebellar folia, ventricles, cerebral sulci and around the spinal cord
", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=TClBtDEbQB4&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Nodular enhancement in the subarachnoid space", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lIPL9fXMkAQ-3jvnhQnB8UhZXarfUTL_", + "_$visited": true + }, + { + "name": "Nodular enhancement in the fourth ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lfnizK6wi5O72p5wGvpfoXMe9JqaXHaj", + "_$visited": true + }, + { + "name": "Nodular enhancement in a periventricular area", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tDI_RQco7TRa0Zbv-EPS5Qy6IWK8nqr_", + "_$visited": true + }, + { + "name": "Nodular enhancement around the spinal cord", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=15KbCI-NUASoy-daAB_VP-f0w4OvkGAAa", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement in a cranial nerve root", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-FTsHGD2AmHKMrif-Jb5xmVQ2RdnzYmT", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement along the cerebellar folia", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1IkG9InZ5xT4DqBtjd4CbZGddjY8BpBKD", + "_$visited": true + }, + { + "name": "Linear “sheet-like” lesion, wrapping around the spinal cord", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tDI_RQco7TRa0Zbv-EPS5Qy6IWK8nqr_", + "_$visited": true + }, + { + "name": "Linear/curvilinear and nodular enhancement along the lining of a ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1MyX_mpEANpiOgzn9T82BFEMtWdHFitgX", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement along a cerebral sulcus", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1mrIu7sk3L5g-S_2LPAZfpA4mbpE0iiKD" + } + ] + }, + "Size is > 5x5mm": { + "goalAndDefinition": "

Goals:

  • determine the size of all leptomeningeal enhancements using the fiducial tool
  • if an enhancement has both nodular and linear components, determine size based on the nodular component.
  • for a focal lesion with an irregular border, measure the largest sub-component without crossing normal tissue.


Definitions:

  • note that the diameter of the red fiducial circle is 5 mm.
  • if the diameter of an enhancement is greater than 5 mm in the two greatest perpendicular diameters in any 2D plane, please select “yes” under the “Size” column of the annotation table.
  • if the diameter of an enhancement is equal to or smaller than 5 mm in the two greatest perpendicular diameters in any 2D plane, please select “no” under the “Size” column of the annotation table


", + "tools": "

= Turn on/off fiducial tool

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=dqDNTr2ACcw&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Lesion with size > 5 mm in bi-diameter ", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1-SM18L11DD4ON-Wk19jailg4wQ2BwTfl", + "_$visited": true + }, + { + "name": "Lesion with irregular border", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1VM1Aqidordd0XRWZKkVsNgc5nbZhvhey", + "_$visited": true + } + ] + }, + "Spatial extension": { + "goalAndDefinition": "

Goals:

  • Determine the spatial extension (i.e., focal or diffuse) of all leptomeningeal enhancements.
  • If a lesion has both a focal and diffuse component, label this lesion as diffuse. The more focal component may be measurable (>5x5 mm) even though the lesion is diffuse.      

Definitions:

-  Focal enhancement: circumscribed area of enhancement with sharp boundaries. The lesion can be measured in two dimensions (one plane) without crossing normal brain.

-   Diffuse enhancement: diffuse enhancement will encompass lesions with any of the following characteristics:

  • A lesion without well-defined boundaries
  • A lesion is spatially spread out such that it cannot be measured in two dimensions (one plane) without crossing normal brain parenchyma. 
", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotatio

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

= Delete annotation

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=Nta2Xg-KT_k&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebellar focal enhancement", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1SWzP5BbkuydkTCqRhnsvDhXe31OVt0tc", + "_$visited": true + }, + { + "name": "Cerebellar focal enhancement ", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1Hsfi0zwxmC02FYImylWTpqjogxI6Bicv", + "_$visited": true + }, + { + "name": "Cerebellar diffuse enhancement", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1b2iA6sK__mD9BZ2XlNL3Es7swWqOY379", + "_$visited": true + }, + { + "name": "Focal", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1Sw15RAeSdoemxpBPN8q1XeXsJk_YSpWZ", + "_$visited": true + }, + { + "name": "Focal", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1g6GPjslmnc345WfjG0rCaOdxW1BgiODp", + "_$visited": true + }, + { + "name": "Diffuse", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1doy-Bh7V_N_9B4a9tvD8VVxRjXiOsCBS", + "_$visited": true + }, + { + "name": "Diffuse", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1JZMUW7C5uzaS8Wjp11zgrx4zZNBvL02A" + } + ] + }, + "Uncertainties": { + "goalAndDefinition": "

Instructions:

-       please indicate if you are not certain regarding the location, size, shape, and/or spatial extension of a leptomeningeal enhancement

", + "tools": "", + "combos": "", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=rOJdY34UFUs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [] + } + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "NMO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "NMO2", + "caseStatus": "notstarted", + "caseCurrentStep": 0 + }, + { + "caseNumber": "NMO3", + "caseStatus": "started", + "caseCurrentStep": 1 + }, + { + "caseNumber": "NMO4", + "caseStatus": "done" + }, + { + "caseNumber": "NMO4", + "caseStatus": "done" + } + ], + "currentCase": "NMO1" +} \ No newline at end of file diff --git a/schemas/tool.schema.json b/schemas/tool.schema.json index 7b9915b..6ff5054 100644 --- a/schemas/tool.schema.json +++ b/schemas/tool.schema.json @@ -30,6 +30,7 @@ "toolType":{ "$id": "#toolType", "description": "Type of tool. This defines the layout to be used. SLICE_SELECTOR_TOOL refers to a tool where a slice is selected. SEGMENTATION_TOOL refers to a tool where a segmentation is created. CONFIRMATION_TOOL refers to a tool to confirm results, the output is only true/false without need for binding to anything.", + "$comment": "It is deprecated property. There is no much use of it due to the fact that tool configuration is now flexible and allows to have both segmentation and annotation widgets in a single configuration.", "type": "string", "enum": ["SLICE_SELECTOR_TOOL", "SEGMENTATION_TOOL", "CONFIRMATION_TOOL", "ANNOTATION_TOOL", "IDENTIFICATION_TOOL", "CHARACTERIZATION_TOOL", "SLICE_SELECTOR_PICKER_TOOL", "SLICE_SELECTOR_TOOL_RANGED", diff --git a/schemas/viewer.schema.json b/schemas/viewer.schema.json index 2d9f05d..1247d56 100644 --- a/schemas/viewer.schema.json +++ b/schemas/viewer.schema.json @@ -50,7 +50,7 @@ "strategyInitialWindowLevel":{ "$id": "#strategyInitialWindowLevel", - "description": "Strategy to define initial window/level. DYNAMIC uses the intensity ranges of the image to dynamicall set the window/level values. NUMBER uses predefined values.", + "description": "Strategy to define initial window/level. DYNAMIC uses the intensity ranges of the image to dynamically set the window/level values. NUMBER uses predefined values.", "type": "string", "enum": ["DYNAMIC", "NUMBER"] }, diff --git a/tests/tool.schema.test.js b/tests/tool.schema.test.js index f212cb4..ed6a6c1 100644 --- a/tests/tool.schema.test.js +++ b/tests/tool.schema.test.js @@ -1,7 +1,13 @@ const toolSchema = require('../schemas/tool.schema'); + let { test, expect ,it} = global; test('tool schema for tool_sliceSelectorInAxialDirection', () => { - expect(true).toBeAjvValid(toolSchema,require( "../examples/segmentationMedullaOblongata/tool_sliceSelectorInAxialDirection")); + expect(true).toBeAjvValid(toolSchema,require( "../examples/tool/invisibleFilterDemo").miniWorkflow.currentTool); +}); + + +test('tool schema ', () => { + expect(true).toBeAjvValid(toolSchema,require( "../examples/tool/sceneDemo").miniWorkflow.currentTool); }); \ No newline at end of file From 9d283694cced2e091af9690df224b7fc7ed3958c Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 26 Oct 2021 13:02:26 +0200 Subject: [PATCH 40/59] [Update] Viewer schema splitted into 2 definitions: 2D and 3D (due to their heterogeneity). --- schemas/tool.schema.json | 5 +- schemas/viewer.schema.json | 587 ++++++++++++++++++++++++++----------- 2 files changed, 414 insertions(+), 178 deletions(-) diff --git a/schemas/tool.schema.json b/schemas/tool.schema.json index 6ff5054..6c8d3ee 100644 --- a/schemas/tool.schema.json +++ b/schemas/tool.schema.json @@ -196,7 +196,10 @@ "patternProperties": { ".*": { "description": "Configuration of a viewer", - "$ref": "viewer.schema.json" + "oneOf":[ + {"$ref": "viewer.schema.json#/definitions/viewer2D"}, + {"$ref": "viewer.schema.json#/definitions/viewer3D"} + ] } } } diff --git a/schemas/viewer.schema.json b/schemas/viewer.schema.json index 1247d56..00e9703 100644 --- a/schemas/viewer.schema.json +++ b/schemas/viewer.schema.json @@ -45,7 +45,7 @@ "$id": "#dynamicValueInitialSlice", "description": "Value for initial slices calculated dynamically for a given image", "type": "string", - "enum": ["LOWERMOST", "UPPERMOST", "LEFTMOST", "RIGHTMOST", "POSTERIORMOST", "ANTERIORMOST", "MIDDLE"] + "enum": ["INFERIORMOST", "SUPERIORMOST", "LEFTMOST", "RIGHTMOST", "POSTERIORMOST", "ANTERIORMOST", "MIDDLE"] }, "strategyInitialWindowLevel":{ @@ -64,6 +64,7 @@ "viewerType":{ "$id": "#viewerType", + "$comment": "Deprecated definition since viewers have it defined in properties. It is kept for future extensions", "description": "Type of viewers", "type": "string", "enum": ["2D", "3D"] @@ -90,222 +91,454 @@ "orientation" ] - } - - }, - "properties": { - "name": { - "description": "Name of the viewer", - "type": "string" }, - "type":{ - "description": "Type of viewer", - "$ref": "#/definitions/viewerType" - }, - "initialState":{ - "description": "Initial state of the viewer", - "properties":{ - "orientationAndSliceNumber": { - "description": "Initial orientation and slice number", - "type": "object", + + "viewer2D": { + "$id": "#viewer2D", + "description": "All the viewer properties that can be exposed", + "type": "object", + "properties": { + "name": { + "description": "Name of the viewer", + "type": "string" + }, + "type":{ + "description": "Type of viewer", + "type": "string", + "enum": ["2D"] + }, + "initialState":{ + "description": "Initial state of the viewer", "properties":{ - "orientation": { - "$ref": "#/definitions/orientationType" + "orientationAndSliceNumber": { + "description": "Initial orientation and slice number", + "type": "object", + "properties":{ + "orientation": { + "$ref": "#/definitions/orientationType" + }, + "strategy": { + "description": "Strategy to define the initial slice", + "$ref": "#/definitions/strategyInitialSlice" + }, + "dynamicSliceValue":{ + "description": "Initial slice value", + "$ref": "#/definitions/dynamicValueInitialSlice" + }, + "predefinedSliceNumber":{ + "description": "Predefined slice number", + "type": "number" + } + }, + "oneOf":[ + { + "required": ["orientation", "strategy", "dynamicSliceValue"] + }, + { + "required": ["orientation", "strategy", "predefinedSliceNumber"] + } + ] }, - "strategy": { - "description": "Strategy to define the initial slice", - "$ref": "#/definitions/strategyInitialSlice" + "initialWindowLevel":{ + "description": "Initial window/level for the viewer", + "type": "object", + "properties":{ + "strategy": { + "description": "Strategy to define the initial window/level", + "$ref": "#/definitions/strategyInitialWindowLevel" + }, + "dynamicStrategy":{ + "description": "Initial slice value", + "$ref": "#/definitions/dynamicStrategyWindowLevel" + }, + "predefinedWindowWidth":{ + "description": "Predefined window width", + "type": "number" + }, + "predefinedLevelValue":{ + "description": "Predefined level value", + "type": "number" + } + }, + "oneOf":[ + { + "required": ["strategy", "dynamicStrategy"] + }, + { + "required": ["orientation", "dynamicStrategy", "predefinedWindowWidth", "predefinedLevelValue"] + } + ] + } + }, + "required": ["orientationAndSliceNumber", "initialWindowLevel"] + }, + "displayImages":{ + "description": "Display options of the viewer regarding the images.", + "type": "object", + "properties":{ + "possibleImagesToDisplay": { + "description": "List of input images the viewer can display. The input images must reference an id of the input images.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 }, - "dynamicSliceValue":{ - "description": "Initial slice value", - "$ref": "#/definitions/dynamicValueInitialSlice" + "hasDefaultImageToDisplay":{ + "description": "True is there is a default image to display", + "type": "boolean" }, - "predefinedSliceNumber":{ - "description": "Predefined slice number", - "type": "number" + "defaultImageToDisplay": { + "description": "Id of the input images to be displayed", + "type": "string" } }, - "oneOf":[ - { - "required": ["orientation", "strategy", "dynamicSliceValue"] + "required": ["possibleImagesToDisplay", "hasDefaultImageToDisplay"], + "dependencies": { + "defaultImageToDisplay": { + "properties": { + "hasDefaultImageToDisplay": { + "enum": [true] + } + } + } + } + }, + "windowLevel": { + "description": "Window/Level configuration for the viewer", + "type": "object", + "properties": { + "userCanChangeWindowLevel":{ + "description": "If the user is able to control the Window/Level.", + "type": "boolean", + "default": true }, - { - "required": ["orientation", "strategy", "predefinedSliceNumber"] + "defaultValue": { + "description": "Default values for the Window/Level.", + "type": "object", + "properties": { + "window": { + "description": "Default Window value.", + "type": "number", + "default": 0 + }, + "level": { + "description": "Default Level value.", + "type": "number", + "default": 0 + } + } } - ] + }, + "required": ["userCanChangeWindowLevel"] }, - "initialWindowLevel":{ - "description": "Initial window/level for the viewer", + "displayControls":{ + "description": "Display controls of the viewer.", "type": "object", "properties":{ - "strategy": { - "description": "Strategy to define the initial window/level", - "$ref": "#/definitions/strategyInitialWindowLevel" + "orientation": { + "description": "Orientation settings", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "options":{ + "description": "Orientation options for the viewer control components", + "type": "array", + "minItems": 1, + "maxItems": 3, + "items": { + "type": "object", + "description": "Orientation option has mandatory enumerated value and mandatory label of any value", + "properties":{ + "label": { + "type":"string" + }, + "value": { + "type":"string", + "enum": ["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["label","value"] + } + }, + "defaultValue": { + "description": "Default orientation of the viewer", + "type": "string", + "enum":["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["defaultValue", "controlVisible","controlEnabled"] }, - "dynamicStrategy":{ - "description": "Initial slice value", - "$ref": "#/definitions/dynamicStrategyWindowLevel" + "smoothing":{ + "description": "Smoothing control for the images", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean", + "description": "Initial setting (whether smoothing is on or off)" + } + }, + "required": ["defaultValue", "controlVisible","controlEnabled"] }, - "predefinedWindowWidth":{ - "description": "Predefined window width", - "type": "number" + "sequence":{ + "description": "Control for change of the main image in viewer", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] }, - "predefinedLevelValue":{ - "description": "Predefined level value", - "type": "number" + "linked":{ + "description": "Control for linking of viewers", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] } }, - "oneOf":[ - { - "required": ["strategy", "dynamicStrategy"] - }, - { - "required": ["orientation", "dynamicStrategy", "predefinedWindowWidth", "predefinedLevelValue"] - } - ] + "required": ["orientation", "smoothing"] } }, - "required": ["orientationAndSliceNumber", "initialWindowLevel"] + "required":["name", "type", "initialState"] }, - "displayImages":{ - "description": "Display options of the viewer regarding the images.", + "viewer3D": { + "$id": "#viewer3D", + "description": "All the viewer properties that can be exposed", "type": "object", - "properties":{ - "possibleImagesToDisplay": { - "description": "List of input images the viewer can display. The input images must reference an id of the input images.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 + "properties": { + "name": { + "description": "Name of the viewer", + "type": "string" }, - "hasDefaultImageToDisplay":{ - "description": "True is there is a default image to display", - "type": "boolean" + "type":{ + "description": "Type of viewer", + "type": "string", + "enum": ["3D"] }, - "defaultImageToDisplay": { - "description": "Id of the input images to be displayed", - "type": "string" - } - }, - "required": ["possibleImagesToDisplay", "hasDefaultImageToDisplay"], - "dependencies": { - "defaultImageToDisplay": { - "properties": { - "hasDefaultImageToDisplay": { - "enum": [true] + "initialState":{ + "description": "Initial state of the viewer", + "properties":{ + "orientationAndSliceNumber": { + "description": "Initial orientation and slice number", + "type": "object", + "properties":{ + "orientation": { + "$ref": "#/definitions/orientationType" + }, + "strategy": { + "description": "Strategy to define the initial slice", + "$ref": "#/definitions/strategyInitialSlice" + }, + "dynamicSliceValue":{ + "description": "Initial slice value", + "$ref": "#/definitions/dynamicValueInitialSlice" + }, + "predefinedSliceNumber":{ + "description": "Predefined slice number", + "type": "number" + } + }, + "oneOf":[ + { + "required": ["orientation", "strategy", "dynamicSliceValue"] + }, + { + "required": ["orientation", "strategy", "predefinedSliceNumber"] + } + ] + }, + "initialWindowLevel":{ + "description": "Initial window/level for the viewer", + "type": "object", + "properties":{ + "strategy": { + "description": "Strategy to define the initial window/level", + "$ref": "#/definitions/strategyInitialWindowLevel" + }, + "dynamicStrategy":{ + "description": "Initial slice value", + "$ref": "#/definitions/dynamicStrategyWindowLevel" + }, + "predefinedWindowWidth":{ + "description": "Predefined window width", + "type": "number" + }, + "predefinedLevelValue":{ + "description": "Predefined level value", + "type": "number" + } + }, + "oneOf":[ + { + "required": ["strategy", "dynamicStrategy"] + }, + { + "required": ["orientation", "dynamicStrategy", "predefinedWindowWidth", "predefinedLevelValue"] + } + ] } } - } - } - }, - "windowLevel": { - "description": "Window/Level configuration for the viewer", - "type": "object", - "properties": { - "userCanChangeWindowLevel":{ - "description": "If the user is able to control the Window/Level.", - "type": "boolean", - "default": true }, - "defaultValue": { - "description": "Default values for the Window/Level.", + "displayImages":{ + "description": "Display options of the viewer regarding the images.", "type": "object", - "properties": { - "window": { - "description": "Default Window value.", - "type": "number", - "default": 0 + "properties":{ + "possibleImagesToDisplay": { + "description": "List of input images the viewer can display. The input images must reference an id of the input images.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 }, - "level": { - "description": "Default Level value.", - "type": "number", - "default": 0 + "hasDefaultImageToDisplay":{ + "description": "True is there is a default image to display", + "type": "boolean" + }, + "defaultImageToDisplay": { + "description": "Id of the input images to be displayed", + "type": "string" } - } - } - }, - "required": ["userCanChangeWindowLevel"] - }, - "displayControls":{ - "description": "Display controls of the viewer.", - "type": "object", - "properties":{ - "orientation": { - "description": "Orientation settings", - "type": "object", - "allOf": [{ "$ref": "#toolbarControls" }], - "properties": { - "controlVisible": {}, - "controlEnabled": {}, - "options":{ - "description": "Orientation options for the viewer control components", - "type": "array", - "minItems": 1, - "maxItems": 3, - "items": { - "type": "object", - "description": "Orientation option has mandatory enumerated value and mandatory label of any value", - "properties":{ - "label": { - "type":"string" - }, - "value": { - "type":"string", - "enum": ["SAGITTAL","CORONAL","AXIAL"] - } - }, - "required": ["label","value"] + }, + "required": ["possibleImagesToDisplay", "hasDefaultImageToDisplay"], + "dependencies": { + "defaultImageToDisplay": { + "properties": { + "hasDefaultImageToDisplay": { + "enum": [true] } - }, - "defaultValue": { - "description": "Default orientation of the viewer", - "type": "string", - "enum":["SAGITTAL","CORONAL","AXIAL"] } - }, - "required": ["defaultValue", "controlVisible","controlEnabled"] + } + } }, - "smoothing":{ - "description": "Smoothing control for the images", - "type": "object", - "additionalProperties": false, - "allOf": [{ "$ref": "#toolbarControls" }], - "properties":{ - "controlVisible": {}, - "controlEnabled": {}, - "defaultValue": { + "windowLevel": { + "description": "Window/Level configuration for the viewer", + "type": "object", + "properties": { + "userCanChangeWindowLevel":{ + "description": "If the user is able to control the Window/Level.", "type": "boolean", - "description": "Initial setting (whether smoothing is on or off)" + "default": true + }, + "defaultValue": { + "description": "Default values for the Window/Level.", + "type": "object", + "properties": { + "window": { + "description": "Default Window value.", + "type": "number", + "default": 0 + }, + "level": { + "description": "Default Level value.", + "type": "number", + "default": 0 + } + } } - }, - "required": ["defaultValue", "controlVisible","controlEnabled"] - }, - "sequence":{ - "description": "Control for change of the main image in viewer", - "type": "object", - "additionalProperties": false, - "allOf": [{ "$ref": "#toolbarControls" }], - "properties":{ - "controlVisible": {}, - "controlEnabled": {} }, - "required": ["controlVisible","controlEnabled"] + "required": ["userCanChangeWindowLevel"] }, - "linked":{ - "description": "Control for linking of viewers", + "displayControls":{ + "description": "Display controls of the viewer.", "type": "object", - "additionalProperties": false, - "allOf": [{ "$ref": "#toolbarControls" }], "properties":{ - "controlVisible": {}, - "controlEnabled": {} + "orientation": { + "description": "Orientation settings", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "options":{ + "description": "Orientation options for the viewer control components", + "type": "array", + "minItems": 1, + "maxItems": 3, + "items": { + "type": "object", + "description": "Orientation option has mandatory enumerated value and mandatory label of any value", + "properties":{ + "label": { + "type":"string" + }, + "value": { + "type":"string", + "enum": ["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["label","value"] + } + }, + "defaultValue": { + "description": "Default orientation of the viewer", + "type": "string", + "enum":["SAGITTAL","CORONAL","AXIAL"] + } + }, + "required": ["defaultValue", "controlVisible","controlEnabled"] + }, + "smoothing":{ + "description": "Smoothing control for the images", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "type": "boolean", + "description": "Initial setting (whether smoothing is on or off)" + } + }, + "required": ["defaultValue", "controlVisible","controlEnabled"] + }, + "sequence":{ + "description": "Control for change of the main image in viewer", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] + }, + "linked":{ + "description": "Control for linking of viewers", + "type": "object", + "additionalProperties": false, + "allOf": [{ "$ref": "#toolbarControls" }], + "properties":{ + "controlVisible": {}, + "controlEnabled": {} + }, + "required": ["controlVisible","controlEnabled"] + } }, - "required": ["controlVisible","controlEnabled"] + "required": ["smoothing"] + }, + "displayScenes":{ + "description": "Display controls of the viewer." + }, + "displayImageSlices":{ + "description": "Display image slice actors in the scene. The actors can be associated with state of other viewers." } }, - "required": ["orientation", "smoothing"] + "required":["name", "type"] } - }, - "required":["name", "type", "initialState"] + } } \ No newline at end of file From 41fd1c42ee124fb51286803a34a31489cb1679a3 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 26 Oct 2021 13:03:40 +0200 Subject: [PATCH 41/59] [Update] Added new testing suite for the contents of examples/tool folder. Added examples. --- examples/tool/abdominalFatDemo.json | 221 +++++++ examples/tool/leptomeningeal1v1.json | 319 ++++++++++ examples/tool/leptomeningeal1v2.json | 481 +++++++++++++++ examples/tool/leptomeningeal2.json | 883 +++++++++++++++++++++++++++ examples/tool/leptomeningeal3.json | 404 ++++++++++++ examples/tool/modelingToolDemo.json | 487 +++++++++++++++ tests/tools2.schema.test.js | 21 + 7 files changed, 2816 insertions(+) create mode 100644 examples/tool/abdominalFatDemo.json create mode 100644 examples/tool/leptomeningeal1v1.json create mode 100644 examples/tool/leptomeningeal1v2.json create mode 100644 examples/tool/leptomeningeal2.json create mode 100644 examples/tool/leptomeningeal3.json create mode 100644 examples/tool/modelingToolDemo.json create mode 100644 tests/tools2.schema.test.js diff --git a/examples/tool/abdominalFatDemo.json b/examples/tool/abdominalFatDemo.json new file mode 100644 index 0000000..8eafa05 --- /dev/null +++ b/examples/tool/abdominalFatDemo.json @@ -0,0 +1,221 @@ +{ + "miniWorkflow": { + "currentTool": { + "id":"1dd174e57260e384c4f45e9c490004a9", + "name": "Segmentation tool", + "description": "This tool is used to visualize a segmentation and check it.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "SEGMENTATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key2":{ + "name": "Segmentation", + "description": "Segmentation", + "isList": false, + "type": "roiInOut", + "typeROI": "EXPLICIT", + "required": true, + "imageEntityInOut_FileFormat": "nii.gz" + }, + "inputLut_key1": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLutDescription_key1": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + } + }, + "outputs": { + }, + "configuration":{ + "luts": { + "lut_key1": { + "fromInputs": true, + "lutInputKey": "inputLut_key1" + } + }, + "lutDescriptions": { + "lutDescription_key1": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key1" + } + }, + "rois": { + "overlays": { + "overlay_key_1": { + "fromInputs": true, + "imageInputKey": "inputImage_key2", + "lutKey": "lut_key1", + "lutDescriptionKey": "lutDescription_key1" + } + } + }, + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "overlays": ["overlay_key_1"] + } + } + }, + "viewers": { + "layout": { + "type": "SINGLE_WITH_WIDGET_PANEL", + "layoutOrder": { + "middle": "0" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "INFERIORMOST" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a", + "hasControlPanel": false + } + } + } + }, + "widgets": + { + "brushTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fillingTool":{ + "controlVisible": true, + "controlEnabled": true + }, + "eraserTool":{ + "controlVisible":true, + "controlEnabled":true, + "properties":{ + "radius":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "labelmapLUT": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 32764 + }, + "labelmapUndoRedoTool":{ + "controlVisible":true, + "controlEnabled":true + }, + "labelmapOpacity":{ + "controlVisible":true, + "controlEnabled":true, + "defaultValue":1 + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "35d4d7d1d0fb89504a8c0c7425004e25", + "format": "nii.gz" + }, + "inputImage_key2": { + "value": "35d4d7d1d0fb89504a8c0c74250066d3", + "format": "nii.gz" + }, + "inputLut_key1": { + "value": "ac49b96c6eea3e74c9dce58927000af5" + }, + "inputLutDescription_key1": { + "value": "ac49b96c6eea3e74c9dce58927001617" + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "caseStatus": "notstarted" + } + ], + "currentCase": "training-9c39728c-8ce8-419e-886d-8138ba68e96e" +} \ No newline at end of file diff --git a/examples/tool/leptomeningeal1v1.json b/examples/tool/leptomeningeal1v1.json new file mode 100644 index 0000000..17e1ae9 --- /dev/null +++ b/examples/tool/leptomeningeal1v1.json @@ -0,0 +1,319 @@ +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Identification of Leptomeningeal metastases" + } + ], + "currentStep": 1, + "currentTool": { + "id":"1dd174e57260e384c4f45e9c490004a9", + "name": "Pin tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "IDENTIFICATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + } + }, + "outputs": { + "outputAnnotationTableData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation table", + "isList": false, + "type": "annotationTableDataInOut", + "required": true + } + }, + "configuration":{ + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "geometricalROIsInputKeys": [ + "inputROIDataList_key1" + ] + } + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middle": "1", + "right": "2" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "SUPERIORMOST" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#90ee90" + } + } + } + }, + "widgets":{ + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": true, + "controlEnabled": true + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationTableTool": { + "controlVisible": false, + "controlEnabled": false, + "properties": { + "fullTable": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + } + } + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "575bddd50492d394fe3e506e21134850", + "label": "FLAIR", + "format": "nii.gz" + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "LEPTO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "LEPTO2", + "caseStatus": "notstarted" + } + ], + "currentCase": "LEPTO1" +} \ No newline at end of file diff --git a/examples/tool/leptomeningeal1v2.json b/examples/tool/leptomeningeal1v2.json new file mode 100644 index 0000000..21b1433 --- /dev/null +++ b/examples/tool/leptomeningeal1v2.json @@ -0,0 +1,481 @@ +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Identification of Leptomeningeal metastases" + }, + { + "stepKey": "annotation", + "stepName": "Characterization of Leptomeningeal metastases" + } + ], + "currentStep": 0, + "currentTool": { + "id":"1dd174e57260e384c4f45e9c490004a9", + "name": "Pin tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "ANNOTATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputAnnotationTableDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationTableDefinitionInputOutput", + "required": true + } + }, + "outputs": { + "outputAnnotationTableData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation table", + "isList": false, + "type": "annotationTableDataInOut", + "required": true + } + }, + "configuration":{ + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "geometricalROIsInputKeys": [ + "outputAnnotationTableData_key" + ] + } + } + }, + "viewers": { + "layout": { + "type": "1M2R_WIDGETS", + "layoutOrder": { + "middle": "1", + "upperRight": "2", + "lowerRight": "0" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": true, + "controlEnabled": true, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "SUPERIORMOST" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": true, + "controlEnabled": true, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": true, + "controlEnabled": true, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#90ee90" + } + } + } + }, + "widgets":{ + "casesControl": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 12 + }, + "subAnnotationsAvailable":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "clearAll":{ + "controlVisible": true, + "controlEnabled": true + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "customCursorTool": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true, + "properties": { + "type": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "fixedCrosshairRelativeCircle" + }, + "diameter": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 2 + } + } + }, + "annotationTableTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "fullTable": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "editableROI": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": true + }, + "subAnnotationColumn":{ + "controlVisible": true, + "controlEnabled": false + }, + "csvExport": { + "controlVisible": false, + "controlEnabled": false + } + } + }, + "pinUndoRedoTool":{ + "controlVisible":true, + "controlEnabled":true + } + } + } + }, + "currentMaterializedTask": { + "navigationRules": { + "forwardConfirmationWindow": true, + "forwardConfirmationWindowMessage": "This is message", + "backwardConfirmationWindow": true, + "backwardConfirmationWindowMessage": "This is message" + }, + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "75c919ef4923cd7eef878de2930001c3", + "label": "T1", + "format": "nii.gz" + }, + "inputAnnotationTableDefinition_key1": { + "value": "22e8e7c87745251cf661c72342004595" + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "currentHelpPanel": { + "_main": { + "goalAndDefinition": "

Goals:

- Identify each site of leptomeningeal metastasis (LM)\t

  • To be considered one lesion, the enhancing area must be continuous (all components must demonstrate connection in at least one projection).

- Select/pin each lesion only ONCE

  • Leptomeningeal metastasis are 3-dimensional objects.
  • Do not select the same lesion on multiple slices or projections.
  • If a lesion is “diffuse” in nature, only pin the lesion in one place. 

- Pin each lesion at its approximate center point.

- If a lesion has both a linear and nodular component, pin the nodular component.  

Definitions:

- Leptomeningeal enhancement: a continuous, contrast-enhancing lesions with less than 2 mm distance between the outer edge of the lesion and the leptomeninges.

", + "tools": "

= Turn on/off pin tool

= Undo/redo the most recently placed annotation

= Triangulate a focal area in all three projections

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected


", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=sBQKQ7t23mw&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=unxHdPwvoWs&feature=youtu.be" + }, + "images": [ + { + "name": "Definition of Leptomeningeal Metastasis (1/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1nKNP9s822TF6qW4aHopWIY9ec1dh9sTT", + "_$visited": true + }, + { + "name": "Definition of Leptomeningeal Metastasis (2/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tI2O7J4aTHFpDcJZ0U4kU2YIJ2iCKnN7", + "_$visited": true + }, + { + "name": "Ventricular nodular enhancement (1/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1YxGNw-AE_t0zPMwy5htz-7HDBcj7zsXa", + "_$visited": true + }, + { + "name": "Ventricular nodular enhancement (2/2)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1PDT29ASEmpajFHdadnDyAj90jAvl9p9R", + "_$visited": true + }, + { + "name": "Cerebral nodular enhancement", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1We29fGlH25hYmedNUbw71F2un51vNCg2", + "_$visited": true + }, + { + "name": "Cerebellar nodular enhancement", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1miRZ9CqAs_HnTSsG7kpFQ0jrww8iz5CV", + "_$visited": true + }, + { + "name": "Leptomeningeal lesion with linear and nodular component", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1NxtOmbXfFbLYa0jyqI0Hwj8ghLgFAYnt", + "_$visited": true + }, + { + "name": "Nodular enhancements in the lumbar spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=10Wq8oDibc_3CoAUfhjVvFMjVYyFt4Leg", + "_$visited": true + }, + { + "name": "Diffuse enhancement in the cervical spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1D3hVp-BhN_-4OK5eZj9XBXgPlzq0QyuW", + "_$visited": true + } + ], + "combos": "

= Place pins (annotations) on image when pin tool is toggled

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Move image

= Window image

= Window image when pin tool is toggled

= Move image

= Zoom in/out

= Zoom in/out when pin tool is toggled

= Delete annotation

", + "annotations": "

ANNOTATION TABLE

= Edit a cell

= Delete annotation

" + }, + "Uncertainties":{ + "goalAndDefinition": "

Goal:

  • please indicate if you are not certain whether an enhancement is indeed a leptomeningeal metastasis
", + "tools": "

ANNOTATION TABLE

= Edit a cell

= Delete annotation

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=hFHQcCYNp1M&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=unxHdPwvoWs&feature=youtu.be" + }, + "images": [] + }, + "Comments": { + "goalAndDefinition": "

Goal:

-       please enter any comments you may have separately for each leptomeningeal metastasis

", + "tools": "

ANNOTATION TABLE

= Edit a cell

= Delete annotation

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=1qB00MCpEwQ&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=unxHdPwvoWs&feature=youtu.be" + }, + "images": [] + } + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "LEPTO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "LEPTO2", + "caseStatus": "notstarted" + } + ], + "currentCase": "LEPTO1" +} diff --git a/examples/tool/leptomeningeal2.json b/examples/tool/leptomeningeal2.json new file mode 100644 index 0000000..b0e9fdd --- /dev/null +++ b/examples/tool/leptomeningeal2.json @@ -0,0 +1,883 @@ +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Identification of Leptomeningeal metastases" + }, + { + "stepKey": "annotation", + "stepName": "Characterization of Leptomeningeal metastases" + } + ], + "currentStep": 1, + "currentTool": { + "id":"1dd174e57260e384c4f45e9c490004a9", + "name": "Characterization tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "CHARACTERIZATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputAnnotationTableDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationTableDefinitionInputOutput", + "required": true + }, + "inputAnnotationFormDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationFormDefinitionInputOutput", + "required": true + }, + "inputROIDataList_key1": { + "name": "Input ROis data", + "description": "Input ROI data for annotation table", + "isList": true, + "type": "roiInOut", + "typeROI": "IMPLICIT", + "required": true + } + }, + "outputs": { + "outputAnnotationTableData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation table", + "isList": false, + "type": "annotationTableDataInOut", + "required": true + }, + "outputAnnotationFormData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation form", + "isList": false, + "type": "annotationFormDataInOut", + "required": true + } + }, + "configuration":{ + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "geometricalROIsInputKeys": [ + "inputROIDataList_key1" + ] + } + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middle": "1", + "right": "2" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#90ee90" + } + } + } + }, + "widgets":{ + "casesControl": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "pinTool": { + "controlVisible": false, + "controlEnabled": false, + "properties": { + "markerSize": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": false, + "controlEnabled": false + }, + "subAnnotationsAvailable":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "centering": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fiducialTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "circle" + }, + "diameter": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 5 + }, + "rotation": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + }, + "adjustableZoom": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 4 + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationTableTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "fullTable": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "editableROI": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "subAnnotationColumn":{ + "controlVisible": true, + "controlEnabled": false + } + } + } + } + } + }, + "currentMaterializedTask": { + "navigationRules": { + "forwardConfirmationWindow": true, + "backwardConfirmationWindow": true + }, + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "75c919ef4923cd7eef878de2930001c3", + "label": "T1", + "format": "nii.gz" + }, + "inputAnnotationTableDefinition_key1": { + "value": "7e0c58ae948decbb445f6c495e0042b6" + }, + "inputAnnotationFormDefinition_key1": { + "value": "b7fc97eb044d96113b87f38b2d000d93" + }, + "inputROIDataList_key1": { + "value": ["75c919ef4923cd7eef878de293001652", "75c919ef4923cd7eef878de293002c70","75c919ef4923cd7eef878de2930038e0"] + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "currentHelpPanel": { + "_main": { + "goalAndDefinition": "

Goals:

  • determine the location(s) of all leptomeningeal enhancements,
  • if you believe the lesion crosses two brain locations, you may choose more than one location option.
  • if you are unsure about a lesion’s location, you will be able to indicate this in the uncertainties column. 


Definitions:

Leptomeningeal enhancements can be seen in the following locations:

BRAIN:

  • Cerebrum: Lesion located within 2mm of the cerebral cortex. The lesion should involve a cerebral sulcus to ensure that it is leptomeningeal, rather than dural-based.  
  • Cerebellum: Lesion located within 2mm of the cerebellar cortex (along cerebellar folia).
  • Brainstem: Lesion along the surface of the brainstem, within 2mm of the brainstem parenchyma.·
  • Ventricle: Lesion located within the ventricular system (lateral ventricles, third ventricle, or fourth ventricle) or along the lining of a ventricle. A ventricular lesion may grow into the periventricular space, however the inner-most edge of the lesion should be less than 2mm from the ventricular border.
  • Cranial nerves: Lesion surrounding a cranial nerve.

SPINE:

  • Cervical spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of C1 and the superior end plate of T1.
  • Thoracic spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of T1 to the superior end plate of L1.
  • Lumbar spine: Lumbar spine will encompass the lumbosacral spine and will include remainder of the spinal cord extending below the superior end plate of L1. This will include intradural extramedullary lesions of the lower lumbar spinal cord/conus medullaris and cauda equina




", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=0kl5sOz7PEs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebrum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1EBsmK1qg-y2kU7mnrqi05E6g_T9u9bzr", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kg1Y2gSJAKYLa9h9V01QcZtK4_5PVAfZ", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1C9CjjAGy8L0utDhJFjz182-awGC1QTw_", + "_$visited": true + }, + { + "name": "Brainstem Midbrain", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18Ksu2i4SoVUvjxqD40jEA3s7qvy1bfoY", + "_$visited": true + }, + { + "name": "Brainstem Pons", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-T07qu0PvZb7Nov0KPA772MdXSan4lzD", + "_$visited": true + }, + { + "name": "Brainstem Medulla", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=172ZmQT05Wf6vBzOPlvadrAqhKN3J1uks", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1Kvg0CNL0LVY1ISDOlBGAGRwnbcgD7jBX", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=175gTnmem0gvNgSyP8YJQhpv-nmaAQqh1", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=158tgwe7O1q_okBRIYa_7BHSBx_YFsEA9", + "_$visited": true + }, + { + "name": "Cranial Nerve III", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=17CE85JuNWTwwaJWrkHp7_fK_GXTmV0ew", + "_$visited": true + }, + { + "name": "Cranial Nerve V", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kDjxWSCA9CuMXcwuRTXGiRa8hRMvMQ8I", + "_$visited": true + }, + { + "name": "Cranial Nerve VII-VIII", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1AMHAgyBSGeyRY9ESIC-8ThH_-qh1uk0N", + "_$visited": true + }, + { + "name": "Lesion involving the 4th ventricle and cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tbVv0ZmjsJM7Nlg-FI1UgTfqIzZBcsEi", + "_$visited": true + }, + { + "name": "Cervical Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1DuiZVwVEQWHd2uA9UkgkFj1rR6XCv3eW", + "_$visited": true + }, + { + "name": "Thoracic Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1XJDi9W-huc-NQsW7gwHdSdzhNFFpgCAB" + }, + { + "name": "Lumbar Spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1hpw9nZbxzG_ri8DxjCre3TUkOiHxuCVv", + "_$visited": true + }, + { + "name": "Lumbar Spine (Axial View through the Cauda Equina)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1ILgs4FlonrFNZ15OFmG3Z7AFo527_8r9", + "_$visited": true + } + ] + }, + "Location": { + "goalAndDefinition": "

Goals:

  • determine the location(s) of all leptomeningeal enhancements,
  • if you believe the lesion crosses two brain locations, you may choose more than one location option.
  • if you are unsure about a lesion’s location, you will be able to indicate this in the uncertainties column. 


Definitions:

Leptomeningeal enhancements can be seen in the following locations:

BRAIN:

  • Cerebrum: Lesion located within 2mm of the cerebral cortex. The lesion should involve a cerebral sulcus to ensure that it is leptomeningeal, rather than dural-based.  
  • Cerebellum: Lesion located within 2mm of the cerebellar cortex (along cerebellar folia).
  • Brainstem: Lesion along the surface of the brainstem, within 2mm of the brainstem parenchyma.·
  • Ventricle: Lesion located within the ventricular system (lateral ventricles, third ventricle, or fourth ventricle) or along the lining of a ventricle. A ventricular lesion may grow into the periventricular space, however the inner-most edge of the lesion should be less than 2mm from the ventricular border.
  • Cranial nerves: Lesion surrounding a cranial nerve.

SPINE:

  • Cervical spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of C1 and the superior end plate of T1.
  • Thoracic spine: Intradural extramedullary lesion located in the spinal cord between the superior end plate of T1 to the superior end plate of L1.
  • Lumbar spine: Lumbar spine will encompass the lumbosacral spine and will include remainder of the spinal cord extending below the superior end plate of L1. This will include intradural extramedullary lesions of the lower lumbar spinal cord/conus medullaris and cauda equina




", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=0kl5sOz7PEs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebrum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1EBsmK1qg-y2kU7mnrqi05E6g_T9u9bzr", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kg1Y2gSJAKYLa9h9V01QcZtK4_5PVAfZ", + "_$visited": true + }, + { + "name": "Cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1C9CjjAGy8L0utDhJFjz182-awGC1QTw_", + "_$visited": true + }, + { + "name": "Brainstem Midbrain", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18Ksu2i4SoVUvjxqD40jEA3s7qvy1bfoY", + "_$visited": true + }, + { + "name": "Brainstem Pons", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-T07qu0PvZb7Nov0KPA772MdXSan4lzD", + "_$visited": true + }, + { + "name": "Brainstem Medulla", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=172ZmQT05Wf6vBzOPlvadrAqhKN3J1uks", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1Kvg0CNL0LVY1ISDOlBGAGRwnbcgD7jBX", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=175gTnmem0gvNgSyP8YJQhpv-nmaAQqh1", + "_$visited": true + }, + { + "name": "Ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=158tgwe7O1q_okBRIYa_7BHSBx_YFsEA9", + "_$visited": true + }, + { + "name": "Cranial Nerve III", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=17CE85JuNWTwwaJWrkHp7_fK_GXTmV0ew", + "_$visited": true + }, + { + "name": "Cranial Nerve V", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1kDjxWSCA9CuMXcwuRTXGiRa8hRMvMQ8I", + "_$visited": true + }, + { + "name": "Cranial Nerve VII-VIII", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1AMHAgyBSGeyRY9ESIC-8ThH_-qh1uk0N", + "_$visited": true + }, + { + "name": "Lesion involving the 4th ventricle and cerebellum", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tbVv0ZmjsJM7Nlg-FI1UgTfqIzZBcsEi", + "_$visited": true + }, + { + "name": "Cervical Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1DuiZVwVEQWHd2uA9UkgkFj1rR6XCv3eW", + "_$visited": true + }, + { + "name": "Thoracic Spine", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1XJDi9W-huc-NQsW7gwHdSdzhNFFpgCAB" + }, + { + "name": "Lumbar Spine (Sagittal View)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1hpw9nZbxzG_ri8DxjCre3TUkOiHxuCVv", + "_$visited": true + }, + { + "name": "Lumbar Spine (Axial View through the Cauda Equina)", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1ILgs4FlonrFNZ15OFmG3Z7AFo527_8r9", + "_$visited": true + } + ] + }, + "Comments": { + "goalAndDefinition": "

Instructions:

  • please enter any comments you may have separately for each leptomeningeal metastasis
", + "tools": "", + "combos": "", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=V3dcGsSfx-E&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [] + }, + "Is hydrocephalus present?": { + "goalAndDefinition": "

Instructions:

1. Record measurements that will be used to calculate the Evan’s Index (a quantitative tool to assess for ventriculomegaly).

2. Indicate whether there is qualitative hydrocephalus.

 

Definitions:


1. Evan’s index measurements:

Scroll through the axial images until you find the largest width of the frontal horns. Measure the largest frontal horn width in a plane that is perpendicular to midline (between the cerebral hemispheres). On the same axial slice, measure the largest left to right internal skull diameter (inner bone edge to inner bone edge). This measurement should also be perpendicular to midline. Enter these measurements into


2. Hydrocephalus: Subjective enlargement of the ventricles without proportional widening of the cortical sulci. Periventricular edema may be seen, but is not required. 

", + "tools": "

= Triangulate a focal area in all three projections

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows


", + "combos": "

= Move between slices

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Click on “yes” or “no” button

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=mQB0-yzc60Q&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Measurements for Evan’s Index", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=15bQgKjgVAjkXOupWeBPRbqqfjmmiVHIU", + "_$visited": true + }, + { + "name": "Measurements for Evan’s Index", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1f1j7YqQpCAIhqbHSGOh2oOrUOobt48ZC", + "_$visited": true + }, + { + "name": "Normal Ventricles", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1GpBcu4pAPk_wZTaszGpetrh5QwecMtIB", + "_$visited": true + }, + { + "name": "Normal Ventricles", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1_Qy8vIMyJ4g_JDRdJZjECqMjXY_x8tbk", + "_$visited": true + }, + { + "name": "Normal Ventrices", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1cy5IpurgfJAym_Lj4T-Wdsduo7Dr184a", + "_$visited": true + }, + { + "name": "Unclear", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lUqAO4xMrS_GC7w7nZ2iOvdnIR_VIF7-" + }, + { + "name": "Unclear", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1GOIiS3c_7PiTMbB8qAIdmuvPAxZ8Czk1", + "_$visited": true + }, + { + "name": "Hydrocephalus. Two axial slices from single brain MRI demonstrates enlarged ventricles (particularly in left temporal horn). There is periventricular edema which increases sensitivity", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=18O6s2mZW6cGdzaLU9tQHKneLQD-1a2Ie" + }, + { + "name": "Hydrocephalus. Two axial slices from single brain MRI demonstrates enlarged ventricles (particularly in left temporal horn). There is periventricular edema which increases sensitivity", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1xxsIeIfdLWXIMuapXx_RCWrP2IlJiwFv" + } + ] + }, + "Shape": { + "goalAndDefinition": "

Goals:

  • determine the shape (i.e., nodular versus linear/curvilinear) of all leptomeningeal enhancements.
  • if a lesion has both nodular and linear components, select both.

Definitions:

Nodular enhancement: mass with rounded or convex polygonal contour.

  • a mass must have at least partially convex borders in three dimensions.
  • can be seen in the subarachnoid space, ventricles, periventricular areas and around the spinal cord

Linear enhancement: arranged along a straight or close-to-straight line

  • in three dimensions, a linear lesion is often “sheet-like”
  • can have a 2-D rounded/polygonal shape, but lacks convex borders in three dimensions.
  • can be seen in cranial and spinal nerve roots, cerebellar folia, ventricles, cerebral sulci and around the spinal cord
", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=TClBtDEbQB4&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Nodular enhancement in the subarachnoid space", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lIPL9fXMkAQ-3jvnhQnB8UhZXarfUTL_", + "_$visited": true + }, + { + "name": "Nodular enhancement in the fourth ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1lfnizK6wi5O72p5wGvpfoXMe9JqaXHaj", + "_$visited": true + }, + { + "name": "Nodular enhancement in a periventricular area", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tDI_RQco7TRa0Zbv-EPS5Qy6IWK8nqr_", + "_$visited": true + }, + { + "name": "Nodular enhancement around the spinal cord", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=15KbCI-NUASoy-daAB_VP-f0w4OvkGAAa", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement in a cranial nerve root", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1-FTsHGD2AmHKMrif-Jb5xmVQ2RdnzYmT", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement along the cerebellar folia", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1IkG9InZ5xT4DqBtjd4CbZGddjY8BpBKD", + "_$visited": true + }, + { + "name": "Linear “sheet-like” lesion, wrapping around the spinal cord", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1tDI_RQco7TRa0Zbv-EPS5Qy6IWK8nqr_", + "_$visited": true + }, + { + "name": "Linear/curvilinear and nodular enhancement along the lining of a ventricle", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1MyX_mpEANpiOgzn9T82BFEMtWdHFitgX", + "_$visited": true + }, + { + "name": "Linear/curvilinear enhancement along a cerebral sulcus", + "link": "", + "src": "https://drive.google.com/uc?export=download&id=1mrIu7sk3L5g-S_2LPAZfpA4mbpE0iiKD" + } + ] + }, + "Size is > 5x5mm": { + "goalAndDefinition": "

Goals:

  • determine the size of all leptomeningeal enhancements using the fiducial tool
  • if an enhancement has both nodular and linear components, determine size based on the nodular component.
  • for a focal lesion with an irregular border, measure the largest sub-component without crossing normal tissue.


Definitions:

  • note that the diameter of the red fiducial circle is 5 mm.
  • if the diameter of an enhancement is greater than 5 mm in the two greatest perpendicular diameters in any 2D plane, please select “yes” under the “Size” column of the annotation table.
  • if the diameter of an enhancement is equal to or smaller than 5 mm in the two greatest perpendicular diameters in any 2D plane, please select “no” under the “Size” column of the annotation table


", + "tools": "

= Turn on/off fiducial tool

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

= Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotations

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=dqDNTr2ACcw&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Lesion with size > 5 mm in bi-diameter ", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1-SM18L11DD4ON-Wk19jailg4wQ2BwTfl", + "_$visited": true + }, + { + "name": "Lesion with irregular border", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1VM1Aqidordd0XRWZKkVsNgc5nbZhvhey", + "_$visited": true + } + ] + }, + "Spatial extension": { + "goalAndDefinition": "

Goals:

  • Determine the spatial extension (i.e., focal or diffuse) of all leptomeningeal enhancements.
  • If a lesion has both a focal and diffuse component, label this lesion as diffuse. The more focal component may be measurable (>5x5 mm) even though the lesion is diffuse.      

Definitions:

-  Focal enhancement: circumscribed area of enhancement with sharp boundaries. The lesion can be measured in two dimensions (one plane) without crossing normal brain.

-   Diffuse enhancement: diffuse enhancement will encompass lesions with any of the following characteristics:

  • A lesion without well-defined boundaries
  • A lesion is spatially spread out such that it cannot be measured in two dimensions (one plane) without crossing normal brain parenchyma. 
", + "tools": "

= Triangulate a focal area in all three projections

= Project annotations to adjacent slices

Neighborhood of projection = Number of adjacent slices onto which the annotations are projected

Display a 3D crosshair showing the position of the slices currently shown in all 3 viewer windows

", + "combos": "

= Move between slices

= Move forward between annotatio

= Move backward between annotations

= Window image

= Move image

= Zoom in/out

= Delete annotation

", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=Nta2Xg-KT_k&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [ + { + "name": "Cerebellar focal enhancement", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1SWzP5BbkuydkTCqRhnsvDhXe31OVt0tc", + "_$visited": true + }, + { + "name": "Cerebellar focal enhancement ", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1Hsfi0zwxmC02FYImylWTpqjogxI6Bicv", + "_$visited": true + }, + { + "name": "Cerebellar diffuse enhancement", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1b2iA6sK__mD9BZ2XlNL3Es7swWqOY379", + "_$visited": true + }, + { + "name": "Focal", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1Sw15RAeSdoemxpBPN8q1XeXsJk_YSpWZ", + "_$visited": true + }, + { + "name": "Focal", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1g6GPjslmnc345WfjG0rCaOdxW1BgiODp", + "_$visited": true + }, + { + "name": "Diffuse", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1doy-Bh7V_N_9B4a9tvD8VVxRjXiOsCBS", + "_$visited": true + }, + { + "name": "Diffuse", + "link": "", + "src": " https://drive.google.com/uc?export=download&id=1JZMUW7C5uzaS8Wjp11zgrx4zZNBvL02A" + } + ] + }, + "Uncertainties": { + "goalAndDefinition": "

Instructions:

-       please indicate if you are not certain regarding the location, size, shape, and/or spatial extension of a leptomeningeal enhancement

", + "tools": "", + "combos": "", + "annotations": "

= Edit a cell

", + "videos": { + "goalAndDefinition": "https://www.youtube.com/watch?v=rOJdY34UFUs&feature=youtu.be", + "tools": "https://www.youtube.com/watch?v=LcZ31mRQ7bg&feature=youtu.be" + }, + "images": [] + } + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "NMO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "NMO2", + "caseStatus": "notstarted" + } + ], + "currentCase": "NMO1" +} \ No newline at end of file diff --git a/examples/tool/leptomeningeal3.json b/examples/tool/leptomeningeal3.json new file mode 100644 index 0000000..12b0c8a --- /dev/null +++ b/examples/tool/leptomeningeal3.json @@ -0,0 +1,404 @@ +{ + "miniWorkflow": { + "miniWorkflowPath": [ + { + "stepKey": "annotation", + "stepName": "Step1" + }, + { + "stepKey": "annotation", + "stepName": "Step2" + } + ], + "currentStep": 1, + "currentTool": { + "id":"1dd174e57260e384c4f45e9c490004a9", + "name": "Characterization tool", + "description": "This tool is used to characterize previously identified ROI's.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "CHARACTERIZATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputAnnotationFormDefinition_key1": { + "name": "Input annotations definition", + "description": "Input annotations definition for annotation table", + "isList": false, + "type": "annotationFormDefinitionInputOutput", + "required": true + }, + "inputROIDataList_key1": { + "name": "Input ROis data", + "description": "Input ROI data for annotation table", + "isList": true, + "type": "roiInOut", + "typeROI": "IMPLICIT", + "required": true + } + }, + "outputs": { + "outputAnnotationTableData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation table", + "isList": false, + "type": "annotationTableDataInOut", + "required": true + }, + "outputAnnotationFormData_key": { + "name": "Output annotations data", + "description": "Output annotations data from annotation form", + "isList": false, + "type": "annotationFormDataInOut", + "required": true + } + }, + "configuration":{ + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "geometricalROIsInputKeys": [ + "inputROIDataList_key1" + ] + } + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middle": "1", + "right": "2" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a" + } + }, + "1": { + "name": "Middle viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } + }, + "2": { + "name": "Right viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + }, + "linked": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#90ee90" + } + } + } + }, + "widgets":{ + "pinTool": { + "controlVisible": false, + "controlEnabled": false, + "properties": { + "markerSize": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": false, + "controlEnabled": false + }, + "subAnnotationsAvailable":{ + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "centering": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + } + } + }, + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "onClick" + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fiducialTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "type": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": "circle" + }, + "diameter": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": 5 + }, + "rotation": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": 0 + } + } + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationTableTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "fullTable": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "editableROI": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "subAnnotationColumn":{ + "controlVisible": true, + "controlEnabled": false + } + } + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "310a4fa25bb6c2e79dbc0b35c900614d", + "label": "T1", + "format": "nii.gz" + }, + "inputAnnotationFormDefinition_key1": { + "value": "3d177f94c60db9b3157d356a9f002a58" + }, + "inputROIDataList_key1": { + "value": ["e64e724ff6782f91b0ad792220007a97","e64e724ff6782f91b0ad7922200088a0","e64e724ff6782f91b0ad792220009d79","e64e724ff6782f91b0ad79222000ac2e","e64e724ff6782f91b0ad79222000c3ae"] + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "NMO1", + "caseStatus": "notstarted" + }, + { + "caseNumber": "NMO2", + "caseStatus": "notstarted" + } + ], + "currentCase": "NMO1" +} \ No newline at end of file diff --git a/examples/tool/modelingToolDemo.json b/examples/tool/modelingToolDemo.json new file mode 100644 index 0000000..0dc4179 --- /dev/null +++ b/examples/tool/modelingToolDemo.json @@ -0,0 +1,487 @@ +{ + "miniWorkflow": { + "currentTool": { + "id": "1dd174e57260e384c4f45e9c490004a9", + "name": "Annotation tool", + "description": "This tool is used to visualize a segmentation and check it.", + "version": "1.0.0", + "privacy": "PUBLIC", + "type": "ANNOTATION_TOOL", + "inputs": { + "inputImage_key":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key2":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputImage_key3":{ + "name": "Input image", + "description": "Input image", + "isList": false, + "type": "imageEntityInOut", + "imageEntityInOut_Type": "ANATOMICAL", + "imageEntityInOut_FileFormat": "nii.gz", + "required": true + }, + "inputLut_key1": { + "name": "Input LUT data", + "description": "Input LUT data for overlay displaying.", + "isList": false, + "type": "lookUpTable", + "required": true + }, + "inputLutDescription_key1": { + "name": "Input description of LUT data", + "description": "Input LUT data for displaying options.", + "isList": false, + "type": "lookUpTableDescription", + "required": true + } + }, + "outputs": { + }, + "configuration":{ + "luts": { + "lut_key1": { + "fromInputs": true, + "lutInputKey": "inputLut_key1" + } + }, + "lutDescriptions": { + "lutDescription_key1": { + "fromInputs": true, + "lutDescriptionInputKey": "inputLutDescription_key1" + } + }, + "rois": { + "overlays": { + "overlay_key_1": { + "fromInputs": false, + "lutKey": "lut_key1", + "lutDescriptionKey": "lutDescription_key1" + } + } + }, + "scenes": { + "sceneKey1": { + "primaryImageInputKey": "inputImage_key", + "rois": { + "overlays": [ + "overlay_key_1" + ] + } + }, + "sceneKey2": { + "primaryImageInputKey": "inputImage_key2" + }, + "sceneKey3": { + "primaryImageInputKey": "inputImage_key3" + } + }, + "viewers": { + "layout": { + "type": "ROW", + "layoutOrder": { + "left": "0", + "middleLeft": "1", + "middleRight": "2", + "right": "3" + } + }, + "renderWindows": { + "0": { + "name": "Sagittal viewer", + "type": "2D", + "initialState": { + "orientationAndSliceNumber": { + "orientation": "SAGITTAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1", + "sceneKey2", + "sceneKey3" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#ffa07a" + } + }, + "1": { + "name": "Coronal viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "CORONAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1", + "sceneKey2", + "sceneKey3" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "CORONAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel":{ + "color":"#fff967" + } + }, + "2": { + "name": "Coronal viewer", + "type": "2D", + "linkedControlVisible": false, + "initialState": { + "orientationAndSliceNumber": { + "orientation": "AXIAL", + "strategy": "DYNAMIC", + "dynamicSliceValue": "MIDDLE" + }, + "initialWindowLevel": { + "strategy": "DYNAMIC", + "dynamicStrategy": "FULLWINDOW_MIDDLELEVEL" + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1", + "sceneKey2", + "sceneKey3" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayControls": { + "orientation": { + "controlVisible": false, + "controlEnabled": false, + "options": [ + { + "label": "Sagittal", + "value": "SAGITTAL" + }, + { + "label": "Coronal", + "value": "CORONAL" + }, + { + "label": "Axial", + "value": "AXIAL" + } + ], + "defaultValue": "AXIAL" + }, + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": false + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "lookAndFeel": { + "color": "#90ee90" + } + }, + "3": { + "name": "3D viewer", + "type": "3D", + "displayControls": { + "smoothing": { + "controlVisible": false, + "controlEnabled": false, + "defaultValue": true + }, + "sequence": { + "controlVisible": false, + "controlEnabled": false + } + }, + "displayScenes": { + "possibleScenesToDisplay": [ + "sceneKey1", + "sceneKey2", + "sceneKey3" + ], + "hasDefaultSceneToDisplay": true, + "defaultSceneToDisplay": "sceneKey1" + }, + "displayImageSlices": { + "0": { + "fromState": true, + "stateKey": "left" + }, + "1": { + "fromState": true, + "stateKey": "middleLeft" + }, + "2": { + "fromState": true, + "stateKey": "middleRight" + } + } + } + } + }, + "widgets": { + "pointerTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "mode": { + "controlVisible": false, + "controlEnabled": true, + "defaultValue": "onClick" + } + } + }, + "pinTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "markerSize": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 2 + }, + "fontSize":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 12 + }, + "clearAll":{ + "controlVisible": true, + "controlEnabled": true + } + } + }, + "projectionTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "range": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "levelTracingTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false, + "properties": { + "levelFunction": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": "DARKER" + }, + "levelFunctionMargin": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 25 + }, + "lineWidth": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 0.1 + }, + "levelThreeD": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + } + } + }, + "linkAllTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "crossHairTool": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": false + }, + "annotationOpacity": { + "controlVisible": true, + "controlEnabled": false, + "defaultValue": 1 + }, + "brushTool": { + "controlVisible": true, + "controlEnabled": true, + "properties": { + "radius": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "fillingTool":{ + "controlVisible": true, + "controlEnabled": true + }, + "eraserTool":{ + "controlVisible":true, + "controlEnabled":true, + "properties":{ + "radius":{ + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + } + } + }, + "labelmapLUT": { + "controlVisible": true, + "controlEnabled": true, + "defaultValue": 1 + }, + "labelmapUndoRedoTool":{ + "controlVisible":true, + "controlEnabled":true + }, + "labelmapOpacity":{ + "controlVisible":true, + "controlEnabled":true, + "defaultValue":1 + } + } + } + }, + "currentMaterializedTask": { + "label": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "inputs": { + "inputImage_key": { + "value": "20c2d549136e928edffcdb64b902474d", + "label": "T1", + "format": "nii.gz" + }, + "inputImage_key2": { + "value": "20c2d549136e928edffcdb64b902474d", + "label": "T2", + "format": "nii.gz" + }, + "inputImage_key3": { + "value": "20c2d549136e928edffcdb64b902474d", + "label": "FLAIR", + "format": "nii.gz" + }, + "inputLut_key1": { + "value": "c63c3c687bfeab4782a90290e1000260" + }, + "inputLutDescription_key1": { + "value": "c63c3c687bfeab4782a90290e1003c40" + } + }, + "status": "TODO", + "assignedTo": "gregory.bliault@u-bordeaux.fr", + "dueDate": "2019-09-28T22:43:29.728Z", + "miniWorkflow": "mw1", + "uuid": "53b643d3f55f98c38962b596530000d3" + }, + "status": "notstarted" + }, + "listOfCases": [ + { + "caseNumber": "training-9c39728c-8ce8-419e-886d-8138ba68e96e", + "caseStatus": "notstarted" + } + ], + "currentCase": "training-9c39728c-8ce8-419e-886d-8138ba68e96e" +} \ No newline at end of file diff --git a/tests/tools2.schema.test.js b/tests/tools2.schema.test.js new file mode 100644 index 0000000..e7516e8 --- /dev/null +++ b/tests/tools2.schema.test.js @@ -0,0 +1,21 @@ +const path = require('path'); +const fs = require('fs'); +const toolSchema = require('../schemas/tool.schema'); + + +let { test, expect ,it, describe} = global; + + +describe('Automatic Suite for testing all tools in examples', () => { + + const directoryPath = path.join(__dirname, '../examples/tool'); + const files = fs.readdirSync(directoryPath, function (err, files) { if (err) return []; }); + files.forEach(file=> { + + if (file.includes(".json")){ + test(`checks for ${file}`, () => { + expect(true).toBeAjvValid(toolSchema,require( "../examples/tool/"+file.split(".json")[0]).miniWorkflow.currentTool); + }); + } + }); +}); \ No newline at end of file From b05fdf6e4a2c797fca167031a9f70eb01ab5e9cf Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 26 Oct 2021 13:06:53 +0200 Subject: [PATCH 42/59] [Update] Updated manual. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 46771f1..7fbe0f7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ It is shown in [Example of matcher extension](/tests/jest.ajv.schema.test.js). ### Test cases Examples used for testing are defined in "examples" subfolders. To identify weak tests [Mutation tests](https://en.wikipedia.org/wiki/Mutation_testing) are applied (json files with _mutation._ prefix). +Manual tools definitions can be tested using [automatic suite](/tests/tools2.schema.test.js) + ### Links: From 2f28585bff0df138159da7dcfe33c1fbcac30ac8 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 28 Oct 2021 10:56:18 +0200 Subject: [PATCH 43/59] [Update] Added new suite for validation of local folder content. --- tests/tools2.schema.test.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/tools2.schema.test.js b/tests/tools2.schema.test.js index e7516e8..def300d 100644 --- a/tests/tools2.schema.test.js +++ b/tests/tools2.schema.test.js @@ -6,7 +6,7 @@ const toolSchema = require('../schemas/tool.schema'); let { test, expect ,it, describe} = global; -describe('Automatic Suite for testing all tools in examples', () => { +describe('Automatic Suite for testing all tools in examples folder', () => { const directoryPath = path.join(__dirname, '../examples/tool'); const files = fs.readdirSync(directoryPath, function (err, files) { if (err) return []; }); @@ -18,4 +18,19 @@ describe('Automatic Suite for testing all tools in examples', () => { }); } }); +}); + +describe('Automatic Suite for testing all tools in remote location folder', () => { + + const globalPath = "c:\\PATH"; // PUT YOUR LOCAL FOLDER HERE + const files = fs.readdirSync(globalPath, function (err, files) { if (err) return []; }); + files.forEach(file=> { + if (file.includes(".json")){ + const jsonObject = require( globalPath+file.split(".json")[0]); + if (jsonObject!=null && jsonObject.miniWorkflow!=null && jsonObject.miniWorkflow.currentTool!=null) + test(`checks for ${file}`, () => { + expect(true).toBeAjvValid(toolSchema,jsonObject.miniWorkflow.currentTool); + }); + } + }); }); \ No newline at end of file From 2588707abb62483ee57a53b49403b438d2b44b12 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 28 Oct 2021 10:58:09 +0200 Subject: [PATCH 44/59] [Update] Added missing enumerated values. Fixed initial windowLevel definition. --- schemas/viewer.schema.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/schemas/viewer.schema.json b/schemas/viewer.schema.json index 00e9703..8062aa6 100644 --- a/schemas/viewer.schema.json +++ b/schemas/viewer.schema.json @@ -45,7 +45,7 @@ "$id": "#dynamicValueInitialSlice", "description": "Value for initial slices calculated dynamically for a given image", "type": "string", - "enum": ["INFERIORMOST", "SUPERIORMOST", "LEFTMOST", "RIGHTMOST", "POSTERIORMOST", "ANTERIORMOST", "MIDDLE"] + "enum": ["INFERIORMOST", "SUPERIORMOST", "LEFTMOST", "RIGHTMOST", "POSTERIORMOST", "ANTERIORMOST", "MIDDLE","LABELMAP_COG"] }, "strategyInitialWindowLevel":{ @@ -112,6 +112,7 @@ "properties":{ "orientationAndSliceNumber": { "description": "Initial orientation and slice number", + "$comment": "TODO: make patterns for property values, i.e. strategy=number, required sliceNumber", "type": "object", "properties":{ "orientation": { @@ -128,6 +129,10 @@ "predefinedSliceNumber":{ "description": "Predefined slice number", "type": "number" + }, + "sliceNumber":{ + "description": "The reference key to input containing slice number value", + "type": "string" } }, "oneOf":[ @@ -136,6 +141,9 @@ }, { "required": ["orientation", "strategy", "predefinedSliceNumber"] + }, + { + "required": ["orientation", "strategy", "sliceValue"] } ] }, @@ -264,12 +272,12 @@ } }, "defaultValue": { - "description": "Default orientation of the viewer", + "description": "Default orientation of the viewer. It is not required since initial setting should be inherited from initial state of orientation. ", "type": "string", "enum":["SAGITTAL","CORONAL","AXIAL"] } }, - "required": ["defaultValue", "controlVisible","controlEnabled"] + "required": ["controlVisible","controlEnabled"] }, "smoothing":{ "description": "Smoothing control for the images", From b342ff3f821f104b5086e397d6c6c4200c96057b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 28 Oct 2021 11:00:01 +0200 Subject: [PATCH 45/59] [Update] Added definitions of scenes, luts, lutdescriptions, rois, overlays, masks and polydatas --- schemas/core.schema.json | 24 +++- schemas/tool.schema.json | 297 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 318 insertions(+), 3 deletions(-) diff --git a/schemas/core.schema.json b/schemas/core.schema.json index 162d80e..54ffcc8 100644 --- a/schemas/core.schema.json +++ b/schemas/core.schema.json @@ -45,7 +45,7 @@ "enum": ["String","Number","imageFileInOut","imageEntityInOut", "auxiliaryImageFile","lookUpTable","lookUpTableDescription","roiInOut","annotationInOut", "roiWithAnnotationsInOut","annotationTableDefinitionInputOutput", "annotationFormDefinitionInputOutput", - "annotationTableDataInOut", "annotationFormDataInOut"] + "annotationTableDataInOut", "annotationFormDataInOut","polyRoiInOut"] }, "inputOutput": { @@ -240,6 +240,25 @@ } ] }, + "polyRoiInOut": { + "$id": "#polyRoiInOut", + "description": "Schema defining a polydata as input output", + "allOf":[ + {"$ref": "#/definitions/inputOutput_spine"}, + { + "properties": { + "type":{ + "type": "string", + "enum": ["polyRoiInOut"] + }, + "dataEntityInOut_FileFormat":{ + "type": "string" + } + }, + "required": ["type"] + } + ] + }, "lookupTableInOut_FileFormat": { "$id" : "#lookupTableInOut_Type", @@ -467,7 +486,8 @@ {"$ref": "#/definitions/annotationTableDefinitionInOut"}, {"$ref": "#/definitions/annotationTableDataInOut"}, {"$ref": "#/definitions/annotationFormDefinitionInOut"}, - {"$ref": "#/definitions/annotationFormDataInOut"} + {"$ref": "#/definitions/annotationFormDataInOut"}, + {"$ref": "#/definitions/polyRoiInOut"} ] } diff --git a/schemas/tool.schema.json b/schemas/tool.schema.json index 6c8d3ee..97540d6 100644 --- a/schemas/tool.schema.json +++ b/schemas/tool.schema.json @@ -207,6 +207,170 @@ "required": ["layout", "renderWindows"] }, + "scenesConfiguration": { + "$id": "#scenesConfiguration", + "description": "Configuration of scene elements like original image, overlays (rois).", + "type": "object", + "properties": { + "primaryImageInputKey": { + "description": "The reference key to input image being used as reference image. All other images (eg. MRI, segmentations, attention maps) will use the space of original image. If overlays are of different size, they will be resampled and cut according to primary Image space (bounding box and origin).", + "type": "string" + }, + "rois": { + "description": "The object defining all scene elements (rois) to be visualized.", + "type": "object", + "properties": { + "overlays": { + "description": "The array of reference keys that needs to be visualized in scene. The order is important - following layers can cover predecessors", + "type": "array", + "items": { + "type": "string" + } + }, + "masks": { + "description": "The array of reference keys that needs to be visualized in scene. The order is important - following layers can cover predecessors", + "type": "array", + "items": { + "type": "string" + } + }, + "geometricalROIs": { + "description": "The array of reference keys that needs to be visualized in scene. The order is important - following layers can cover predecessors", + "type": "array", + "items": { + "type": "string" + } + }, + "polydatas": { + "description": "The array of reference keys that needs to be visualized in scene. The order is important - following layers can cover predecessors", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "required": ["primaryImageInputKey"] + }, + + "lookupTableConfiguration": { + "$id": "#lookupTableConfiguration", + "description": "Configuration of lookup table", + "type": "object", + "properties": { + "fromInputs": { + "description": "Shall lookup table configuration be taken from file. If so, proper input should be created.", + "type": "boolean" + }, + "lutInputKey": { + "description": "The key of input providing lookup table configuration (value).", + "type": "string" + }, + "type": { + "description": "The type of LUT when not provided by input.", + "$comment": "TODO: Establish whether still in use", + "type": "string", + "enum": ["none"] + } + }, + "additionalProperties": false, + "required": ["fromInputs"] + }, + "lookupTableDescriptionConfiguration": { + "$id": "#lookupTableDescriptionConfiguration", + "description": "Configuration of lookup table description", + "type": "object", + "properties": { + "fromInputs": { + "description": "Shall lookup table description configuration be taken from file. If so, proper input should be created.", + "type": "boolean" + }, + "lutDescriptionInputKey": { + "description": "The key of input providing lookup table configuration (value)." + }, + "type": { + "description": "The type of LUT description when not provided by input.", + "$comment": "TODO: Establish whether still in use", + "type": "string", + "enum": ["none"] + } + }, + "additionalProperties": false, + "required": ["fromInputs"] + }, + "overlayConfiguration": { + "$id": "#overlayConfiguration", + "description": "Configuration of overlay (labelmap, segmentation) to be visualized in the tool.", + "type": "object", + "properties": { + "fromInputs": { + "description": "Shall overlay configuration be taken from file. If so, proper input should be created.", + "type": "boolean" + }, + "lutKey": { + "description": "The reference key of lookup table that needs to be used to visualize label values with proper colors. Refers to lookup table configuration section.", + "type": "string" + }, + "lutDescriptionKey": { + "description": "The reference key of lookup table description that needs to be used to provide legend for overlay. Refers to lookup table descriptions configuration section. ", + "type": "string" + }, + "label": { + "description": "The label for an overlay to be displayed in legend (eg. 'Contribution of John Doe', 'Segmentation of Medulla Oblongata', etc.).", + "type": "string" + }, + "imageInputKey": { + "description": "The optional reference key to the input providing overlay data (if 'fromInputs' is set to be true)", + "type": "string" + }, + "onInit": { + "description": "The set of predefined conditions determining initial state of overlay", + "type": "object", + "properties": { + "visible": { + "description": "Shall overlay be visible when tool is initialized", + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["fromInputs","lutDescriptionKey","lutKey"] + }, + + "scalarTypeValueConfiguration": { + "$id": "#scalarTypeValueConfiguration", + "description": "Configuration of type and value properties for scalar", + "type": "object", + "properties": { + "type": { + "description": "Defines type of value ", + "type": "string", + "enum": ["scalar"] + }, + "value": { + "description": "Defines value ", + "type": "number" + } + } + }, + "vectorTypeValueConfiguration": { + "$id": "#vectorTypeValueConfiguration", + "description": "Configuration of type and value properties for vector", + "type": "object", + "properties": { + "type": { + "description": "Defines type of value ", + "type": "string", + "enum": ["vector"] + }, + "value": { + "description": "Defines value ", + "type": "array" + } + } + }, "widgetsTool": { "$id": "#widgetsTool", "description": "Widget for the annotation tool.", @@ -410,6 +574,135 @@ "description": "Configuration of the particular tool. For instance the particular configuration of an anotation tool.", "type": "object", "properties": { + "luts": { + "description": "Map of lookup table configurations", + "patternProperties" : { + ".*" : { + "allOf":[ + {"$ref": "#lookupTableConfiguration"} + ] + } + } + }, + "lutDescriptions": { + "description": "Map of lookup table descriptions configurations", + "patternProperties" : { + ".*" : { + "allOf":[ + {"$ref": "#lookupTableDescriptionConfiguration"} + ] + } + } + }, + "rois": { + "description": "Configuration of the regions of interests. Contains maps of different scene objects, like overlays, masks and geometrical ROIs", + "properties": { + "overlays": { + "description": "Map of overlays (labelmaps, segmentations) used in a tool.", + "patternProperties" : { + ".*" : { + "allOf":[ + {"$ref": "#overlayConfiguration"} + ], + "properties": { + "fromInputs": {}, + "lutKey": {}, + "lutDescriptionKey": {}, + "label": {}, + "imageInputKey": {}, + "onInit": {} + }, + "additionalProperties": false + } + } + }, + "masks": { + "description": "Map of overlays (labelmaps, segmentations) being used as masks in a tool.", + "patternProperties" : { + ".*" : { + "allOf":[ + {"$ref": "#overlayConfiguration"} + ], + "properties": { + "fromInputs": {}, + "lutKey": {}, + "lutDescriptionKey": {}, + "label": {}, + "imageInputKey": {}, + "onInit": {}, + "interactions": { + "description": "The definition of interactions between mask and other elements of tool. This is property that really distinguishes mask from overlay", + "type": "object", + "properties": { + "PREVENT_FROM_ADDING_PIN": { + "description": "The interaction between mask and anotation tool preventing from adding pin in a voxel belonging to mask", + "type": "object", + "properties": { + "type": {}, + "value": {} + }, + "oneOf": [ + {"$ref": "#scalarTypeValueConfiguration"}, + {"$ref": "#vectorTypeValueConfiguration"} + ], + "additionalProperties": false + }, + "PREVENT_FROM_CHANGING_VOXEL_VALUE": { + "description": "The interaction between mask and painting tools preventing from changing a value of voxel being at the same location as voxel belonging to mask", + "type": "object", + "properties": { + "type": {}, + "value": {} + }, + "oneOf": [ + {"$ref": "#scalarTypeValueConfiguration"}, + {"$ref": "#vectorTypeValueConfiguration"} + ], + "additionalProperties": false + } + } + } + }, + "additionalProperties": false + } + } + }, + "geometricalROIs": { + "description": "Map of geometrical (implicit) ROIs to be used in tool. TODO: To be finished - it is only a placeholder." + }, + "polydatas": { + "description": "Map of polydata ROIs to be used in a tool.", + "$comment": "This needs to be extended", + "patternProperties" : { + ".*" : { + "properties": { + "fromInputs": { + "description": "Shall polydata be taken from file. If so, proper input should be created.", + "type": "boolean" + }, + "dataInputKey": { + "description": "The reference key to input providing polydata.", + "type": "string" + } + } + } + } + } + } + }, + "scenes": { + "description": "Configuration of the scenes to be displayed in viewers.", + "type": "object", + "patternProperties" : { + ".*" : { + "allOf":[ + {"$ref": "#/definitions/scenesConfiguration"} + ]} + } + }, + "annotationScenes": { + "description": "Map of data bindings between ROIs and annotations with respect to scenes. TODO: To be finished - it is only a placeholder." + }, "viewers": { "description": "Configuration of the viewers for the tool.", "$ref": "#/definitions/viewersTool" @@ -418,7 +711,9 @@ "description": "Configuration of the widgets for the tool.", "$ref": "#/definitions/widgetsTool" } - } + }, + "required": ["scenes", "viewers"], + "additionalProperties": false } }, "required": ["name", "description", "version", "privacy", "type", "inputs", "outputs", "configuration"] From bf7ba4df064a4411d8d9eb7eb284c1f8cc1bb079 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 28 Oct 2021 11:01:12 +0200 Subject: [PATCH 46/59] [Update] Updated version to the new release 2.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 292b5d7..78882fa 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workflow" ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", - "version": "2.2.1", + "version": "2.4.0", "dependencies": { "ajv": "^6.12.2" }, From bbadac157241ac07cf4e102b6b635cfd5d22d335 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 29 Oct 2021 10:41:45 +0200 Subject: [PATCH 47/59] [Update] Added roiTable widget --- package.json | 2 +- schemas/widget.schema.json | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 78882fa..26ae07f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workflow" ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", - "version": "2.4.0", + "version": "2.4.1", "dependencies": { "ajv": "^6.12.2" }, diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index c5556be..cb7b6a5 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -1001,6 +1001,21 @@ "type": "boolean" } } + }, + "roiTableTool": { + "$id": "#roiTableTool", + "description": "Widget displaying table of ROIs. To be used in visualization tool. Designed for input from experimental dataset only.", + "type": "object", + "allOf": [{ "$ref": "#toolbarControls" }], + "additionalProperties": false, + "properties": { + "controlVisible": {}, + "controlEnabled": {}, + "defaultValue": { + "description": "Flag indicating whether table should be visible on init", + "type": "boolean" + } + } } }, "properties": { @@ -1032,6 +1047,7 @@ "spawnViewersWindowTool": {"$ref": "#/definitions/spawnViewersWindowTool"}, "exportAnnotationsToServerTool": {"$ref": "#/definitions/exportAnnotationsToServerTool"}, "exportAnnotationsToDiskTool": {"$ref": "#/definitions/exportAnnotationsToDiskTool"}, - "importAnnotationsFromDiskTool": {"$ref": "#/definitions/importAnnotationsFromDiskTool"} + "importAnnotationsFromDiskTool": {"$ref": "#/definitions/importAnnotationsFromDiskTool"}, + "roiTableTool": {"$ref": "#/definitions/roiTableTool"} } } \ No newline at end of file From 34d3bdaf98a851cd76e364ea6f7a81498a4672de Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 2 Dec 2021 15:19:08 +0100 Subject: [PATCH 48/59] [Update] Added cohort schema along with testing suite and examples. Update version in package.json. --- examples/cohort/epvs.json | 19 +++++ examples/cohort/epvs2.json | 30 ++++++++ examples/cohort/fat.json | 25 ++++++ examples/cohort/lacunes.json | 30 ++++++++ examples/cohort/miccai.json | 46 ++++++++++++ examples/cohort/miccai2.json | 30 ++++++++ package.json | 2 +- schemas/cohort.schema.json | 142 +++++++++++++++++++++++++++++++++++ tests/cohort.schema.test.js | 22 ++++++ 9 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 examples/cohort/epvs.json create mode 100644 examples/cohort/epvs2.json create mode 100644 examples/cohort/fat.json create mode 100644 examples/cohort/lacunes.json create mode 100644 examples/cohort/miccai.json create mode 100644 examples/cohort/miccai2.json create mode 100644 schemas/cohort.schema.json create mode 100644 tests/cohort.schema.test.js diff --git a/examples/cohort/epvs.json b/examples/cohort/epvs.json new file mode 100644 index 0000000..2929dd0 --- /dev/null +++ b/examples/cohort/epvs.json @@ -0,0 +1,19 @@ +{ + "uuid": "5ec47681-0872-4368-a53f-aaed3f8ac391", + "docType": "cohort", + "name": "EPVS MICCAI VALDO 2021", + "description": "EPVS dataset", + "creationDate": "2021-09-07T17:04:06.158040", + "privacy": "private", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "logo": { + "type": "url", + "value": "https://www.miccai2021.org/files/images/layout/en/miccai2021-logo.png", + "url": "https://valdo.grand-challenge.org/Task3" + }, + "relativePath": "EPVS_MICCAI_VALDO_2021", + "sourceSystem": "SPINE Insertion script" +} \ No newline at end of file diff --git a/examples/cohort/epvs2.json b/examples/cohort/epvs2.json new file mode 100644 index 0000000..815a5e5 --- /dev/null +++ b/examples/cohort/epvs2.json @@ -0,0 +1,30 @@ +{ + "uuid": "8631a9d7-6247-412c-b921-a73769e16647", + "docType": "cohort", + "name": "EPVS MICCAI VALDO 2021", + "description": "EPVS dataset", + "creationDate": "2021-09-01T15:38:09.421140", + "privacy": "private", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "diseases": [], + "keywords": [ + { + "label": "Brain", + "iri": "http://purl.org/sig/ont/fma/fma50801" + }, + { + "label": "MRI", + "iri": "http://purl.bioontology.org/ontology/SNOMEDCT/113091000" + } + ], + "relativePath": "EPVS_MICCAI_VALDO_2021", + "logo": { + "type": "url", + "value": "https://www.miccai2021.org/files/images/layout/en/miccai2021-logo.png", + "url": "https://valdo.grand-challenge.org/Task1" + }, + "sourceSystem": "SPINE Insertion script" +} \ No newline at end of file diff --git a/examples/cohort/fat.json b/examples/cohort/fat.json new file mode 100644 index 0000000..589b25a --- /dev/null +++ b/examples/cohort/fat.json @@ -0,0 +1,25 @@ +{ + "uuid": "35d4d7d1d0fb89504a8c0c74250008af", + "docType": "cohort", + "name": "Abdominal Fat Testing", + "description": "Testing database for Abdominal Faat", + "creationDate": "2020-03-10T23:54:14.063Z", + "privacy": "public", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "diseases": [], + "keywords": [ + { + "label": "Brain", + "iri": "http://purl.org/sig/ont/fma/fma50801" + }, + { + "label": "MRI", + "iri": "http://purl.bioontology.org/ontology/SNOMEDCT/113091000" + } + ], + "relativePath": "Cohort_AbdominalFat_Testing", + "sourceSystem": "manualInsertion" +} \ No newline at end of file diff --git a/examples/cohort/lacunes.json b/examples/cohort/lacunes.json new file mode 100644 index 0000000..c677e37 --- /dev/null +++ b/examples/cohort/lacunes.json @@ -0,0 +1,30 @@ +{ + "uuid": "2be44462-b0a5-4a32-b3a7-cd33bcdff3f1", + "docType": "cohort", + "name": "LACUNES MICCAI VALDO 2021", + "description": "LACUNES dataset", + "creationDate": "2021-09-21T14:37:57.266647", + "relativePath": "LACUNES_MICCAI_VALDO_2021", + "privacy": "private", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "diseases": [], + "keywords": [ + { + "label": "Brain", + "iri": "http://purl.org/sig/ont/fma/fma50801" + }, + { + "label": "MRI", + "iri": "http://purl.bioontology.org/ontology/SNOMEDCT/113091000" + } + ], + "logo": { + "type": "url", + "value": "https://www.miccai2021.org/files/images/layout/en/miccai2021-logo.png", + "url": "https://valdo.grand-challenge.org/Task3" + }, + "sourceSystem": "SPINE Insertion script" +} \ No newline at end of file diff --git a/examples/cohort/miccai.json b/examples/cohort/miccai.json new file mode 100644 index 0000000..346979f --- /dev/null +++ b/examples/cohort/miccai.json @@ -0,0 +1,46 @@ + { + "uuid": "023e3ce2-d2e0-4b73-87c1-c0c166b8c313", + "docType": "cohort", + "name": "MICROBLEEDS MICCAI VALDO 2021", + "description": "MICROBLEEDS dataset. he goal of this task is to develop an automated method to segment cerebral microbleeds in MRI scans. The training set contains segmentations of microbleeds in the full brain for every case. The submitted automated methods will be applied on the hidden test set and should output microbleed segmentations for the full brain. The predicted segmentation mask should be continuous and will be thresholded at 0.5 during evaluation to obtain a binary segmentation mask (0: background, 1: microbleed). An interesting public dataset that can be used to have additional training cases, is the microbleeds dataset that was released with the following paper: Q. Dou*, H. Chen*, L. Yu, J. Qin, L. Shi, P. A. Heng, et al. 'Automatic Detection of Cerebral Microbleeds from MR Images via 3D Convolutional Neural Networks'. IEEE Transactions on Medical Imaging (TMI), 2016.This set contains 20 cases with dot annotations of microbleeds.", + "creationDate": "2021-09-20T16:51:33.887301", + "privacy": "private", + "relativePath": "EPVS_MICCAI_VALDO_2021", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "diseases": [ + { + "label": "Multiple Sclerosis", + "icdCode": "G35", + "url": "https://www.icd10data.com/search?s=G35" + }, + { + "label": "Disseminated demyelination", + "icdCode": "G36", + "url": "https://www.icd10data.com/search?s=G36" + } + ], + "keywords": [ + { + "label": "Multiple Sclerosis", + "iri": "http://purl.bioontology.org/ontology/SNOMEDCT/24700007" + }, + { + "label": "Freesurfer", + "iri": "http://purl.org/sig/ont/fma/fma271960" + }, + { + "label": "White Matter", + "iri": "http://purl.org/sig/ont/fma/fma83929" + } + ], + "logo": { + "type": "url", + "value": "https://www.miccai2021.org/files/images/layout/en/miccai2021-logo.png", + "url": "https://valdo.grand-challenge.org/Task2" + }, + "sourceSystem": "SPINE Insertion script" + } + diff --git a/examples/cohort/miccai2.json b/examples/cohort/miccai2.json new file mode 100644 index 0000000..40b29fe --- /dev/null +++ b/examples/cohort/miccai2.json @@ -0,0 +1,30 @@ +{ + "uuid": "20c2d549136e928edffcdb64b901f990", + "docType": "cohort", + "name": "Testing Openneuro. Dataset ds000221.", + "description": "Testing database from Openneuro", + "creationDate": "2019-08-21T19:32:14.063Z", + "privacy": "public", + "pi": "a60017ce-6be9-4054-9f84-06722d5166be", + "co-investigators": [ + "0f8fc6f7-1361-4f55-9bfa-b8e70c3a5718" + ], + "relativePath": "CohortOpenneuro_ds0002221", + "sourceSystem": "manualInsertion", + "diseases": [], + "keywords": [ + { + "label": "Brain", + "iri": "http://purl.org/sig/ont/fma/fma50801" + }, + { + "label": "MRI", + "iri": "http://purl.bioontology.org/ontology/SNOMEDCT/113091000" + } + ], + "logo": { + "type": "url", + "value": "https://openneuro.org/assets/on-light-horz.23ff22c3.svg", + "url": "https://openneuro.org/datasets/ds000221/versions/1.0.0" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 26ae07f..2ca7264 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workflow" ], "description": "Set of JSON schemas for the SPINE Virtual Laboratory", - "version": "2.4.1", + "version": "2.5.0", "dependencies": { "ajv": "^6.12.2" }, diff --git a/schemas/cohort.schema.json b/schemas/cohort.schema.json new file mode 100644 index 0000000..64d43a2 --- /dev/null +++ b/schemas/cohort.schema.json @@ -0,0 +1,142 @@ +{ + "$id":"https://raw.githubusercontent.com/SPINEProject/SPINE-json-schema/master/schemas/cohort.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Cohort schema", + "description": "Schema for cohort document.", + "$comment": "The schema can be indirectly applied to both CouchDB document and Cohort API response data, assuming that: 'uuid' property replaced '_id' and 'rev' property is removed.", + "type": "object", + "definitions": { + "cohortLogo": { + "$id" : "#cohortLogo", + "description": "Logo of a given cohort", + "type": "object", + "properties": { + "type": { + "description":"Type of medium to be used to display image.", + "$comment": "So far only one option is available: 'url', since this can be used both for links and serialized image data. In future other forms of embedding external graphics can be useful", + "type":"string", + "enum": ["url"] + }, + "value": { + "description":"The link to logo or encoded with base64 serialized version of cohort", + "$comment": "The value can be an absolute link eg.https://www.miccai2021.org/files/images/layout/en/miccai2021-logo.png or encoded string with binaries, i.e. data:image/png;base64,...", + "type":"string" + }, + "url": { + "description":"Link to the external Cohort home page", + "type":"string" + } + }, + "additionalProperties": false + } + }, + "properties": { + "uuid": { + "description": "Cohort UUID", + "type": "string" + }, + "docType": { + "description": "Cohort", + "type": "string", + "enum": ["cohort"] + }, + "name": { + "description": "Cohort name", + "type": "string" + }, + "pi": { + "description": "Identifier of Principal Investigator of cohort", + "$comment": "Uses uuid format", + "type": "string" + }, + "co-investigators": { + "description": "Identifiers of co-investigators of cohort", + "$comment": "Uses uuid format", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "Cohort description", + "type": "string" + }, + "version": { + "description": "Cohort version", + "type": "string" + }, + "creationDate":{ + "description": "Date of creation of the cohort.", + "$comment": "Uses ISO 8601", + "type":"string" + }, + "privacy": { + "description": "Cohort privacy", + "type": "string", + "enum": [ + "PUBLIC", + "public", + "PRIVATE", + "private" + ] + }, + "relativePath": { + "description": "Relative Path to cohort folder in filesystem", + "$comment": "This is for visualization of assets", + "type": "string" + }, + "sourceSystem": { + "description": "How cohort was introduced into SPINE", + "type": "string", + "enum": ["SPINE Insertion script","manualInsertion"] + }, + "logo": { + "$ref": "#/definitions/cohortLogo" + }, + "keywords": { + "description": "Array of keywords defining contents of cohort", + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "description":"Keyword label", + "type": "string" + }, + "iri": { + "description":"internationalized Resource Identifier for keyword concept", + "$comment":"This is an ontological identifier, that can be used as link in some cases, eg. http://purl.bioontology.org/ontology/SNOMEDCT/24700007", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "diseases": { + "description": "Array of disease codes for cohort", + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "description":"Label to be displayed along with the disease code, eg. Multiple Sclerosis", + "type": "string" + }, + "icdCode": { + "description":"International Classification of Diseases Code", + "$comment":"This is an ICD 10 code, eg. G35 for MS", + "type": "string" + }, + "url": { + "description":"Link to external page with disease classification", + "$comment":"This link can refer to external page with disease description, eg. https://www.icd10data.com/search?s=G36", + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "required": ["name","privacy","uuid","pi"] +} \ No newline at end of file diff --git a/tests/cohort.schema.test.js b/tests/cohort.schema.test.js new file mode 100644 index 0000000..f3b2beb --- /dev/null +++ b/tests/cohort.schema.test.js @@ -0,0 +1,22 @@ +const path = require('path'); +const fs = require('fs'); +const cohortSchema = require('../schemas/cohort.schema'); + + +let { test, expect ,it, describe} = global; + + +describe('Automatic Suite for testing all cohorts in examples folder', () => { + + const directoryPath = path.join(__dirname, '../examples/cohort'); + const files = fs.readdirSync(directoryPath, function (err, files) { if (err) return []; }); + files.forEach(file=> { + + if (file.includes(".json")){ + test(`checks for ${file}`, () => { + expect(true).toBeAjvValid(cohortSchema,require( "../examples/cohort/"+file.split(".json")[0])); + }); + } + }); +}); + From ac3ac4c2e7d331c3cb2575fd7c6632dc85564bb3 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 17 Dec 2021 17:31:44 +0100 Subject: [PATCH 49/59] [Update] Added mainTool widget configuration schema. --- schemas/widget.schema.json | 47 +++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/schemas/widget.schema.json b/schemas/widget.schema.json index cb7b6a5..d0e9a51 100644 --- a/schemas/widget.schema.json +++ b/schemas/widget.schema.json @@ -1016,6 +1016,50 @@ "type": "boolean" } } + }, + "mainTool": { + "$id": "#mainTool", + "description": "Widget defining properties of the entire manual tool.", + "type": "object", + "additionalProperties": false, + "properties": { + "activeViewer ": { + "description": "Defines which viewer will be initially the source of events", + "type": "string" + }, + "leftButtonMode": { + "description": "Defines which widget will be used initially on mouse left-click (important for mutually exclusive brush, fill, pin etc.). Widgets are encoded with numbers", + "type": "number", + "maximum": 10, + "minimum": 0, + "$comment": " NONE: 0,PIN: 2,BRUSH: 3,ERASER: 4,FILLING: 5,AZIMUTH: 6, RULER:7,SAMPLER:8,LEVEL:9,DRAW:10" + }, + "annotationsVisible": { + "description": "Defines whether annotations shall be visible on init", + "type": "boolean" + }, + "projectionsVisible": { + "description": "Defines whether annotation projections shall be visible on init", + "type": "boolean" + }, + "navigationModeLabel": { + "description": "Defines which transition after going to next/previous step should take place: to the next step or to the next case (keeping the same step)", + "type": "string", + "enum": ["step","case"], + "$comment": "By default step is used" + }, + "autosaving": { + "description": "Defines whether and how often autosaving for a chosen outputs shall be applied", + "type": "object", + "additionalProperties": false, + "properties": { + "interval": { + "description": "if set up, defines how often (in seconds) autosave should be performed", + "type": "number" + } + } + } + } } }, "properties": { @@ -1048,6 +1092,7 @@ "exportAnnotationsToServerTool": {"$ref": "#/definitions/exportAnnotationsToServerTool"}, "exportAnnotationsToDiskTool": {"$ref": "#/definitions/exportAnnotationsToDiskTool"}, "importAnnotationsFromDiskTool": {"$ref": "#/definitions/importAnnotationsFromDiskTool"}, - "roiTableTool": {"$ref": "#/definitions/roiTableTool"} + "roiTableTool": {"$ref": "#/definitions/roiTableTool"}, + "mainTool": {"$ref": "#/definitions/mainTool"} } } \ No newline at end of file From b6beaaf0816d38c5a50eddde0ad6736a06350c74 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 17 Dec 2021 17:35:20 +0100 Subject: [PATCH 50/59] [Update][#42] Propoerty docType is required in cohort document. --- schemas/cohort.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/cohort.schema.json b/schemas/cohort.schema.json index 64d43a2..5fa999d 100644 --- a/schemas/cohort.schema.json +++ b/schemas/cohort.schema.json @@ -138,5 +138,5 @@ } }, "additionalProperties": false, - "required": ["name","privacy","uuid","pi"] + "required": ["name","privacy","uuid","pi","docType"] } \ No newline at end of file From 9380c8a9c821fa8ba963fac9eb3ca97ce8f5488b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 10 Mar 2022 12:07:38 +0100 Subject: [PATCH 51/59] [Update] Added experimentalPlan schema --- .../experimentConfiguration_V1_1.json | 157 ++++++++++++++++++ schemas/experimentalPlan.schema.json | 84 ++++++++++ tests/experimentalPlan.schema.test.js | 22 +++ 3 files changed, 263 insertions(+) create mode 100644 examples/experimentalPlan/experimentConfiguration_V1_1.json create mode 100644 schemas/experimentalPlan.schema.json create mode 100644 tests/experimentalPlan.schema.test.js diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json new file mode 100644 index 0000000..9c22637 --- /dev/null +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -0,0 +1,157 @@ +{ + "_id": "fc6092c28f33a27d686da9e16c000646", + "docType": "experimentConfiguration", + "creationDate": "2022-03-06T16:11:21.798Z", + "reference": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "projectId": "25b2a82a03fc48d45377c1703c4ba42e" + }, + "config": { + "experimentalPlan": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "docType": "experimentalPlan", + "questionnaires": [ + "5b66d65a-cd61-4358-9248-5ae53d173e62", + "5a8460d1-d731-4b78-9193-182d6873846f", + "2910b5f86d3389dbf4fd16dc27000291" + ], + "planning": { + "startExperiment": { + "time-windows": [ + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire", + "trigger": "system", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + } + ] + }, + "evaluationPeriod": { + "cyclic": [ + { + "cycle-period": "daily", + "time-windows": [ + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "name": "VAS Fatigue", + "trigger": "system", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 1, + "toleranceUnit": "minutes", + "reminders": [ + 0, + 50 + ], + "remindersUnit": "minutes" + } + ] + }, + { + "cycle-period": "custom", + "time-windows": [ + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire (monthly)", + "trigger": "system", + "repeat": 28, + "unitRepeat": "days", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + } + ] + } + ], + "specialEvent": { + "time-windows": [ + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": false, + "trigger": "sleep", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": false, + "trigger": "nap", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpSleep", + "isVAS": false, + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpSleep", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpNap", + "isVAS": false, + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpNap", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + } + ] + } + }, + "endingExperiment": { + "ending-criteria": "timeAfterExperimentStart", + "timeAfterExperimentStart":88, + "timeAfterExperimentStartUnit":"days" + } + } + } + } +} \ No newline at end of file diff --git a/schemas/experimentalPlan.schema.json b/schemas/experimentalPlan.schema.json new file mode 100644 index 0000000..4ef3e22 --- /dev/null +++ b/schemas/experimentalPlan.schema.json @@ -0,0 +1,84 @@ +{ + "$id":"https://raw.githubusercontent.com/SPINEProject/SPINE-json-schema/master/schemas/experimentalPlan.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Description of Experimental Plan ", + "description": "Schema for Experimental plan being a part of experiment configuration.", + "type": "object", + "definitions":{ + "timeWindow": { + "$id": "#timeWindow", + "description": "Boolean flags defining controls for a given widget.", + "type": "object", + "$comment": "So far control components for widgets are implemented with Toggleable Buttons. Model can be extended by adding other complex controls, eg. sets of buttons, dropdown etc.", + "properties": { + "controlVisible": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is visible." + }, + "controlEnabled": { + "type": "boolean", + "description": "Defines whether control component (eg. Button) is enabled, ie. can change a state. If not enabled, then works only as indicator." + } + }, + "required": ["controlEnabled", "controlVisible"] + } + }, + "properties": { + "experimentId": { + "type": "string", + "description": "Reference to the experiment id" + }, + "docType": { + "type": "string", + "description": "Type of document", + "enum": [ + "experimentalPlan" + ] + }, + "questionnaires": { + "type": "array", + "items": { + "type": "string", + "description": "Reference to the questionnaire Id" + } + }, + "planning": { + "type": "object", + "properties": { + "startExperiment": { + "type": "object", + "properties": { + "time-windows": { + + } + } + }, + "evaluationPeriod": { + "type": "object" + }, + "endingExperiment": { + "type": "object", + "properties": { + "time-windows": { + + }, + "ending-criteria": { + "type": "string", + "enum": ["timeAfterExperimentStart"] + }, + "timeAfterExperimentStart":{ + "type": "number" + }, + "timeAfterExperimentStartUnit":{ + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "required": ["experimentId", "docType","questionnaires","planning"], + "additionalProperties": false +} diff --git a/tests/experimentalPlan.schema.test.js b/tests/experimentalPlan.schema.test.js new file mode 100644 index 0000000..8983fe6 --- /dev/null +++ b/tests/experimentalPlan.schema.test.js @@ -0,0 +1,22 @@ +const path = require('path'); +const fs = require('fs'); +const planSchema = require('../schemas/experimentalPlan.schema'); + + +let { test, expect , describe} = global; + + +describe('Automatic Suite for testing all plans in examples folder', () => { + + const examplesPath = '../examples/experimentalPlan/'; + const directoryPath = path.join(__dirname, examplesPath); + const files = fs.readdirSync(directoryPath, function (err, files) { if (err) return []; }); + files.forEach(file=> { + if (file.includes(".json")){ + test(`checks for ${file}`, () => { + expect(true).toBeAjvValid(planSchema,require( examplesPath+file.split(".json")[0]).config.experimentalPlan); + }); + } + }); +}); + From 62b370b8feee2651abe50aa5d144a0630bb15de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Thu, 10 Mar 2022 09:04:57 -0500 Subject: [PATCH 52/59] Add "one-time questionnaires" scheduling "one-time questionnaires" scheduling is specified in the section "startExperiment". --- .../experimentConfiguration_V1_1.json | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json index 9c22637..1a2014a 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -20,7 +20,7 @@ "time-windows": [ { "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire", + "name": "one-time questionnaire starting day", "trigger": "system", "timeToComplete": 3, "timeToCompleteUnit": "days", @@ -28,6 +28,48 @@ "toleranceUnit": "hours", "reminders": [6, 12, 24, 30, 36, 48, 54, 60], "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventUnit": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventUnit": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventUnit": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" } ] }, @@ -154,4 +196,4 @@ } } } -} \ No newline at end of file +} From 7e07f464171fc6736d30115a2c7d337917441698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Morales=20Pinz=C3=B3n?= Date: Thu, 10 Mar 2022 09:42:25 -0500 Subject: [PATCH 53/59] Remove cyclic questionnaires --- .../experimentConfiguration_V1_1.json | 44 ++----------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json index 1a2014a..b364942 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -74,50 +74,12 @@ ] }, "evaluationPeriod": { - "cyclic": [ - { - "cycle-period": "daily", - "time-windows": [ - { - "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "name": "VAS Fatigue", - "trigger": "system", - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 1, - "toleranceUnit": "minutes", - "reminders": [ - 0, - 50 - ], - "remindersUnit": "minutes" - } - ] - }, - { - "cycle-period": "custom", - "time-windows": [ - { - "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire (monthly)", - "trigger": "system", - "repeat": 28, - "unitRepeat": "days", - "timeToComplete": 3, - "timeToCompleteUnit": "days", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], - "remindersUnit": "hours" - } - ] - } - ], + "cyclic": [ ], "specialEvent": { "time-windows": [ { "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": false, + "isVAS": true, "trigger": "sleep", "timeToComplete": 2, "timeToCompleteUnit": "hours", @@ -128,7 +90,7 @@ }, { "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": false, + "isVAS": true, "trigger": "nap", "timeToComplete": 2, "timeToCompleteUnit": "hours", From 6a124421f8be47badd941f3d190b21eeb90fd01b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Fri, 11 Mar 2022 13:00:04 +0100 Subject: [PATCH 54/59] Update experimentConfiguration_V1_1.json --- .../experimentConfiguration_V1_1.json | 331 +++++++++--------- 1 file changed, 173 insertions(+), 158 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json index b364942..5e9989f 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -1,161 +1,176 @@ { - "_id": "fc6092c28f33a27d686da9e16c000646", - "docType": "experimentConfiguration", - "creationDate": "2022-03-06T16:11:21.798Z", - "reference": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", - "projectId": "25b2a82a03fc48d45377c1703c4ba42e" - }, - "config": { - "experimentalPlan": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", - "docType": "experimentalPlan", - "questionnaires": [ - "5b66d65a-cd61-4358-9248-5ae53d173e62", - "5a8460d1-d731-4b78-9193-182d6873846f", - "2910b5f86d3389dbf4fd16dc27000291" - ], - "planning": { - "startExperiment": { - "time-windows": [ - { - "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire starting day", - "trigger": "system", - "timeToComplete": 3, - "timeToCompleteUnit": "days", - "tolerance": 0, - "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], - "remindersUnit": "hours" - }, - { - "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire 25 days after the previous", - "trigger": "timeAfterPreviousEvent", - "timeAfterPreviousEvent": 25, - "timeAfterPreviousEventUnit": "days", - "timeAfterPreviousEventUnit": "eventEnds", - "timeToComplete": 3, - "timeToCompleteUnit": "days", - "tolerance": 0, - "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], - "remindersUnit": "hours" - }, - { - "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire 25 days after the previous", - "trigger": "timeAfterPreviousEvent", - "timeAfterPreviousEvent": 25, - "timeAfterPreviousEventUnit": "days", - "timeAfterPreviousEventUnit": "eventEnds", - "timeToComplete": 3, - "timeToCompleteUnit": "days", - "tolerance": 0, - "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], - "remindersUnit": "hours" - }, - { - "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", - "name": "one-time questionnaire 25 days after the previous", - "trigger": "timeAfterPreviousEvent", - "timeAfterPreviousEvent": 25, - "timeAfterPreviousEventUnit": "days", - "timeAfterPreviousEventUnit": "eventEnds", - "timeToComplete": 3, - "timeToCompleteUnit": "days", - "tolerance": 0, - "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], - "remindersUnit": "hours" - } - ] - }, - "evaluationPeriod": { - "cyclic": [ ], - "specialEvent": { - "time-windows": [ - { - "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": true, - "trigger": "sleep", - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - }, - { - "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": true, - "trigger": "nap", - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - }, - { - "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", - "trigger": "wakeUpSleep", - "isVAS": false, - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - }, - { - "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": true, - "trigger": "wakeUpSleep", - "repeat": 4, - "unitRepeat": "hours", - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - }, - { - "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", - "trigger": "wakeUpNap", - "isVAS": false, - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - }, - { - "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", - "isVAS": true, - "trigger": "wakeUpNap", - "repeat": 4, - "unitRepeat": "hours", - "timeToComplete": 2, - "timeToCompleteUnit": "hours", - "tolerance": 0, - "toleranceUnit": "minutes", - "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], - "remindersUnit": "minutes" - } - ] + "_id": "fc6092c28f33a27d686da9e16c000646", + "docType": "experimentConfiguration", + "creationDate": "2022-03-06T16:11:21.798Z", + "reference": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "projectId": "25b2a82a03fc48d45377c1703c4ba42e" + }, + "config": { + "experimentalPlan": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "docType": "experimentalPlan", + "questionnaires": [ + "5b66d65a-cd61-4358-9248-5ae53d173e62", + "5a8460d1-d731-4b78-9193-182d6873846f", + "2910b5f86d3389dbf4fd16dc27000291" + ], + "eventParameters": { + "tolerances": [ + { + "event": "wakeUpSleep", + "tolerance": 2, + "toleranceUnit": "hours" + }, + { + "event": "wakeUpNap", + "tolerance": 2, + "toleranceUnit": "hours" } - }, - "endingExperiment": { - "ending-criteria": "timeAfterExperimentStart", - "timeAfterExperimentStart":88, - "timeAfterExperimentStartUnit":"days" - } - } - } - } + ], + "maxAwakeTime": "15:10:00" + }, + "planning": { + "startExperiment": { + "time-windows": [ + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire starting day", + "trigger": "system", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventRelative": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventRelative": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 25, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventRelative": "eventEnds", + "timeToComplete": 3, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + } + ] + }, + "evaluationPeriod": { + "cyclic": [ ], + "specialEvent": { + "time-windows": [ + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "sleep", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "nap", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpSleep", + "isVAS": false, + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpSleep", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpNap", + "isVAS": false, + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpNap", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 2, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110], + "remindersUnit": "minutes" + } + ] + } + }, + "endingExperiment": { + "ending-criteria": "timeAfterExperimentStart", + "timeAfterExperimentStart":88, + "timeAfterExperimentStartUnit":"days" + } + } + } + } } From b22ad4a09f0fa54c1a73f291cab9d435de3e521b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Mon, 14 Mar 2022 16:47:06 +0100 Subject: [PATCH 55/59] Update according to changes proposed by Giovanni Added experimentDeadlineTime and experimentDeadlineUnit props to ending section. ending-criteria and other props are optional and related to the fact whether there are questionnaires at ending. --- examples/experimentalPlan/experimentConfiguration_V1_1.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json index 5e9989f..17fbf6b 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -166,9 +166,8 @@ } }, "endingExperiment": { - "ending-criteria": "timeAfterExperimentStart", - "timeAfterExperimentStart":88, - "timeAfterExperimentStartUnit":"days" + "experimentDeadlineTime":88, + "experimentDeadlineUnit":"days" } } } From c5783f9d6e4988436cb7f05a4040e568ed64aa58 Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 15 Mar 2022 13:57:24 +0100 Subject: [PATCH 56/59] [Update] Added experimentalPlan - short version --- .../experimentConfiguration_V1_1Short.json | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 examples/experimentalPlan/experimentConfiguration_V1_1Short.json diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1Short.json b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json new file mode 100644 index 0000000..1f81860 --- /dev/null +++ b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json @@ -0,0 +1,161 @@ +{ + "_id": "fc6092c28f33a27d686da9e16c000646", + "docType": "experimentConfiguration", + "creationDate": "2022-03-06T16:11:21.798Z", + "reference": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "projectId": "25b2a82a03fc48d45377c1703c4ba42e" + }, + "config": { + "experimentalPlan": { + "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "docType": "experimentalPlan", + "questionnaires": [ + "5b66d65a-cd61-4358-9248-5ae53d173e62", + "5a8460d1-d731-4b78-9193-182d6873846f", + "2910b5f86d3389dbf4fd16dc27000291" + ], + "eventParameters": { + "tolerances": [ + { + "event": "wakeUpSleep", + "tolerance": 10, + "toleranceUnit": "minutes" + }, + { + "event": "wakeUpNap", + "tolerance": 10, + "toleranceUnit": "minutes" + } + ], + "maxAwakeTime": "8:10:00" + }, + "planning": { + "startExperiment": { + "time-windows": [ + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire starting day", + "trigger": "system", + "timeToComplete": 1, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 3, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventRelative": "eventEnds", + "timeToComplete": 1, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + }, + { + "questionnaireId": "2910b5f86d3389dbf4fd16dc27000291", + "name": "one-time questionnaire 25 days after the previous", + "trigger": "timeAfterPreviousEvent", + "timeAfterPreviousEvent": 3, + "timeAfterPreviousEventUnit": "days", + "timeAfterPreviousEventRelative": "eventEnds", + "timeToComplete": 1, + "timeToCompleteUnit": "days", + "tolerance": 0, + "toleranceUnit": "hours", + "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "remindersUnit": "hours" + } + ] + }, + "evaluationPeriod": { + "cyclic": [ ], + "specialEvent": { + "time-windows": [ + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "sleep", + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "nap", + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpSleep", + "isVAS": false, + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpSleep", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5a8460d1-d731-4b78-9193-182d6873846f", + "trigger": "wakeUpNap", + "isVAS": false, + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + }, + { + "questionnaireId": "5b66d65a-cd61-4358-9248-5ae53d173e62", + "isVAS": true, + "trigger": "wakeUpNap", + "repeat": 4, + "unitRepeat": "hours", + "timeToComplete": 1, + "timeToCompleteUnit": "hours", + "tolerance": 0, + "toleranceUnit": "minutes", + "reminders": [10, 20, 30, 40, 50], + "remindersUnit": "minutes" + } + ] + } + }, + "endingExperiment": { + "experimentDeadlineTime":9, + "experimentDeadlineUnit":"days" + } + } + } + } +} From 77fe5a5d14fa1ae15cf1a90c52a7a0e88a69cf5b Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 15 Mar 2022 13:58:29 +0100 Subject: [PATCH 57/59] [Update] Updated experimentalPlan - short version --- .../experimentalPlan/experimentConfiguration_V1_1Short.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1Short.json b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json index 1f81860..09753f7 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1Short.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json @@ -41,7 +41,7 @@ "timeToCompleteUnit": "days", "tolerance": 0, "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "reminders": [6, 12], "remindersUnit": "hours" }, { @@ -55,7 +55,7 @@ "timeToCompleteUnit": "days", "tolerance": 0, "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "reminders": [6, 12], "remindersUnit": "hours" }, { @@ -69,7 +69,7 @@ "timeToCompleteUnit": "days", "tolerance": 0, "toleranceUnit": "hours", - "reminders": [6, 12, 24, 30, 36, 48, 54, 60], + "reminders": [6, 12], "remindersUnit": "hours" } ] From e7562279e1ff8faf1afb46cceb68f26fa06b623e Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Tue, 15 Mar 2022 15:24:27 +0100 Subject: [PATCH 58/59] [Update] Updated schema for experimentalPlan --- schemas/experimentalPlan.schema.json | 48 ++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/schemas/experimentalPlan.schema.json b/schemas/experimentalPlan.schema.json index 4ef3e22..baaa346 100644 --- a/schemas/experimentalPlan.schema.json +++ b/schemas/experimentalPlan.schema.json @@ -42,6 +42,39 @@ "description": "Reference to the questionnaire Id" } }, + "eventParameters": { + "type": "object", + "description": "Properties parameterizing event handling in mobile application, eg. after clicking on 'sleep' button", + "properties": { + "tolerances": { + "type": "array", + "items": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": ["wakeUpSleep","wakeUpNap"], + "description": "Type of event" + }, + "tolerance": { + "type": "number" + }, + "toleranceUnit": { + "type": "string", + "enum": ["days","hours","minutes"], + "description": "Time unit for 'toleranceUnit'" + } + }, + "additionalProperties": false + } + }, + "maxAwakeTime": { + "type": "string", + "pattern": "[0-9]{0,2}:[0-9]{2}:[0-9]{2}" + } + }, + "additionalProperties": false + }, "planning": { "type": "object", "properties": { @@ -67,10 +100,21 @@ "enum": ["timeAfterExperimentStart"] }, "timeAfterExperimentStart":{ - "type": "number" + "type": "number", + "description": "Criterion for executing questionnaires if defined in time-windows section." }, "timeAfterExperimentStartUnit":{ - "type": "string" + "type": "string", + "description": "Time unit for 'timeAfterExperimentStartUnit'" + }, + "experimentDeadlineTime":{ + "type": "number", + "description": "Stop criterion for experiment. It is calculated in relation to the starting time of experiment" + }, + "experimentDeadlineUnit":{ + "type": "string", + "enum": ["days","hours","minutes"], + "description": "Time unit for 'experimentDeadlineTime'" } }, "additionalProperties": false From c7bb72a21cd911218cdb106f028b5327f43d65ea Mon Sep 17 00:00:00 2001 From: Andrzej Marciniak Date: Thu, 17 Mar 2022 17:11:37 +0100 Subject: [PATCH 59/59] [Update] Updated experimentId --- examples/experimentalPlan/experimentConfiguration_V1_1.json | 4 ++-- .../experimentalPlan/experimentConfiguration_V1_1Short.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1.json b/examples/experimentalPlan/experimentConfiguration_V1_1.json index 17fbf6b..acf6eff 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1.json @@ -3,12 +3,12 @@ "docType": "experimentConfiguration", "creationDate": "2022-03-06T16:11:21.798Z", "reference": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "experimentId": "ef2173835dfc5b25509f0fa87e006929", "projectId": "25b2a82a03fc48d45377c1703c4ba42e" }, "config": { "experimentalPlan": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "experimentId": "ef2173835dfc5b25509f0fa87e006929", "docType": "experimentalPlan", "questionnaires": [ "5b66d65a-cd61-4358-9248-5ae53d173e62", diff --git a/examples/experimentalPlan/experimentConfiguration_V1_1Short.json b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json index 09753f7..b10d408 100644 --- a/examples/experimentalPlan/experimentConfiguration_V1_1Short.json +++ b/examples/experimentalPlan/experimentConfiguration_V1_1Short.json @@ -3,12 +3,12 @@ "docType": "experimentConfiguration", "creationDate": "2022-03-06T16:11:21.798Z", "reference": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "experimentId": "ef2173835dfc5b25509f0fa87e006929", "projectId": "25b2a82a03fc48d45377c1703c4ba42e" }, "config": { "experimentalPlan": { - "experimentId": "4c7fce096dcd7b8da5a2740fda000b96", + "experimentId": "ef2173835dfc5b25509f0fa87e006929", "docType": "experimentalPlan", "questionnaires": [ "5b66d65a-cd61-4358-9248-5ae53d173e62",