From ad9e29e0265c282757e52c3831025ce7bfb72045 Mon Sep 17 00:00:00 2001 From: juanf <juanf> Date: Fri, 25 Apr 2014 11:02:48 +0000 Subject: [PATCH] SSDM-110: Annotations Support SVN: 31416 --- .../1/as/webapps/newbrowser/html/index.html | 1 + .../newbrowser/html/js/config/Profile.js | 52 +++++++- .../html/js/controllers/MainController.js | 1 + .../newbrowser/html/js/util/FormUtil.js | 122 ++++++++++++++++++ .../newbrowser/html/js/views/SampleForm.js | 9 +- .../html/js/widgets/SampleLinksWidget.js | 106 ++++++++++++--- 6 files changed, 266 insertions(+), 25 deletions(-) create mode 100644 plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/util/FormUtil.js diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/index.html b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/index.html index b6cffaa1c3c..b0746b74b1d 100644 --- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/index.html +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/index.html @@ -67,6 +67,7 @@ <script type="text/javascript" src="./js/server/ServerFacade.js"></script> <script type="text/javascript" src="./js/util/Util.js"></script> + <script type="text/javascript" src="./js/util/FormUtil.js"></script> <script type="text/javascript" src="./js/util/BlockScrollUtil.js"></script> <script type="text/javascript" src="./js/views/DataSetForm.js"></script> diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/config/Profile.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/config/Profile.js index 327dd4fda74..066c4d3b0e6 100644 --- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/config/Profile.js +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/config/Profile.js @@ -46,6 +46,7 @@ $.extend(DefaultProfile.prototype, { this.allTypes = []; this.allVocabularies = []; this.allDataStores = []; + this.allPropertyTypes = []; this.displaySettings = {}; this.typeGroups = { @@ -57,6 +58,7 @@ $.extend(DefaultProfile.prototype, { }; this.typePropertiesForTable = {}; + this.typePropertiesForSmallTable = {}; this.colorForInspectors = {}; @@ -165,6 +167,14 @@ $.extend(DefaultProfile.prototype, { return $.inArray(sampleTypeCode, this.ELNExperiments) !== -1; } + this.getPropertyType = function(propertyTypeCode) { + for (var i = 0; i < this.allPropertyTypes.length; i++) { + if(this.allPropertyTypes[i].code === propertyTypeCode) { + return this.allPropertyTypes[i]; + } + } + return null; + } this.getVocabularyById = function(id) { for (var i = 0; i < this.allVocabularies.length; i++) { var vocabulary = this.allVocabularies[i]; @@ -263,6 +273,13 @@ $.extend(DefaultProfile.prototype, { return sampleTypes; } + this.initPropertyTypes = function() { + var _this = this; + this.serverFacade.listPropertyTypes(function(data) { + _this.allPropertyTypes = data.result; + }); + } + this.initVocabulariesForSampleTypes = function() { //Build Vocabularies from sample types for(var sampleTypeIdx = 0; sampleTypeIdx < this.allTypes.length; sampleTypeIdx++) { @@ -352,6 +369,7 @@ $.extend(DefaultProfile.prototype, { } } + this.initPropertyTypes(); this.initVocabulariesForSampleTypes(); this.initMenuStructure(); } @@ -1137,7 +1155,27 @@ $.extend(BodenmillerLabProfile.prototype, DefaultProfile.prototype, { this.ELNExperiments = ["SYSTEM_EXPERIMENT"]; this.notShowTypes = ["ANTIBODY_PANEL", "SYSTEM_EXPERIMENT"]; this.isShowUnavailablePreviewOnSampleTable = false; - + +// For testing +// this.sampleTypeDefinitionsExtension = { +// "SYSTEM_EXPERIMENT" : { +// "SAMPLE_PARENTS_HINT" : [ +// { +// "LABEL" : "Protein", +// "TYPE": "PROTEIN", +// "MIN_COUNT" : 1, +// "ANNOTATION_PROPERTIES" : [ {"TYPE" : "TEST_NUMBER", "MANDATORY" : true } +// ,{"TYPE" : "CONTRIBUTOR", "MANDATORY" : false }] +// } +// ] +// } +// } +// +// this.typePropertiesForSmallTable = { +// "SYSTEM_EXPERIMENT" : ["NAME"], +// "PROTEIN" : ["PROTEIN_NAME"] +// } +// this.typeGroups = { "ANTIBODIES" : { "TYPE" : "ANTIBODIES", @@ -1344,25 +1382,29 @@ $.extend(TestProfile.prototype, DefaultProfile.prototype, { { "LABEL" : "Protocol", "TYPE": "PROTOCOL", - "MIN_COUNT" : 1 + "MIN_COUNT" : 1, + "ANNOTATIONS" : [] } , { "LABEL" : "Plasmid", "TYPE": "PLASMIDS", - "MIN_COUNT" : 1 + "MIN_COUNT" : 1, + "ANNOTATIONS" : [] } , { "LABEL" : "Inhibitor", "TYPE": "INHIBITORS", - "MIN_COUNT" : 1 + "MIN_COUNT" : 1, + "ANNOTATIONS" : [] } , { "LABEL" : "Cell Line", "TYPE": "CELL_LINE", - "MIN_COUNT" : 1 + "MIN_COUNT" : 1, + "ANNOTATIONS" : [] } ], } diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/controllers/MainController.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/controllers/MainController.js index 9f6445f710f..2beba36be32 100644 --- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/controllers/MainController.js +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/controllers/MainController.js @@ -41,6 +41,7 @@ function MainController(profile) { // Configuration this.profile = profile; this.profile.serverFacade = this.serverFacade; + FormUtil.profile = this.profile; // Atributes - Widgets typically hold both the model and the view, they are here so they can be accessed by inline HTML/Javascript when needed. diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/util/FormUtil.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/util/FormUtil.js new file mode 100644 index 00000000000..52f46c035cb --- /dev/null +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/util/FormUtil.js @@ -0,0 +1,122 @@ +var FormUtil = new function() { + this.profile = null; + + // + // Standard Form Fields + // + + this.getFieldForPropertyTypeWithLabel = function(propertyType) { + var $fieldset = $('<fieldset>'); + + var $controlGroup = $('<div>', {class : 'control-group'}); + var $controlLabel = $('<label>', {class : 'control-label'}).text(propertyType.label + ":"); + var $controls = $('<div>', {class : 'controls'}); + + $controlGroup.append($controlLabel); + $controlGroup.append($controls); + $fieldset.append($controlGroup); + + var $component = this.getFieldForPropertyType(propertyType); + + $controls.append($component); + if(propertyType.mandatory) { + $controls.append(' (Required)') + } + + return $fieldset; + } + + this.getFieldForPropertyType = function(propertyType) { + var $component = null; + if (propertyType.dataType === "BOOLEAN") { + $component = this._getBooleanField(propertyType.code, propertyType.description); + } else if (propertyType.dataType === "CONTROLLEDVOCABULARY") { + var vocabulary = null; + if(isNaN(propertyType.vocabulary)) { + vocabulary = this.profile.getVocabularyById(propertyType.vocabulary.id); + if(vocabulary === null) { //This should not happen, but can save the day. + vocabulary = propertyType.vocabulary; + vocabulary.terms = propertyType.terms; + } + } else { + vocabulary = this.profile.getVocabularyById(propertyType.vocabulary); + } + $component = this._getDropDownFieldForVocabulary(propertyType.code, vocabulary.terms, propertyType.description, propertyType.mandatory); + } else if (propertyType.dataType === "HYPERLINK") { + $component = this._getInputField("url", propertyType.code, propertyType.description, null, propertyType.mandatory); + } else if (propertyType.dataType === "INTEGER") { + $component = this._getInputField("number", propertyType.code, propertyType.description, '1', propertyType.mandatory); + } else if (propertyType.dataType === "MATERIAL") { + $component = this._getInputField("text", propertyType.code, propertyType.description, null, propertyType.mandatory); + } else if (propertyType.dataType === "MULTILINE_VARCHAR") { + $component = this._getTextBox(propertyType.code, propertyType.description, propertyType.mandatory); + } else if (propertyType.dataType === "REAL") { + $component = this._getInputField("number", propertyType.code, propertyType.description, 'any', propertyType.mandatory); + } else if (propertyType.dataType === "TIMESTAMP") { + $component = this._getDatePickerField(propertyType.code, propertyType.description, propertyType.mandatory); + } else if (propertyType.dataType === "VARCHAR") { + $component = this._getInputField("text", propertyType.code, propertyType.description, null, propertyType.mandatory); + } else if (propertyType.dataType === "XML") { + $component = this._getTextBox(propertyType.code, propertyType.description, propertyType.mandatory); + } + + return $component; + } + + this._getBooleanField = function(id, alt) { + return $('<input>', {'type' : 'checkbox', 'id' : id, 'alt' : alt, 'placeholder' : alt }); + } + + this._getDropDownFieldForVocabulary = function(code, terms, alt, isRequired) { + var $component = $("<select>", {'placeholder' : alt}); + $component.attr('id', code); + + if (isRequired) { + $component.attr('required', ''); + } + + $component.append($("<option>").attr('value', '').attr('selected', '').text('')); + for(var i = 0; i < terms.length; i++) { + $component.append($("<option>").attr('value',terms[i].code).text(terms[i].label)); + } + + return $component; + } + + this._getInputField = function(type, id, alt, step, isRequired) { + var $component = $('<input>', {'type' : type, 'id' : id, 'alt' : alt, 'placeholder' : alt }); + if (isRequired) { + $component.attr('required', ''); + } + if (isRequired) { + $component.attr('step', step); + } + return $component; + } + + this._getTextBox = function(id, alt, isRequired) { + var $component = $('<textarea>', {'id' : id, 'alt' : alt, 'style' : 'height: 80px; width: 450px;', 'placeholder' : alt }); + if (isRequired) { + $component.attr('required', ''); + } + return $component; + } + + this._getDatePickerField = function(id, alt, isRequired) { + var $component = $('<div>', {'class' : 'well', 'style' : 'width: 250px;', 'placeholder' : alt }); + var $subComponent = $('<div>', {'class' : 'input-append date', 'id' : 'datetimepicker_' + id }); + var $input = $('<input>', {'type' : 'text', 'id' : id, 'data-format' : 'yyyy-MM-dd HH:mm:ss'}); + if (isRequired) { + $input.attr('required', ''); + } + var $spanAddOn = $('<span>', {'class' : 'add-on'}) + .append($('<i>', {'data-date-icon' : 'icon-calendar' , 'data-time-icon' : 'icon-time' })); + + $subComponent.append($input); + $subComponent.append($spanAddOn); + $subComponent.datetimepicker({ language: 'en' }); + $component.append($subComponent); + + return $component; + } +} \ No newline at end of file diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleForm.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleForm.js index 0156eef0e5e..a9255b3bdc2 100644 --- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleForm.js +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleForm.js @@ -139,6 +139,7 @@ function SampleForm(serverFacade, inspector, containerId, profile, sampleTypeCod localReference._reloadPreviewImage(); } + //Disable managed and dinamic var sampleType = localReference.profile.getTypeForTypeCode(localReference.sampleTypeCode); for(var i = 0; i < sampleType.propertyTypeGroups.length; i++) { var propertyTypeGroup = sampleType.propertyTypeGroups[i]; @@ -149,7 +150,11 @@ function SampleForm(serverFacade, inspector, containerId, profile, sampleTypeCod } } } - + + //Repaint parents and children after updating the property state to show the annotations + localReference.sampleLinksParents.repaint(); + localReference.sampleLinksChildren.repaint(); + //Allow user input Util.unblockUI(); localReference.isFormLoaded = true; @@ -506,8 +511,6 @@ function SampleForm(serverFacade, inspector, containerId, profile, sampleTypeCod //Add form to layout $("#"+this.containerId).append(component); this.storage.repaint(); - this.sampleLinksParents.repaint(); - this.sampleLinksChildren.repaint(); //Enable Events $("#sampleCode").change( diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/widgets/SampleLinksWidget.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/widgets/SampleLinksWidget.js index a08394de641..6e7f7e595db 100644 --- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/widgets/SampleLinksWidget.js +++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/widgets/SampleLinksWidget.js @@ -21,8 +21,8 @@ * * @constructor * @this {SampleLinksTable} - * @param {string} containerId The Container where the Inspector DOM will be atached. - * @param {Profile} profile The profile to be used, typicaly, the global variable that holds the configuration for the application. + * @param {string} containerId The Container where the Inspector DOM will be attached. + * @param {Profile} profile The profile to be used, typically, the global variable that holds the configuration for the application. * @param {boolean} isDisabled Disables the component. */ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleTypeHints, isDisabled, samplesToEdit) { @@ -35,10 +35,31 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType this.samplesToEdit = (samplesToEdit)?samplesToEdit:new Array(); //Only used to populate the widget this.samples = {}; this.samplesRemoved = {}; + this.stateObj = null; this._lastUsedId = null; this._lastIndex = 0; + this._getDefaultSampleHint = function(sampleTypeCode) { + var defaultMinCount = 0; + var defaultProperties = []; + + for(var i = 0; i < sampleTypeHints.length; i++) { + if(sampleTypeHints[i]["TYPE"] === sampleTypeCode) { + defaultMinCount = sampleTypeHints[i]["MIN_COUNT"]; + defaultProperties = sampleTypeHints[i]["ANNOTATION_PROPERTIES"]; + } + } + + var typeToAdd = { + "LABEL" : sampleTypeCode, + "TYPE": sampleTypeCode, + "MIN_COUNT" : defaultMinCount, + "ANNOTATION_PROPERTIES" : defaultProperties + }; + + return typeToAdd; + } this._addAny = function(id, tableId, sampleId) { var sampleTypes = this.profile.getAllSampleTypes(); @@ -72,12 +93,7 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType $("#"+_this._lastUsedId).css({"background-color" : "#FFFFFF" }); } - var typeToAdd = { - "LABEL" : sampleTypeCode, - "TYPE": sampleTypeCode, - "MIN_COUNT" : 0 - }; - + var typeToAdd = _this._getDefaultSampleHint(sampleTypeCode); _this.addOneSlot(typeToAdd); Util.unblockUI(); } @@ -113,6 +129,35 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType $controls.append($buttonTextField); $controls.append(" "); + var annotations = sampleTypeHint["ANNOTATION_PROPERTIES"]; + var annotationComponents = []; + + for(var i = 0; i < annotations.length; i++) { + var propertyType = this.profile.getPropertyType(annotations[i]["TYPE"]); + propertyType.mandatory = annotations[i]["MANDATORY"]; + var $propertyField = FormUtil.getFieldForPropertyType(propertyType); + $propertyField.attr("property-type-code" , annotations[i]["TYPE"]); + + $propertyField.change(function() { + var samplePermId = _this.samples[sampleId].permId; + + var field = $(this); + var sampleTypeAnnotations = _this.stateObj[samplePermId]; + if(!sampleTypeAnnotations) { + sampleTypeAnnotations = {}; + _this.stateObj[samplePermId] = sampleTypeAnnotations; + } + sampleTypeAnnotations[field.attr("property-type-code")] = field.val(); + + $("#ANNOTATIONS_STATE").val(JSON.stringify(_this.stateObj)); + }); + + $controls.append(propertyType.label + ": "); + $controls.append($propertyField); + $controls.append(" "); + annotationComponents.push($propertyField); + } + var $buttonPlusOne = $("<a>", {"class" : "btn" }); $buttonPlusOne.append($("<i>", { "class" : "icon-plus-sign"})); $controls.append($buttonPlusOne); @@ -124,6 +169,9 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType if(this.isDisabled) { $buttonTextField.attr("disabled", ""); + for(var i = 0; i < annotationComponents.length; i++) { + annotationComponents[i].attr("disabled", ""); + } $buttonPlusOne.attr("disabled", ""); $buttonDelete.attr("disabled", ""); } else { @@ -133,10 +181,12 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType var sampleType = _this.profile.getTypeForTypeCode(sampleTypeCode); if(sampleType !== null) { + //Clear last state if(_this._lastUsedId) { $('#'+_this._lastUsedId + "-table").empty(); $("#"+_this._lastUsedId).css({"background-color" : "#FFFFFF" }); } + //Put new state var onClick = function(sample) { $('#'+_this._lastUsedId + "-table").empty(); $("#"+_this._lastUsedId).css({"background-color" : "#FFFFFF" }); @@ -147,6 +197,7 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType $("#" + id).css({"border-radius" : "10px", "padding" : "10px", "background-color" : "#EEEEEE" }); var sampleTable = new SampleTable(_this.serverFacade,tableId,_this.profile, sampleTypeCode, false, false, onClick, false, true); sampleTable.init(); + //Store new state _this._lastUsedId = id; } else { _this._addAny(id, tableId, sampleId); @@ -207,6 +258,19 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType this.addOneSlot(sampleTypeHints[i]); } + //Initialize annotations from property + var stateField = $("#ANNOTATIONS_STATE"); + if(stateField.length === 0) { + Util.showError("You need a property with code ANNOTATIONS_STATE on this entity to store the state of the annotations."); + } else { + //Hide State Field + var fieldset = stateField.parent().parent().parent(); + fieldset.hide(); + + //Update Values + this.stateObj = JSON.parse((!stateField.val())?"{}":stateField.val()); + } + //Add sample links to edit for(var i = 0; i < this.samplesToEdit.length; i++) { this.addSample(this.samplesToEdit[i]); @@ -285,11 +349,7 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType var sampleId = this.containerId + "-" + this._lastIndex + "-sample"; freePredefinedSampleId = sampleId; - var typeToAdd = { - "LABEL" : sampleToAdd.sampleTypeCode, - "TYPE": sampleToAdd.sampleTypeCode, - "MIN_COUNT" : 0 - }; + var typeToAdd = this._getDefaultSampleHint(sampleToAdd.sampleTypeCode); this.addOneSlot(typeToAdd); } @@ -297,16 +357,17 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType this.samples[freePredefinedSampleId] = sampleToAdd; //Show meaningful information - var propertiesToShow = this.profile.typePropertiesForTable[sampleToAdd.sampleTypeCode]; + var propertiesToShow = this.profile.typePropertiesForSmallTable[sampleToAdd.sampleTypeCode]; if(propertiesToShow === null || propertiesToShow === undefined) { - propertiesToShow = this.profile.getAllPropertiCodesForTypeCode(sampleToAdd.sampleTypeCode); + propertiesToShow = []; } + var propertiesToShowDisplayNames = this.profile.getPropertiesDisplayNamesForTypeCode(sampleToAdd.sampleTypeCode, propertiesToShow); var meaningfulInfo = "<b>Code: </b>" + sampleToAdd.code + " "; - var max3Length = (propertiesToShow.length > 3)?3:propertiesToShow.length; - for(var j = 0; j < max3Length; j++) { + + for(var j = 0; j < propertiesToShow.length; j++) { var propertyToShow = sampleToAdd.properties[propertiesToShow[j]]; if(!propertyToShow && propertiesToShow[j].charAt(0) === '$') { propertyToShow = sampleToAdd.properties[propertiesToShow[j].substr(1)]; @@ -321,6 +382,17 @@ function SampleLinksWidget(containerId, profile, serverFacade, title, sampleType } $input.empty(); $input.append(meaningfulInfo); + + //Update annotations + var sampleState = this.stateObj[sampleToAdd.permId]; + var items = $input.parent().children(); + for(var i = 0; i < items.length; i++) { + var item = $(items[i]); + var propertyTypeCode = item.attr("property-type-code"); + if(sampleState[propertyTypeCode]) { + item.val(sampleState[propertyTypeCode]); + } + } } else { Util.showError("Item Already selected, choose another."); } -- GitLab