From 72ea7b80fd9dc3c8ac2785d804c2903019db6d7a Mon Sep 17 00:00:00 2001 From: Pegah Sarram Date: Fri, 24 May 2019 09:18:46 -0700 Subject: [PATCH] [Summary Widget] support enum fields (#2406) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Display a drop down menu if the selected key is of type enum. * Create normalized dataum when persisting telemerty datum using metadatum source as key.: * * Clear config values before creating new inputs. * Emit ‘change' event with the value of the first option after creating the select element. * If a value is a number, pass it as a number when emitting ‘change’. Similarly, if the cashed telemetry value is a number, convert it to number before applying the operation and validation. * Update description. * Update description in operations.js also. --- src/plugins/summaryWidget/src/Condition.js | 53 +++++++++++++------ .../summaryWidget/src/ConditionEvaluator.js | 41 +++++++++++--- .../summaryWidget/src/ConditionManager.js | 16 ++++-- .../src/input/OperationSelect.js | 8 +-- .../summaryWidget/src/telemetry/operations.js | 26 ++++++++- 5 files changed, 114 insertions(+), 30 deletions(-) diff --git a/src/plugins/summaryWidget/src/Condition.js b/src/plugins/summaryWidget/src/Condition.js index f46a410897..08d47f54a6 100644 --- a/src/plugins/summaryWidget/src/Condition.js +++ b/src/plugins/summaryWidget/src/Condition.js @@ -70,16 +70,14 @@ define([ */ function onValueInput(event) { var elem = event.target, - value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber), + value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value), inputIndex = self.valueInputs.indexOf(elem); - if (elem.tagName.toUpperCase() === 'INPUT') { - self.eventEmitter.emit('change', { - value: value, - property: 'values[' + inputIndex + ']', - index: self.index - }); - } + self.eventEmitter.emit('change', { + value: value, + property: 'values[' + inputIndex + ']', + index: self.index + }); } this.listenTo(this.deleteButton, 'click', this.remove, this); @@ -108,8 +106,7 @@ define([ Object.values(this.selects).forEach(function (select) { $('.t-configuration', self.domElement).append(select.getDOM()); }); - - this.listenTo($(this.domElement), 'input', onValueInput); + this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput); } Condition.prototype.getDOM = function (container) { @@ -167,7 +164,9 @@ define([ /** * When an operation is selected, create the appropriate value inputs - * and add them to the view + * and add them to the view. If an operation is of type enum, create + * a drop-down menu instead. + * * @param {string} operation The key of currently selected operation */ Condition.prototype.generateValueInputs = function (operation) { @@ -176,25 +175,49 @@ define([ inputCount, inputType, newInput, - index = 0; + index = 0, + emitChange = false; inputArea.html(''); this.valueInputs = []; + this.config.values = []; if (evaluator.getInputCount(operation)) { inputCount = evaluator.getInputCount(operation); inputType = evaluator.getInputType(operation); + while (index < inputCount) { - if (!this.config.values[index]) { - this.config.values[index] = (inputType === 'number' ? 0 : ''); + if (inputType === 'select') { + newInput = $(''); + emitChange = true; + } else { + this.config.values[index] = inputType === 'number' ? 0 : ''; + newInput = $(' '); } - newInput = $(' '); + this.valueInputs.push(newInput.get(0)); inputArea.append(newInput); index += 1; } + + if (emitChange) { + this.eventEmitter.emit('change', { + value: Number(newInput[0].options[0].value), + property: 'values[0]', + index: this.index + }); + } } }; + Condition.prototype.generateSelectOptions = function () { + let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object); + let options = ''; + telemetryMetadata[this.config.key].enumerations.forEach(enumeration => { + options += ''; + }); + return options; + }; + return Condition; }); diff --git a/src/plugins/summaryWidget/src/ConditionEvaluator.js b/src/plugins/summaryWidget/src/ConditionEvaluator.js index a38a66180c..6a43e7e1aa 100644 --- a/src/plugins/summaryWidget/src/ConditionEvaluator.js +++ b/src/plugins/summaryWidget/src/ConditionEvaluator.js @@ -24,7 +24,8 @@ define([], function () { */ this.inputTypes = { number: 'number', - string: 'text' + string: 'text', + enum: 'select' }; /** @@ -34,7 +35,8 @@ define([], function () { */ this.inputValidators = { number: this.validateNumberInput, - string: this.validateStringInput + string: this.validateStringInput, + enum: this.validateNumberInput }; /** @@ -201,7 +203,7 @@ define([], function () { return typeof input[0] === 'undefined'; }, text: 'is undefined', - appliesTo: ['string', 'number'], + appliesTo: ['string', 'number', 'enum'], inputCount: 0, getDescription: function () { return ' is undefined'; @@ -212,11 +214,33 @@ define([], function () { return typeof input[0] !== 'undefined'; }, text: 'is defined', - appliesTo: ['string', 'number'], + appliesTo: ['string', 'number', 'enum'], inputCount: 0, getDescription: function () { return ' is defined'; } + }, + enumValueIs: { + operation: function (input) { + return input[0] === input[1]; + }, + text: 'is', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; + } + }, + enumValueIsNot: { + operation: function (input) { + return input[0] !== input[1]; + }, + text: 'is not', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } } }; } @@ -310,13 +334,16 @@ define([], function () { validator; if (cache[object] && typeof cache[object][key] !== 'undefined') { - telemetryValue = [cache[object][key]]; + let value = cache[object][key]; + telemetryValue = [isNaN(Number(value)) ? value : Number(value)]; } + op = this.operations[operation] && this.operations[operation].operation; input = telemetryValue && telemetryValue.concat(values); validator = op && this.inputValidators[this.operations[operation].appliesTo[0]]; + if (op && input && validator) { - if (this.operations[operation].appliesTo.length === 2) { + if (this.operations[operation].appliesTo.length > 1) { return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input); } else { return validator(input) && op(input); @@ -372,7 +399,7 @@ define([], function () { }; /** - * Returns true only of the given operation applies to a given type + * Returns true only if the given operation applies to a given type * @param {string} key The key of the operation * @param {string} type The value type to query * @returns {boolean} True if the condition applies, false otherwise diff --git a/src/plugins/summaryWidget/src/ConditionManager.js b/src/plugins/summaryWidget/src/ConditionManager.js index e9c18dce98..b5ddf98eaf 100644 --- a/src/plugins/summaryWidget/src/ConditionManager.js +++ b/src/plugins/summaryWidget/src/ConditionManager.js @@ -130,7 +130,9 @@ define ([ this.telemetryTypesById[objectId] = {}; Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) { var type; - if (valueMetadata.hints.hasOwnProperty('range')) { + if (valueMetadata.enumerations !== undefined) { + type = 'enum'; + } else if (valueMetadata.hints.hasOwnProperty('range')) { type = 'number'; } else if (valueMetadata.hints.hasOwnProperty('domain')) { type = 'number'; @@ -163,11 +165,18 @@ define ([ * @param {datum} datum The new data from the telemetry source * @private */ - ConditionManager.prototype.handleSubscriptionCallback = function (objId, datum) { - this.subscriptionCache[objId] = datum; + ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) { + this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum); this.eventEmitter.emit('receiveTelemetry'); }; + ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) { + return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => { + normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source]; + return normalizedDatum; + }, {}); + }; + /** * Event handler for an add event in this Summary Widget's composition. * Sets up subscription handlers and parses its property types. @@ -236,6 +245,7 @@ define ([ id.namespace === identifier.namespace; }); delete this.compositionObjs[objectId]; + delete this.subscriptionCache[objectId]; this.subscriptions[objectId](); //unsubscribe from telemetry source delete this.subscriptions[objectId]; this.eventEmitter.emit('remove', identifier); diff --git a/src/plugins/summaryWidget/src/input/OperationSelect.js b/src/plugins/summaryWidget/src/input/OperationSelect.js index 8ee6b65f56..4493d4c2f2 100644 --- a/src/plugins/summaryWidget/src/input/OperationSelect.js +++ b/src/plugins/summaryWidget/src/input/OperationSelect.js @@ -110,9 +110,11 @@ define([ type = self.manager.getTelemetryPropertyType(self.config.object, key); - self.operationKeys = operations.filter(function (operation) { - return self.evaluator.operationAppliesTo(operation, type); - }); + if (type !== undefined) { + self.operationKeys = operations.filter(function (operation) { + return self.evaluator.operationAppliesTo(operation, type); + }); + } }; OperationSelect.prototype.destroy = function () { diff --git a/src/plugins/summaryWidget/src/telemetry/operations.js b/src/plugins/summaryWidget/src/telemetry/operations.js index 7202d5f52c..67629c8a57 100644 --- a/src/plugins/summaryWidget/src/telemetry/operations.js +++ b/src/plugins/summaryWidget/src/telemetry/operations.js @@ -174,7 +174,7 @@ define([ return typeof input[0] === 'undefined'; }, text: 'is undefined', - appliesTo: ['string', 'number'], + appliesTo: ['string', 'number', 'enum'], inputCount: 0, getDescription: function () { return ' is undefined'; @@ -185,11 +185,33 @@ define([ return typeof input[0] !== 'undefined'; }, text: 'is defined', - appliesTo: ['string', 'number'], + appliesTo: ['string', 'number', 'enum'], inputCount: 0, getDescription: function () { return ' is defined'; } + }, + enumValueIs: { + operation: function (input) { + return input[0] === input[1]; + }, + text: 'is', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' == ' + values[0]; + } + }, + enumValueIsNot: { + operation: function (input) { + return input[0] !== input[1]; + }, + text: 'is not', + appliesTo: ['enum'], + inputCount: 1, + getDescription: function (values) { + return ' != ' + values[0]; + } } };