From e3556697d5ddf85d02495c38730c4c08331aa002 Mon Sep 17 00:00:00 2001 From: jwildfire Date: Wed, 15 May 2019 16:20:44 -0700 Subject: [PATCH 1/5] coerces strings to array if needed. improved detail defaults. fix #131 --- src/configuration/syncSettings.js | 58 ++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/configuration/syncSettings.js b/src/configuration/syncSettings.js index eced49b..53bda3b 100644 --- a/src/configuration/syncSettings.js +++ b/src/configuration/syncSettings.js @@ -24,7 +24,7 @@ export default function syncSettings(settings) { settings.value_col }] [${settings.unit_col}]\n${settings.x.label} = [${settings.x.column}]`; - //Conadd custom tooltip values + //add custom tooltip values if (settings.tooltip_cols) { settings.tooltip_cols.forEach(function(tooltip) { var obj = typeof tooltip == 'string' ? { label: tooltip, value_col: tooltip } : tooltip; @@ -73,5 +73,61 @@ export default function syncSettings(settings) { settings.unscheduled_visit_regex = new RegExp(pattern, flags); } + //handle a string argument to filters + if (!(settings.filters instanceof Array)) + settings.filters = typeof settings.filters === 'string' ? [settings.filters] : []; + + //handle a string argument to details + if (!(settings.details instanceof Array)) + settings.details = typeof settings.details === 'string' ? [settings.details] : []; + + //Define default details. + let defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; + if (Array.isArray(settings.filters)) + settings.filters + .filter(filter => filter.value_col !== settings.id_col) + .forEach(filter => + defaultDetails.push({ + value_col: filter.value_col ? filter.value_col : filter, + label: filter.label + ? filter.label + : filter.value_col + ? filter.value_col + : filter + }) + ); + defaultDetails.push({ value_col: settings.value_col, label: 'Result' }); + if (settings.normal_col_low) + defaultDetails.push({ value_col: settings.normal_col_low, label: 'Lower Limit of Normal' }); + if (settings.normal_col_high) + defaultDetails.push({ + value_col: settings.normal_col_high, + label: 'Upper Limit of Normal' + }); + + //If [settings.details] is not specified: + if (!settings.details) settings.details = defaultDetails; + else { + //If [settings.details] is specified: + //Allow user to specify an array of columns or an array of objects with a column property + //and optionally a column label. + settings.details.forEach(detail => { + if ( + defaultDetails + .map(d => d.value_col) + .indexOf(detail.value_col ? detail.value_col : detail) === -1 + ) + defaultDetails.push({ + value_col: detail.value_col ? detail.value_col : detail, + label: detail.label + ? detail.label + : detail.value_col + ? detail.value_col + : detail + }); + }); + settings.details = defaultDetails; + } + return settings; } From 6a7ed835cefb9d798fd987c02d7dcc9f8167e56c Mon Sep 17 00:00:00 2001 From: jwildfire Date: Wed, 15 May 2019 16:21:53 -0700 Subject: [PATCH 2/5] rebuilds with formatting updates --- package-lock.json | 136 +++++++----------- package.json | 6 +- safetyOutlierExplorer.js | 113 +++++++++++---- src/callbacks/onInit/defineSets/visit.js | 79 +++++----- .../onLayout/addRemovedRecordsContainer.js | 24 ++-- .../onLayout/hideNormalRangeInputs.js | 32 ++--- src/callbacks/onLayout/identifyControls.js | 8 +- .../onPreprocess/calculateYPrecision.js | 4 +- .../functions/highlightSelected.js | 10 +- .../callbacks/onResize/rangePolygon.js | 18 ++- .../participantCharacteristics.js | 4 +- src/callbacks/onResize/orderPoints.js | 10 +- src/util/merge.js | 5 +- 13 files changed, 242 insertions(+), 207 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74efafd..3f0f2cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "safety-outlier-explorer", - "version": "2.5.2", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -858,8 +858,8 @@ "integrity": "sha1-Ty5dfAl13ij4hJgFjcta/KtqX3E=", "optional": true, "requires": { - "commander": "^2.19.0", - "concat-stream": "^1.6.2", + "commander": "^2.20.0", + "concat-stream": "^2.0.0", "voc": "^1.1.0" } }, @@ -870,9 +870,9 @@ "optional": true }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" }, "concat-map": { "version": "0.0.1", @@ -881,70 +881,15 @@ "dev": true }, "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "optional": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "optional": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "optional": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "optional": true - } } }, "convert-source-map": { @@ -1029,6 +974,10 @@ "ansi-regex": "^2.0.0" } }, + "inherits": { + "version": "2.0.3", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, "invariant": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", @@ -1062,7 +1011,7 @@ "adler-32": "^1.2.0", "cfb": ">=0.10.0", "codepage": "~1.3.6", - "commander": "^2.19.0", + "commander": "^2.20.0", "crc-32": "^1.2.0", "jszip": "2.4.0", "ssf": "~0.8.1" @@ -1070,13 +1019,12 @@ }, "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true }, "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, @@ -1165,14 +1113,14 @@ "dev": true }, "prettier": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.9.0.tgz", - "integrity": "sha512-NMIs+QW5ACV7qZxAAz6Y1cBlaq9D24O0yKI357QL19ZkzDkGq/e0uIeZDdm8WZpyIgiY5a6IFY2YFhdZgCSJdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz", + "integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==", "dev": true }, "printj": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "resolved": "http://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" }, "private": { @@ -1181,6 +1129,17 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -1206,7 +1165,6 @@ }, "regexpu-core": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { @@ -1217,13 +1175,11 @@ }, "regjsgen": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, "regjsparser": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -1251,9 +1207,9 @@ } }, "rollup-plugin-babel": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.2.tgz", - "integrity": "sha512-ALGPBFtwJZcYHsNPM6RGJlEncTzAARPvZOGjNPZgDe5hS5t6sJGjiOWibEFVEz5LQN7S7spvCBILaS4N1Cql2w==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.7.tgz", + "integrity": "sha512-bVe2y0z/V5Ax1qU8NX/0idmzIwJPdUGu8Xx3vXH73h0yGjxfv2gkFI82MBVg49SlsFlLTBadBHb67zy4TWM3hA==", "dev": true, "requires": { "rollup-pluginutils": "^1.5.0" @@ -1272,7 +1228,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "semver": { "version": "5.6.0", @@ -1303,6 +1260,15 @@ "voc": "^1.1.0" } }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -1326,7 +1292,6 @@ }, "trim-right": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, @@ -1336,15 +1301,20 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "optional": true }, + "util-deprecate": { + "version": "1.0.2", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true + }, "voc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/voc/-/voc-1.1.0.tgz", "integrity": "sha512-fthgd8OJLqq8vPcLjElTk6Rcl2e3v5ekcXauImaqEnQqd5yUWKg1+ZOBgS2KTWuVKcuvZMQq4TDptiT1uYddUA==" }, "webcharts": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/webcharts/-/webcharts-1.11.1.tgz", - "integrity": "sha512-sZCz2Z2tdC22+HHI58nb5uY8LxvDGXOdMnoG6pBpE/mht2ruaobMnRtOw+S2MUZnIPYybt5jeHO7h5Sggg8Siw==", + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/webcharts/-/webcharts-1.11.5.tgz", + "integrity": "sha512-dI5Jr/O/A0m92e4PWLxzZSJ6d220r6s9IQgGeQm1hfM2LUUGH0HrEHcLGmf8yseN//31aT8QMFmlJEjOsQ7uew==", "requires": { "d3": "^3", "js-xlsx": "^0.8.22" diff --git a/package.json b/package.json index 8ebd23a..e00741d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "license": "MIT", "dependencies": { "d3": "^3", - "webcharts": "^1.10" + "webcharts": "^1.11.5" }, "scripts": { "build": "npm audit fix && npm run bundle && npm run format && npm run build-wiki && npm run check-settings-schema", @@ -28,8 +28,8 @@ "babel-plugin-external-helpers": "^6.22.0", "babel-preset-env": "^1.7.0", "babel-register": "^6.26.0", - "prettier": "^1.7.4", + "prettier": "^1.17.1", "rollup": "^1.1.2", - "rollup-plugin-babel": "^3.0.2" + "rollup-plugin-babel": "^3.0.7" } } diff --git a/safetyOutlierExplorer.js b/safetyOutlierExplorer.js index 8442619..f016b2e 100644 --- a/safetyOutlierExplorer.js +++ b/safetyOutlierExplorer.js @@ -2,9 +2,9 @@ typeof exports === 'object' && typeof module !== 'undefined' ? (module.exports = factory(require('d3'), require('webcharts'))) : typeof define === 'function' && define.amd - ? define(['d3', 'webcharts'], factory) - : ((global = global || self), - (global.safetyOutlierExplorer = factory(global.d3, global.webCharts))); + ? define(['d3', 'webcharts'], factory) + : ((global = global || self), + (global.safetyOutlierExplorer = factory(global.d3, global.webCharts))); })(this, function(d3, webcharts) { 'use strict'; @@ -319,7 +319,7 @@ settings.x.column + ']'; - //Conadd custom tooltip values + //add custom tooltip values if (settings.tooltip_cols) { settings.tooltip_cols.forEach(function(tooltip) { var obj = @@ -369,6 +369,69 @@ settings.unscheduled_visit_regex = new RegExp(pattern, flags); } + //handle a string argument to filters + if (!(settings.filters instanceof Array)) + settings.filters = typeof settings.filters === 'string' ? [settings.filters] : []; + + //handle a string argument to details + if (!(settings.details instanceof Array)) + settings.details = typeof settings.details === 'string' ? [settings.details] : []; + + //Define default details. + var defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; + if (Array.isArray(settings.filters)) + settings.filters + .filter(function(filter) { + return filter.value_col !== settings.id_col; + }) + .forEach(function(filter) { + return defaultDetails.push({ + value_col: filter.value_col ? filter.value_col : filter, + label: filter.label + ? filter.label + : filter.value_col + ? filter.value_col + : filter + }); + }); + defaultDetails.push({ value_col: settings.value_col, label: 'Result' }); + if (settings.normal_col_low) + defaultDetails.push({ + value_col: settings.normal_col_low, + label: 'Lower Limit of Normal' + }); + if (settings.normal_col_high) + defaultDetails.push({ + value_col: settings.normal_col_high, + label: 'Upper Limit of Normal' + }); + + //If [settings.details] is not specified: + if (!settings.details) settings.details = defaultDetails; + else { + //If [settings.details] is specified: + //Allow user to specify an array of columns or an array of objects with a column property + //and optionally a column label. + settings.details.forEach(function(detail) { + if ( + defaultDetails + .map(function(d) { + return d.value_col; + }) + .indexOf(detail.value_col ? detail.value_col : detail) === -1 + ) + defaultDetails.push({ + value_col: detail.value_col ? detail.value_col : detail, + label: detail.label + ? detail.label + : detail.value_col + ? detail.value_col + : detail + }); + }); + settings.details = defaultDetails; + } + return settings; } @@ -730,9 +793,9 @@ return time_settings.order.indexOf(visit) < 0; }) ); - } else - //Otherwise use data-driven visit order. - time_settings.order = visitOrder; + } + //Otherwise use data-driven visit order. + else time_settings.order = visitOrder; //Define domain. time_settings.domain = time_settings.order; @@ -1069,20 +1132,20 @@ (this.removedRecords.nonNumeric > 1 ? 's' : '') + ' with a non-numeric result were removed.' : this.removedRecords.missing > 0 - ? this.removedRecords.missing + - ' record' + - (this.removedRecords.missing > 1 ? 's' : '') + - ' with a missing result ' + - (this.removedRecords.missing > 1 ? 'were' : 'was') + - ' removed.' - : this.removedRecords.nonNumeric > 0 - ? this.removedRecords.nonNumeric + - ' record' + - (this.removedRecords.nonNumeric > 1 ? 's' : '') + - ' with a non-numeric result ' + - (this.removedRecords.nonNumeric > 1 ? 'were' : 'was') + - ' removed.' - : ''; + ? this.removedRecords.missing + + ' record' + + (this.removedRecords.missing > 1 ? 's' : '') + + ' with a missing result ' + + (this.removedRecords.missing > 1 ? 'were' : 'was') + + ' removed.' + : this.removedRecords.nonNumeric > 0 + ? this.removedRecords.nonNumeric + + ' record' + + (this.removedRecords.nonNumeric > 1 ? 's' : '') + + ' with a non-numeric result ' + + (this.removedRecords.nonNumeric > 1 ? 'were' : 'was') + + ' removed.' + : ''; this.removedRecords.container = this.controls.wrap .append('div') .style({ @@ -1254,8 +1317,8 @@ this.measure.range > 0 ? Math.abs(this.measure.range / 15) // non-zero range : this.measure.results[0] !== 0 - ? Math.abs(this.measure.results[0] / 15) // zero range, non-zero result(s) - : 1; // zero range, zero result(s) + ? Math.abs(this.measure.results[0] / 15) // zero range, non-zero result(s) + : 1; // zero range, zero result(s) if (step < 1) { var x10 = 0; do { @@ -1783,7 +1846,9 @@ var value_col = detail.value_col ? detail.value_col : detail; var label = detail.label ? detail.label - : detail.value_col ? detail.value_col : detail; + : detail.value_col + ? detail.value_col + : detail; var tuple = [label, participantDatum[value_col]]; if (tuple[1] !== undefined) diff --git a/src/callbacks/onInit/defineSets/visit.js b/src/callbacks/onInit/defineSets/visit.js index 072fac8..b182900 100644 --- a/src/callbacks/onInit/defineSets/visit.js +++ b/src/callbacks/onInit/defineSets/visit.js @@ -2,47 +2,52 @@ import { set, ascending } from 'd3'; export default function visit() { //ordinal - this.config.time_cols.filter(time_col => time_col.type === 'ordinal').forEach(time_settings => { - let visits, visitOrder; + this.config.time_cols + .filter(time_col => time_col.type === 'ordinal') + .forEach(time_settings => { + let visits, visitOrder; - //Given an ordering variable sort a unique set of visits by the ordering variable. - if (time_settings.order_col && this.raw_data[0].hasOwnProperty(time_settings.order_col)) { - //Define a unique set of visits with visit order concatenated. - visits = set( - this.raw_data.map( - d => `${d[time_settings.order_col]}|${d[time_settings.value_col]}` - ) - ).values(); + //Given an ordering variable sort a unique set of visits by the ordering variable. + if ( + time_settings.order_col && + this.raw_data[0].hasOwnProperty(time_settings.order_col) + ) { + //Define a unique set of visits with visit order concatenated. + visits = set( + this.raw_data.map( + d => `${d[time_settings.order_col]}|${d[time_settings.value_col]}` + ) + ).values(); - //Sort visits. - visitOrder = visits - .sort((a, b) => { - const aOrder = a.split('|')[0], - bOrder = b.split('|')[0], - diff = +aOrder - +bOrder; - return diff ? diff : ascending(a, b); - }) - .map(visit => visit.split('|')[1]); - } else { - //Otherwise sort a unique set of visits alphanumerically. - //Define a unique set of visits. - visits = set(this.raw_data.map(d => d[time_settings.value_col])).values(); + //Sort visits. + visitOrder = visits + .sort((a, b) => { + const aOrder = a.split('|')[0], + bOrder = b.split('|')[0], + diff = +aOrder - +bOrder; + return diff ? diff : ascending(a, b); + }) + .map(visit => visit.split('|')[1]); + } else { + //Otherwise sort a unique set of visits alphanumerically. + //Define a unique set of visits. + visits = set(this.raw_data.map(d => d[time_settings.value_col])).values(); - //Sort visits; - visitOrder = visits.sort(); - } + //Sort visits; + visitOrder = visits.sort(); + } - //Set x-axis domain. - if (time_settings.order) { - //If a visit order is specified, use it and concatenate any unspecified visits at the end. - time_settings.order = time_settings.order.concat( - visitOrder.filter(visit => time_settings.order.indexOf(visit) < 0) - ); - } else + //Set x-axis domain. + if (time_settings.order) { + //If a visit order is specified, use it and concatenate any unspecified visits at the end. + time_settings.order = time_settings.order.concat( + visitOrder.filter(visit => time_settings.order.indexOf(visit) < 0) + ); + } //Otherwise use data-driven visit order. - time_settings.order = visitOrder; + else time_settings.order = visitOrder; - //Define domain. - time_settings.domain = time_settings.order; - }); + //Define domain. + time_settings.domain = time_settings.order; + }); } diff --git a/src/callbacks/onLayout/addRemovedRecordsContainer.js b/src/callbacks/onLayout/addRemovedRecordsContainer.js index bbb85e5..a3c693c 100644 --- a/src/callbacks/onLayout/addRemovedRecordsContainer.js +++ b/src/callbacks/onLayout/addRemovedRecordsContainer.js @@ -8,18 +8,18 @@ export default function addRemovedRecordsNote() { this.removedRecords.nonNumeric > 1 ? 's' : '' } with a non-numeric result were removed.` : this.removedRecords.missing > 0 - ? `${this.removedRecords.missing} record${ - this.removedRecords.missing > 1 ? 's' : '' - } with a missing result ${ - this.removedRecords.missing > 1 ? 'were' : 'was' - } removed.` - : this.removedRecords.nonNumeric > 0 - ? `${this.removedRecords.nonNumeric} record${ - this.removedRecords.nonNumeric > 1 ? 's' : '' - } with a non-numeric result ${ - this.removedRecords.nonNumeric > 1 ? 'were' : 'was' - } removed.` - : ''; + ? `${this.removedRecords.missing} record${ + this.removedRecords.missing > 1 ? 's' : '' + } with a missing result ${ + this.removedRecords.missing > 1 ? 'were' : 'was' + } removed.` + : this.removedRecords.nonNumeric > 0 + ? `${this.removedRecords.nonNumeric} record${ + this.removedRecords.nonNumeric > 1 ? 's' : '' + } with a non-numeric result ${ + this.removedRecords.nonNumeric > 1 ? 'were' : 'was' + } removed.` + : ''; this.removedRecords.container = this.controls.wrap .append('div') .style({ diff --git a/src/callbacks/onLayout/hideNormalRangeInputs.js b/src/callbacks/onLayout/hideNormalRangeInputs.js index bd9ab2b..b104195 100644 --- a/src/callbacks/onLayout/hideNormalRangeInputs.js +++ b/src/callbacks/onLayout/hideNormalRangeInputs.js @@ -17,16 +17,13 @@ export default function hideNormalRangeInputs() { 'normal_range_quantile_high' ].indexOf(d.option) > -1 ) - .style( - 'display', - d => - (this.config.normal_range_method !== 'Standard Deviation' && - d.option === 'normal_range_sd') || - (this.config.normal_range_method !== 'Quantiles' && - ['normal_range_quantile_low', 'normal_range_quantile_high'].indexOf(d.option) > - -1) - ? 'none' - : 'inline-table' + .style('display', d => + (this.config.normal_range_method !== 'Standard Deviation' && + d.option === 'normal_range_sd') || + (this.config.normal_range_method !== 'Quantiles' && + ['normal_range_quantile_low', 'normal_range_quantile_high'].indexOf(d.option) > -1) + ? 'none' + : 'inline-table' ); //Set significant digits to .01. @@ -37,15 +34,12 @@ export default function hideNormalRangeInputs() { .select('option:checked') .text(); - normalRangeInputs.style( - 'display', - d => - (normal_range_method !== 'Standard Deviation' && d.option === 'normal_range_sd') || - (normal_range_method !== 'Quantiles' && - ['normal_range_quantile_low', 'normal_range_quantile_high'].indexOf(d.option) > - -1) - ? 'none' - : 'inline-table' + normalRangeInputs.style('display', d => + (normal_range_method !== 'Standard Deviation' && d.option === 'normal_range_sd') || + (normal_range_method !== 'Quantiles' && + ['normal_range_quantile_low', 'normal_range_quantile_high'].indexOf(d.option) > -1) + ? 'none' + : 'inline-table' ); }); } diff --git a/src/callbacks/onLayout/identifyControls.js b/src/callbacks/onLayout/identifyControls.js index 4e16f03..c5f1168 100644 --- a/src/callbacks/onLayout/identifyControls.js +++ b/src/callbacks/onLayout/identifyControls.js @@ -6,9 +6,11 @@ export default function identifyControls() { .selectAll('.control-group'); //Give each control a unique ID. - controlGroups.attr('id', d => d.label.toLowerCase().replace(' ', '-')).each(function(d) { - select(this).classed(d.type, true); - }); + controlGroups + .attr('id', d => d.label.toLowerCase().replace(' ', '-')) + .each(function(d) { + select(this).classed(d.type, true); + }); //Give y-axis controls a common class name. controlGroups diff --git a/src/callbacks/onPreprocess/calculateYPrecision.js b/src/callbacks/onPreprocess/calculateYPrecision.js index 752f871..ec59842 100644 --- a/src/callbacks/onPreprocess/calculateYPrecision.js +++ b/src/callbacks/onPreprocess/calculateYPrecision.js @@ -12,8 +12,8 @@ export default function calculateYPrecision() { this.measure.range > 0 ? Math.abs(this.measure.range / 15) // non-zero range : this.measure.results[0] !== 0 - ? Math.abs(this.measure.results[0] / 15) // zero range, non-zero result(s) - : 1; // zero range, zero result(s) + ? Math.abs(this.measure.results[0] / 15) // zero range, non-zero result(s) + : 1; // zero range, zero result(s) if (step < 1) { let x10 = 0; do { diff --git a/src/callbacks/onResize/addEventListeners/functions/highlightSelected.js b/src/callbacks/onResize/addEventListeners/functions/highlightSelected.js index 6915a3d..744d86f 100644 --- a/src/callbacks/onResize/addEventListeners/functions/highlightSelected.js +++ b/src/callbacks/onResize/addEventListeners/functions/highlightSelected.js @@ -1,12 +1,10 @@ export default function highlightSelected() { //Add _selected_ class to participant's marks. this.marks.forEach(mark => { - mark.groups.classed( - 'selected', - d => - mark.type === 'line' - ? d.values[0].values.raw[0][this.config.id_col] === this.selected_id - : d.values.raw[0][this.config.id_col] === this.selected_id + mark.groups.classed('selected', d => + mark.type === 'line' + ? d.values[0].values.raw[0][this.config.id_col] === this.selected_id + : d.values.raw[0][this.config.id_col] === this.selected_id ); }); diff --git a/src/callbacks/onResize/addEventListeners/functions/smallMultiples/callbacks/onResize/rangePolygon.js b/src/callbacks/onResize/addEventListeners/functions/smallMultiples/callbacks/onResize/rangePolygon.js index 2aa7713..df5a01e 100644 --- a/src/callbacks/onResize/addEventListeners/functions/smallMultiples/callbacks/onResize/rangePolygon.js +++ b/src/callbacks/onResize/addEventListeners/functions/smallMultiples/callbacks/onResize/rangePolygon.js @@ -4,17 +4,15 @@ export default function rangePolygon() { var area = svg .area() .x(d => this.x(d['TIME']) + (this.config.x.type === 'ordinal' ? this.x.rangeBand() / 2 : 0)) - .y0( - d => - /^-?[0-9.]+$/.test(d[this.config.normal_col_low]) - ? this.y(d[this.config.normal_col_low]) - : 0 + .y0(d => + /^-?[0-9.]+$/.test(d[this.config.normal_col_low]) + ? this.y(d[this.config.normal_col_low]) + : 0 ) - .y1( - d => - /^-?[0-9.]+$/.test(d[this.config.normal_col_high]) - ? this.y(d[this.config.normal_col_high]) - : 0 + .y1(d => + /^-?[0-9.]+$/.test(d[this.config.normal_col_high]) + ? this.y(d[this.config.normal_col_high]) + : 0 ); var dRow = this.filtered_data[0]; diff --git a/src/callbacks/onResize/addEventListeners/functions/smallMultiples/participantCharacteristics.js b/src/callbacks/onResize/addEventListeners/functions/smallMultiples/participantCharacteristics.js index 15ffef2..1ea6845 100644 --- a/src/callbacks/onResize/addEventListeners/functions/smallMultiples/participantCharacteristics.js +++ b/src/callbacks/onResize/addEventListeners/functions/smallMultiples/participantCharacteristics.js @@ -18,7 +18,9 @@ export default function participantCharacteristics() { const value_col = detail.value_col ? detail.value_col : detail; const label = detail.label ? detail.label - : detail.value_col ? detail.value_col : detail; + : detail.value_col + ? detail.value_col + : detail; const tuple = [label, participantDatum[value_col]]; if (tuple[1] !== undefined) diff --git a/src/callbacks/onResize/orderPoints.js b/src/callbacks/onResize/orderPoints.js index 3206321..1e67d2d 100644 --- a/src/callbacks/onResize/orderPoints.js +++ b/src/callbacks/onResize/orderPoints.js @@ -1,7 +1,9 @@ export default function orderPoints() { - this.marks.filter(mark => mark.type === 'circle').forEach(mark => { - mark.groups.each((d, i) => { - d.order = this.IDOrder.find(di => d.key.indexOf(di.ID) === 0).order; + this.marks + .filter(mark => mark.type === 'circle') + .forEach(mark => { + mark.groups.each((d, i) => { + d.order = this.IDOrder.find(di => d.key.indexOf(di.ID) === 0).order; + }); }); - }); } diff --git a/src/util/merge.js b/src/util/merge.js index 41e3a52..759db9b 100644 --- a/src/util/merge.js +++ b/src/util/merge.js @@ -29,9 +29,8 @@ function assignKey(to, from, key) { if (!hasOwnProperty.call(to, key) || !isObj(val)) to[key] = val; else if (val instanceof Array) to[key] = from[key]; - else - // figure out how to merge arrays without converting them into objects - to[key] = assign(Object(to[key]), from[key]); + // figure out how to merge arrays without converting them into objects + else to[key] = assign(Object(to[key]), from[key]); } function assign(to, from) { From 03a5d4504b640876ae9affc75656c26577f6333b Mon Sep 17 00:00:00 2001 From: jwildfire Date: Wed, 15 May 2019 16:30:21 -0700 Subject: [PATCH 3/5] generalizes string to array logic. #131. --- safetyOutlierExplorer.js | 15 +++++++-------- src/configuration/syncSettings.js | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/safetyOutlierExplorer.js b/safetyOutlierExplorer.js index f016b2e..214c388 100644 --- a/safetyOutlierExplorer.js +++ b/safetyOutlierExplorer.js @@ -276,6 +276,13 @@ function syncSettings(settings) { var time_col = settings.time_cols[0]; + //handle a string arguments to array settings + var array_settings = ['filters', 'details', 'tooltip_cols']; + array_settings.forEach(function(s) { + if (!(settings[s] instanceof Array)) + settings[s] = typeof settings[s] === 'string' ? [settings[s]] : []; + }); + //x-axis settings.x.column = time_col.value_col; settings.x.type = time_col.type; @@ -369,14 +376,6 @@ settings.unscheduled_visit_regex = new RegExp(pattern, flags); } - //handle a string argument to filters - if (!(settings.filters instanceof Array)) - settings.filters = typeof settings.filters === 'string' ? [settings.filters] : []; - - //handle a string argument to details - if (!(settings.details instanceof Array)) - settings.details = typeof settings.details === 'string' ? [settings.details] : []; - //Define default details. var defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; if (Array.isArray(settings.filters)) diff --git a/src/configuration/syncSettings.js b/src/configuration/syncSettings.js index 53bda3b..1f6c166 100644 --- a/src/configuration/syncSettings.js +++ b/src/configuration/syncSettings.js @@ -1,6 +1,13 @@ export default function syncSettings(settings) { const time_col = settings.time_cols[0]; + //handle a string arguments to array settings + const array_settings = ['filters', 'details', 'tooltip_cols']; + array_settings.forEach(function(s) { + if (!(settings[s] instanceof Array)) + settings[s] = typeof settings[s] === 'string' ? [settings[s]] : []; + }); + //x-axis settings.x.column = time_col.value_col; settings.x.type = time_col.type; @@ -73,14 +80,6 @@ export default function syncSettings(settings) { settings.unscheduled_visit_regex = new RegExp(pattern, flags); } - //handle a string argument to filters - if (!(settings.filters instanceof Array)) - settings.filters = typeof settings.filters === 'string' ? [settings.filters] : []; - - //handle a string argument to details - if (!(settings.details instanceof Array)) - settings.details = typeof settings.details === 'string' ? [settings.details] : []; - //Define default details. let defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; if (Array.isArray(settings.filters)) From 78e91a2d648427ac782e9af470a7054862641fe0 Mon Sep 17 00:00:00 2001 From: jwildfire Date: Wed, 15 May 2019 16:34:18 -0700 Subject: [PATCH 4/5] update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e00741d..0cc3f60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "safety-outlier-explorer", - "version": "2.5.3", + "version": "2.5.4", "description": "Chart showing participant trajectories of lab measures, vital signs and other related measures in clinical trials.", "module": "./src/index.js", "main": "./safetyOutlierExplorer.js", From 14f36ea65ac38c6f533e55f1a8baafa5448fe0e7 Mon Sep 17 00:00:00 2001 From: jwildfire Date: Mon, 20 May 2019 09:22:59 -0700 Subject: [PATCH 5/5] remove details sync logic --- package-lock.json | 2 +- safetyOutlierExplorer.js | 55 ------------------------------- src/configuration/syncSettings.js | 48 --------------------------- 3 files changed, 1 insertion(+), 104 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f0f2cb..0095b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "safety-outlier-explorer", - "version": "2.5.3", + "version": "2.5.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/safetyOutlierExplorer.js b/safetyOutlierExplorer.js index 214c388..45120c8 100644 --- a/safetyOutlierExplorer.js +++ b/safetyOutlierExplorer.js @@ -376,61 +376,6 @@ settings.unscheduled_visit_regex = new RegExp(pattern, flags); } - //Define default details. - var defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; - if (Array.isArray(settings.filters)) - settings.filters - .filter(function(filter) { - return filter.value_col !== settings.id_col; - }) - .forEach(function(filter) { - return defaultDetails.push({ - value_col: filter.value_col ? filter.value_col : filter, - label: filter.label - ? filter.label - : filter.value_col - ? filter.value_col - : filter - }); - }); - defaultDetails.push({ value_col: settings.value_col, label: 'Result' }); - if (settings.normal_col_low) - defaultDetails.push({ - value_col: settings.normal_col_low, - label: 'Lower Limit of Normal' - }); - if (settings.normal_col_high) - defaultDetails.push({ - value_col: settings.normal_col_high, - label: 'Upper Limit of Normal' - }); - - //If [settings.details] is not specified: - if (!settings.details) settings.details = defaultDetails; - else { - //If [settings.details] is specified: - //Allow user to specify an array of columns or an array of objects with a column property - //and optionally a column label. - settings.details.forEach(function(detail) { - if ( - defaultDetails - .map(function(d) { - return d.value_col; - }) - .indexOf(detail.value_col ? detail.value_col : detail) === -1 - ) - defaultDetails.push({ - value_col: detail.value_col ? detail.value_col : detail, - label: detail.label - ? detail.label - : detail.value_col - ? detail.value_col - : detail - }); - }); - settings.details = defaultDetails; - } - return settings; } diff --git a/src/configuration/syncSettings.js b/src/configuration/syncSettings.js index 1f6c166..c0c6cfb 100644 --- a/src/configuration/syncSettings.js +++ b/src/configuration/syncSettings.js @@ -80,53 +80,5 @@ export default function syncSettings(settings) { settings.unscheduled_visit_regex = new RegExp(pattern, flags); } - //Define default details. - let defaultDetails = [{ value_col: settings.id_col, label: 'Participant ID' }]; - if (Array.isArray(settings.filters)) - settings.filters - .filter(filter => filter.value_col !== settings.id_col) - .forEach(filter => - defaultDetails.push({ - value_col: filter.value_col ? filter.value_col : filter, - label: filter.label - ? filter.label - : filter.value_col - ? filter.value_col - : filter - }) - ); - defaultDetails.push({ value_col: settings.value_col, label: 'Result' }); - if (settings.normal_col_low) - defaultDetails.push({ value_col: settings.normal_col_low, label: 'Lower Limit of Normal' }); - if (settings.normal_col_high) - defaultDetails.push({ - value_col: settings.normal_col_high, - label: 'Upper Limit of Normal' - }); - - //If [settings.details] is not specified: - if (!settings.details) settings.details = defaultDetails; - else { - //If [settings.details] is specified: - //Allow user to specify an array of columns or an array of objects with a column property - //and optionally a column label. - settings.details.forEach(detail => { - if ( - defaultDetails - .map(d => d.value_col) - .indexOf(detail.value_col ? detail.value_col : detail) === -1 - ) - defaultDetails.push({ - value_col: detail.value_col ? detail.value_col : detail, - label: detail.label - ? detail.label - : detail.value_col - ? detail.value_col - : detail - }); - }); - settings.details = defaultDetails; - } - return settings; }