From c965eafdb8c83453cc2a5f2b6c5a5acd7697d1b2 Mon Sep 17 00:00:00 2001 From: goshiro2013 Date: Fri, 28 Apr 2017 13:00:13 -0700 Subject: [PATCH 001/576] modify picklist to add additional select options --- modules/CmpdReg/src/client/src/PickList.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/src/PickList.js b/modules/CmpdReg/src/client/src/PickList.js index c18baa6c7..380e03297 100755 --- a/modules/CmpdReg/src/client/src/PickList.js +++ b/modules/CmpdReg/src/client/src/PickList.js @@ -64,6 +64,16 @@ $(function() { } else { this.insertFirstOption = null; } + if (this.options.insertSecondOption !=null) { + this.insertSecondOption = this.options.insertSecondOption; + } else { + this.insertSecondOption = null; + } + if (this.options.insertThirdOption !=null) { + this.insertThirdOption = this.options.insertThirdOption; + } else { + this.insertThirdOption = null; + } if (this.options.showIgnored !=null) { this.showIgnored = this.options.showIgnored; } else { @@ -72,10 +82,16 @@ $(function() { }, handleListReset: function() { + if (this.insertThirdOption) { + this.collection.add(this.insertThirdOption, {at: 0, silent: true}); + } + if (this.insertSecondOption) { + this.collection.add(this.insertSecondOption, {at: 0, silent: true}); + } if (this.insertFirstOption) { this.collection.add(this.insertFirstOption, {at: 0, silent: true}); } - this.render(); + this.render(); }, render: function() { From 0b3001cc24d0dc14900351be003e2d7b66603f1f Mon Sep 17 00:00:00 2001 From: goshiro2013 Date: Wed, 3 May 2017 22:08:17 -0700 Subject: [PATCH 002/576] add chem struct services --- .../server/routes/ChemStructureRoutes.coffee | 67 +++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee b/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee index ea1c56e61..b2a355a18 100644 --- a/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee +++ b/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee @@ -5,17 +5,19 @@ exports.setupAPIRoutes = (app) -> app.put '/api/chemStructure/:id', exports.putStructure app.post '/api/chemStructure/calculateMoleculeProperties', exports.calculateMoleculeProperties app.post '/api/chemStructure/renderMolStructure', exports.renderMolStructure - + app.post '/api/chemStructure/renderMolStructureBase64', exports.renderMolStructure exports.setupRoutes = (app, loginRoutes) -> app.get '/api/chemStructure/renderStructureByThingCode', loginRoutes.ensureAuthenticated, exports.renderStructureByThingCode + app.get '/api/chemStructure/renderStructureByCode', loginRoutes.ensureAuthenticated, exports.renderStructureByCode + app.get '/api/chemStructure/codename/:structureCode', loginRoutes.ensureAuthenticated, exports.getStructureByCode app.post '/api/chemStructure', loginRoutes.ensureAuthenticated, exports.postStructure app.put '/api/chemStructure/:id', loginRoutes.ensureAuthenticated, exports.putStructure app.post '/api/chemStructure/calculateMoleculeProperties', loginRoutes.ensureAuthenticated, exports.calculateMoleculeProperties app.post '/api/chemStructure/renderMolStructure', loginRoutes.ensureAuthenticated, exports.renderMolStructure - + app.post '/api/chemStructure/renderMolStructureBase64', loginRoutes.ensureAuthenticated, exports.renderMolStructureBase64 _ = require 'underscore' @@ -35,6 +37,19 @@ exports.renderStructureByThingCode = (req, resp) -> console.log rooUrl req.pipe(request(rooUrl)).pipe(resp) +exports.renderStructureByCode = (req, resp) -> + if global.specRunnerTestmode + console.debug process.cwd() + else + config = require '../conf/compiled/conf.js' + baseurl = "#{config.all.client.service.persistence.fullpath}structure/renderStructureByCodeName" + request = require 'request' + queryParams = req._parsedUrl.query + rooUrl = baseurl + '?' + queryParams + console.log '######## line 49 -- renderStructureByCode #########' + console.log rooUrl + req.pipe(request(rooUrl)).pipe(resp) + exports.getStructureByCode = (req, resp) -> if global.specRunnerTestmode console.debug process.cwd() @@ -42,6 +57,7 @@ exports.getStructureByCode = (req, resp) -> config = require '../conf/compiled/conf.js' baseurl = "#{config.all.client.service.persistence.fullpath}structure/getByCodeName/"+req.params.structureCode request = require 'request' +# req.pipe(request(baseurl)).pipe(resp) request( method: 'GET' url: baseurl @@ -132,22 +148,63 @@ exports.renderMolStructure = (req, resp) -> resp.json {molStructure: req.body[0].molStructure, height: req.body[0].height, width: req.body[0].width, format: req.body[0].format} else molecule = req.body + console.log 'incoming req.body' + console.log molecule config = require '../conf/compiled/conf.js' baseurl = config.all.client.service.persistence.fullpath+"structure/renderMolStructure" request = require 'request' + console.log 'line 136 ---- attempt to hit renderMolStructure -- how to handle reponse' request( method: 'POST' url: baseurl body: molecule +# encoding: null json: true - , (error, response, json) => + , (error, response, output) => if !error && response.statusCode == 200 - resp.json json + console.log '$$$$$$$$$$$$$$$$ READY TO RESPOND $$$$$$$$$$$$' +# console.log response + console.log output + resp.end output +# resp.writeHead 200, {'Content-Type': 'text/html'} +# resp.write '') +# resp.writeHead 200, 'Content-Type': 'image/png' +# resp.end buffer else - console.log 'got ajax error trying to render molStructure' + console.log '--- line 153: got ajax error trying to render molStructure' console.log error console.log json console.log response resp.statusCode = 500 resp.end JSON.stringify "render molStructure failed" ) + +exports.renderMolStructureBase64 = (req, resp) -> + if global.specRunnerTestmode + resp.json {molStructure: req.body[0].molStructure, height: req.body[0].height, width: req.body[0].width, format: req.body[0].format} + else + molecule = req.body + console.log 'incoming req.body' + console.log molecule + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"structure/renderMolStructureBase64" + request = require 'request' + console.log 'line 136 ---- attempt to hit renderMolStructure -- how to handle reponse' + request( + method: 'POST' + url: baseurl + body: molecule + json: true + , (error, response, output) => + if !error && response.statusCode == 200 + resp.end output + else + console.log '--- line 153: got ajax error trying to render molStructure' + console.log error + console.log json + console.log response + resp.statusCode = 500 + resp.end JSON.stringify "render molStructure failed" + ) \ No newline at end of file From 45ac9c43823041b62c77e5302a1e3208bea3237a Mon Sep 17 00:00:00 2001 From: goshiro2013 Date: Wed, 3 May 2017 22:14:05 -0700 Subject: [PATCH 003/576] add chem struct services --- .../server/routes/ChemStructureRoutes.coffee | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee b/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee index b2a355a18..3c9b671fb 100644 --- a/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee +++ b/modules/ChemStructure/src/server/routes/ChemStructureRoutes.coffee @@ -148,17 +148,13 @@ exports.renderMolStructure = (req, resp) -> resp.json {molStructure: req.body[0].molStructure, height: req.body[0].height, width: req.body[0].width, format: req.body[0].format} else molecule = req.body - console.log 'incoming req.body' - console.log molecule config = require '../conf/compiled/conf.js' baseurl = config.all.client.service.persistence.fullpath+"structure/renderMolStructure" request = require 'request' - console.log 'line 136 ---- attempt to hit renderMolStructure -- how to handle reponse' request( method: 'POST' url: baseurl body: molecule -# encoding: null json: true , (error, response, output) => if !error && response.statusCode == 200 @@ -181,6 +177,7 @@ exports.renderMolStructure = (req, resp) -> resp.end JSON.stringify "render molStructure failed" ) + exports.renderMolStructureBase64 = (req, resp) -> if global.specRunnerTestmode resp.json {molStructure: req.body[0].molStructure, height: req.body[0].height, width: req.body[0].width, format: req.body[0].format} @@ -207,4 +204,29 @@ exports.renderMolStructureBase64 = (req, resp) -> console.log response resp.statusCode = 500 resp.end JSON.stringify "render molStructure failed" - ) \ No newline at end of file + ) + +exports.acasStructureSearch = (req, resp) -> + if global.specRunnerTestmode + resp.json {queryMol: req.body[0].queryMol, searchType: req.body[0].searchType, maxResults: req.body[0].maxResults, similarity: req.body[0].similarity} + else + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"lsthings/structureSearch" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: req.body.reagentSearchParams + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + resp.json json + else + console.log 'got ajax error trying to search for structures' + console.log error + console.log json + console.log response + resp.statusCode = 500 + resp.end JSON.stringify "ACAS structure search failed" + ) + From de46d29fc80fd71e47c783ed31c5e40a2f1216ee Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 5 May 2017 11:22:18 -0700 Subject: [PATCH 004/576] merging bitbucket compound-inventory, cmg-lims branches into oss feature/inventory --- .../BasicFileValidateReviewAndSave.coffee | 1 + modules/Components/src/client/PickList.coffee | 45 +- .../routes/InventoryServiceRoutes.coffee | 366 +++++++++-- .../routes/ServerUtilityFunctions.coffee | 21 + .../dist/select2-bootstrap.css | 610 ++++++++++++++++++ .../dist/select2-bootstrap.min.css | 1 + 6 files changed, 971 insertions(+), 73 deletions(-) create mode 100755 public/lib/select2-bootstrap-theme-0.1.0-beta.8/dist/select2-bootstrap.css create mode 100755 public/lib/select2-bootstrap-theme-0.1.0-beta.8/dist/select2-bootstrap.min.css diff --git a/modules/Components/src/client/BasicFileValidateReviewAndSave.coffee b/modules/Components/src/client/BasicFileValidateReviewAndSave.coffee index 0dbd9a1b5..d4a8f366b 100644 --- a/modules/Components/src/client/BasicFileValidateReviewAndSave.coffee +++ b/modules/Components/src/client/BasicFileValidateReviewAndSave.coffee @@ -1,6 +1,7 @@ class window.BasicFileValidateReviewAndSaveController extends BasicFileValidateAndSaveController template: _.template($("#BasicFileValidateReviewAndSaveView").html()) + initialize: -> console.log "BasicFileValidateReviewAndSaveController" super() diff --git a/modules/Components/src/client/PickList.coffee b/modules/Components/src/client/PickList.coffee index 470a84cad..c9fef94d3 100644 --- a/modules/Components/src/client/PickList.coffee +++ b/modules/Components/src/client/PickList.coffee @@ -90,10 +90,15 @@ class window.PickListOptionControllerForLsThing extends Backbone.View class window.PickListSelectController extends Backbone.View - initialize: -> + initialize: (options) -> @rendered = false @collection.bind "add", @addOne @collection.bind "reset", @handleListReset + # NOTE: Backbone 1.1.0 no longer automatically attaches options passed + # to View constructors as this.options. So, in order to be compatible + # with Backbone 1.0.0 and versions greater or equal to 1.1.0, we'll + # attach it ourselves here. + @options = options || {} unless @options.selectedCode is "" @selectedCode = @options.selectedCode @@ -228,17 +233,45 @@ class window.ComboBoxController extends PickListSelectController class window.PickListSelect2Controller extends PickListSelectController + # maps the 'code' property to the select2 required 'id' property and + # the 'name' property to the select2 required 'text' property + acasPropertyMap = + id: 'code' + text: 'name' + + # @param options.propertyMap String ('select2' or 'acas', where 'select2' + # does no mapping and 'acas' uses the 'acasPropertyMap' defined above) or + # an Object with 'id' and 'text' properties to use. If + # 'options.propertyMap' is not specified, it defaults to using the + # 'acasPropertyMap' + initialize: (options) -> + @propertyMap = acasPropertyMap + if options.propertyMap? + if _.isObject(options.propertyMap) and options.propertyMap.id? and options.propertyMap.text? + @propertyMap = options.propertyMap + else if options.propertyMap is 'select2' + @propertyMap = null + else if options.propertyMap isnt 'acas' + throw new Error ('PickListSelect2Controller.initialize(): Invalid propertyMap value encountered') + super(options) + render: => -# convert model objects to array of json objects which have 'id' and 'text' properties + # convert model objects to array of json objects which have 'id' and 'text' properties if propertyMap specified mappedData = [] for obj in @collection.toJSON() if (not obj.ignored? or (obj.ignored is false) or (@showIgnored? and @showIgnored is true)) - obj.id = obj.id || obj.code - obj.text = obj.text || obj.name + if @propertyMap? + obj.id = obj[@propertyMap.id] + obj.text = obj[@propertyMap.text] mappedData.push(obj) + # if a first option was inserted, use it for select's placeholder object + @placeholder = "" + if @insertFirstOption? + @placeholder = mappedData[0] + $(@el).select2 - placeholder: "" + placeholder: @placeholder data: mappedData openOnEnter: false allowClear: true @@ -256,7 +289,7 @@ class window.PickListSelect2Controller extends PickListSelectController result = $(@el).val() # if result is null then we'll return the "unassigned" instead if it # was inserted as the first option - if not result? and @insertFirstOption.get('code') is "unassigned" + if not result? and @insertFirstOption.get('code') is "unassigned" result = "unassigned" result diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index b94785c9a..67482e5b5 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3,8 +3,11 @@ csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFuncti _ = require 'underscore' preferredEntityCodeService = require '../routes/PreferredEntityCodeService.js' config = require '../conf/compiled/conf.js' +RUN_CUSTOM_FLAG = "0" + exports.setupAPIRoutes = (app) -> + app.post '/api/getContainersInLocationWithTypeAndKind', exports.getContainersInLocationWithTypeAndKind app.post '/api/getContainersInLocation', exports.getContainersInLocation app.post '/api/getContainerCodesByLabels', exports.getContainerCodesByLabels app.post '/api/getContainersByLabels', exports.getContainersByLabels @@ -48,8 +51,10 @@ exports.setupAPIRoutes = (app) -> app.post '/api/createTube', exports.createTube app.post '/api/createTubes', exports.createTubes app.post '/api/throwInTrash', exports.throwInTrash + app.post '/api/updateContainerHistoryLogs', exports.updateContainerHistoryLogs exports.setupRoutes = (app, loginRoutes) -> + app.post '/api/getContainersInLocationWithTypeAndKind', loginRoutes.ensureAuthenticated, exports.getContainersInLocationWithTypeAndKind app.post '/api/getContainersInLocation', loginRoutes.ensureAuthenticated, exports.getContainersInLocation app.post '/api/getContainerCodesByLabels', loginRoutes.ensureAuthenticated, exports.getContainerCodesByLabels app.post '/api/getContainersByLabels', loginRoutes.ensureAuthenticated, exports.getContainersByLabels @@ -94,6 +99,8 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/createTube', loginRoutes.ensureAuthenticated, exports.createTube app.post '/api/createTubes', loginRoutes.ensureAuthenticated, exports.createTubes app.post '/api/throwInTrash', loginRoutes.ensureAuthenticated, exports.throwInTrash + app.post '/api/updateContainerHistoryLogs', loginRoutes.ensureAuthenticated, exports.updateContainerHistoryLogs + exports.getContainersInLocation = (req, resp) -> req.setTimeout 86400000 @@ -101,8 +108,14 @@ exports.getContainersInLocation = (req, resp) -> inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' resp.json inventoryServiceTestJSON.getContainersInLocationResponse else + queryParams = [] + if req.query.containerType? + queryParams.push "containerType="+req.query.containerType + if req.query.containerKind? + queryParams.push "containerKind="+req.query.containerKind + queryString = queryParams.join "&" config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"containers/getContainersInLocation" + baseurl = config.all.client.service.persistence.fullpath+"containers/getContainersInLocation?"+queryString request = require 'request' request( method: 'POST' @@ -121,6 +134,59 @@ exports.getContainersInLocation = (req, resp) -> resp.end JSON.stringify "getContainersInLocation failed" ) +exports.getContainersInLocationWithTypeAndKind = (req, resp) -> + req.setTimeout 86400000 + if global.specRunnerTestmode + inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' + resp.json inventoryServiceTestJSON.getContainersInLocationResponse + else + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"containers/getContainersInLocation?containerType=location&containerKind=default" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: req.body.values + json: true + timeout: 86400000 + , (error, response, json) => + if !error && response.statusCode == 200 + resp.json json + else + console.error 'got ajax error trying to get getContainersInLocation' + console.error error + console.error json + console.error response + resp.end JSON.stringify "getContainersInLocation failed" + ) + +exports.getContainersInLocationWithTypeAndKindInternal = (values, callback) -> + #req.setTimeout 86400000 + if global.specRunnerTestmode + inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' + resp.json inventoryServiceTestJSON.getContainersInLocationResponse + else + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"containers/getContainersInLocation?containerType=location&containerKind=default" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: values + json: true + timeout: 86400000 + , (error, response, json) => + if !error && response.statusCode == 200 + callback json, response.statusCode + else + console.error 'got ajax error trying to get getContainersInLocation' + console.error error + console.error json + console.error response + #resp.end JSON.stringify "getContainersInLocation failed" + callback response, response.statusCode + ) + exports.getContainersByLabels = (req, resp) -> req.setTimeout 86400000 exports.getContainersByLabelsInternal req.body, req.query.containerType, req.query.containerKind, req.query.labelType, req.query.labelKind, (json, statusCode) -> @@ -220,6 +286,32 @@ exports.getWellCodesByPlateBarcodes = (req, resp) -> else resp.json json +exports.getContainersInBoxPositionInternal = (values, callback) -> + if global.specRunnerTestmode + inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' + resp.json inventoryServiceTestJSON.getContainersInLocationResponse + else + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"containers/getContainersInLocation?containerType=container&containerKind=tube" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: values + json: true + timeout: 86400000 + , (error, response, json) => + if !error && response.statusCode == 200 + callback json, response.statusCode + else + console.error 'got ajax error trying to get getContainersInLocation' + console.error error + console.error json + console.error response + #resp.end JSON.stringify "getContainersInLocation failed" + callback response, response.statusCode + ) + exports.getWellCodesByPlateBarcodes = (req, resp) -> req.setTimeout 86400000 if global.specRunnerTestmode @@ -426,7 +518,7 @@ exports.updateContainersByContainerCodesInternal = (updateInformation, callCusto else barcodes = _.pluck updateInformation, "barcode" console.debug "calling getContainerCodesByLabelsInternal" - exports.getContainerCodesByLabelsInternal barcodes, null, null, "barcode", "barcode", (containerCodes, statusCode) => + exports.getContainerCodesByLabelsInternal barcodes, "container", null, "barcode", "barcode", (containerCodes, statusCode) => if statusCode == 500 console.error "updateContainerMetadataByContainerCodeInternal failed: #{JSON.stringify containerCodes}" callback "updateContainersByContainerCodesInternal failed", 500 @@ -585,6 +677,8 @@ exports.getBreadCrumbByContainerCodeInternal = (codeNamesJSON, delimeter, callba headers: 'content-type': 'application/json' , (error, response, json) => if !error && response.statusCode == 200 + console.log 'json in getBreadCrumbByContainerCodeInternal' + console.log json callback json else console.error 'got ajax error trying to get getBreadCrumbByContainerCode' @@ -1161,41 +1255,52 @@ exports.moveToLocation = (req, resp) -> resp.json json exports.moveToLocationInternal = (input, callCustom, callback) -> - callCustom = callCustom != "0" - if global.specRunnerTestmode - inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' - resp.json inventoryServiceTestJSON.moveToLocationResponse - else - console.debug 'incoming moveToLocationJSON request: ', JSON.stringify(input) - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"containers/moveToLocation" - console.debug 'base url: ', baseurl - request = require 'request' - request( - method: 'POST' - url: baseurl - body: input - json: true - timeout: 86400000 - headers: 'content-type': 'application/json' - , (error, response, json) => - console.debug "response statusCode: #{response.statusCode}" - if !error - if callCustom && csUtilities.moveToLocation? - console.log "running customer specific server function moveToLocation" - csUtilities.moveToLocation input, (customerResponse, statusCode) -> - json = _.extend json, customerResponse - callback json, statusCode + #exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> + # console.log 'updated history logs before moving to temp' + # ) + callCustom = callCustom != "0" + if global.specRunnerTestmode + inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' + resp.json inventoryServiceTestJSON.moveToLocationResponse + else + console.debug 'incoming moveToLocationJSON request: ', JSON.stringify(input) + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"containers/moveToLocation" + console.debug 'base url: ', baseurl + request = require 'request' + console.log 'request into moveToLocation' + console.log input + request( + method: 'POST' + url: baseurl + body: input + json: true + timeout: 86400000 + headers: 'content-type': 'application/json' + , (error, response, json) => + #add the call to updateContainerHistoryLogs here... + console.debug "response statusCode: #{response.statusCode}" + if !error + console.log 'container down here...did it pull through??' + console.log input[0].containerCodeName + exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> + if callCustom && csUtilities.moveToLocation? + console.log "running customer specific server function moveToLocation" + csUtilities.moveToLocation input, (customerResponse, statusCode) -> + json = _.extend json, customerResponse + callback json, statusCode + else + console.warn "could not find customer specific server function moveToLocation so not running it" + callback json, response.statusCode + ) else - console.warn "could not find customer specific server function moveToLocation so not running it" - callback json, response.statusCode - else - console.error 'got ajax error trying to get moveToLocation' - console.error error - console.error json - console.error response - callback JSON.stringify("updateWellContent failed"), 500 - ) + console.error 'got ajax error trying to get moveToLocation' + console.error error + console.error json + console.error response + callback JSON.stringify("updateWellContent failed"), 500 + ) + #) exports.getWellContentByContainerLabel = (req, resp) -> req.setTimeout 86400000 @@ -1484,8 +1589,11 @@ exports.mergeContainersInternal = (input, callback) -> isOdd = (num) -> return (num % 2) == 1 alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - _.map originWellContent, (originWells) -> - quadrant = _.findWhere input.quadrants, {"codeName": originWells.containerCodeName} + # for each quadrant + _.map input.quadrants, (quadrant) -> + # find plate whose name matches the quadrant name + originWells = _.findWhere originWellContent, {"containerCodeName": quadrant.codeName} + # map plate wells to merged plate wells _.map originWells.wellContent, (wellContent) -> wellContent = _.omit(wellContent, ['containerCodeName', 'wellName', 'recordedDate']) wellContent.recordedDate = 1455323242544 @@ -1623,6 +1731,7 @@ exports.containerLogsInternal = (inputs, callCustom, callback) -> callback json, statusCode exports.containerLocationHistory = (req, resp) -> + req.setTimeout 86400000 exports.containerLocationHistoryInternal req.body, req.query.callCustom, (json, statusCode) -> resp.statusCode = statusCode resp.json json @@ -1636,7 +1745,9 @@ exports.containerLocationHistoryInternal = (inputs, callCustom, callback) -> exports.addContainerLocationHistory inputs, callCustom, (json, statusCode) -> callback json, statusCode + exports.getContainerLogs = (req, resp) -> + req.setTimeout 86400000 exports.getContainerLogsInternal [req.params.label], req.query.containerType, req.query.containerKind, req.query.labelType, req.query.labelKind, (json, statusCode) -> resp.statusCode = statusCode resp.json json @@ -1754,7 +1865,6 @@ exports.addContainerLocationHistory = (inputs, callCustom, callback) -> if csUtilities.addContainerLocationHistory? console.log "running customer specific server function addContainerLocationHistory" csUtilities.addContainerLocationHistory inputs, (response) -> - console.log response else console.warn "could not find customer specific server function addContainerLocationHistory so not running it" callback json, statusCode @@ -1980,33 +2090,155 @@ exports.throwInTrash = (req, resp) -> resp.json json exports.throwInTrashInternal = (input, callCustom, callback) -> - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath + "containers/throwInTrash" - console.log baseurl - request = require 'request' - request( - method: 'POST' - url: baseurl - body: JSON.stringify input - json: true - timeout: 6000000 - , (error, response, json) => - if !error && response.statusCode == 204 -# If call custom doesn't equal 0 then call custom - callCustom = callCustom != "0" - if callCustom && csUtilities.throwInTrash? - console.log "running customer specific server function throwInTrash" - csUtilities.throwInTrash input, (customerResponse, statusCode) -> -# json = _.extend json, customerResponse - callback json, response.statusCode + #exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath + "containers/throwInTrash" + console.log baseurl + request = require 'request' + request( + method: 'POST' + url: baseurl + body: JSON.stringify input + json: true + timeout: 6000000 + , (error, response, json) => + if !error && response.statusCode == 204 + exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> + # If call custom doesn't equal 0 then call custom + callCustom = callCustom != "0" + if callCustom && csUtilities.throwInTrash? + console.log "running customer specific server function throwInTrash" + csUtilities.throwInTrash input, (customerResponse, statusCode) -> + # json = _.extend json, customerResponse + callback json, response.statusCode + else + console.warn "could not find customer specific server function throwInTrash so not running it" + callback json, response.statusCode + ) + else if response.statusCode == 400 + callback response.body, response.statusCode else - console.warn "could not find customer specific server function throwInTrash so not running it" - callback json, response.statusCode - else if response.statusCode == 400 - callback response.body, response.statusCode - else - console.log 'got ajax error trying to create tube' - console.log error - console.log response - callback response.body, 500 + console.log 'got ajax error trying to create tube' + console.log error + console.log response + callback response.body, 500 + ) + #) + +exports.updateContainerHistoryLogsInternal = (containers, callback) -> + formattedContainers = [] + index = 0 + #_.each(containers, (container) -> + formatContainersForLocationHistoryUpdate(containers, (statusCode, formattedContainers) -> + exports.containerLocationHistoryInternal(formattedContainers, RUN_CUSTOM_FLAG, (json, statusCode) -> + return callback json,statusCode + ) + # else + # return callback null, 500 + ) + +formatContainersForLocationHistoryUpdate = (containers, callback) -> + createContainersWithBreadcrumb(containers, (containersWithBreadcrumb, modifiedBy, modifiedDate) -> + formatLocationStrings(containersWithBreadcrumb, (formattedContainersWithBreadcrumb) -> + formatContainers(formattedContainersWithBreadcrumb, modifiedBy, modifiedDate, (formattedContainers) -> + return callback 200, formattedContainers + ) + ) + ) + +createContainersWithBreadcrumb = (containers, callback) -> + containerCodeNames = [] + modifiedBy = containers[0].modifiedBy + modifiedDate = containers[0].modifiedDate + _.each(containers, (container) -> + containerCodeNames.push(container.containerCodeName) + ) + exports.getBreadCrumbByContainerCodeInternal(containerCodeNames, "<", (containersWithBreadcrumb) -> + return callback containersWithBreadcrumb, modifiedBy, modifiedDate + ) + +formatLocationStrings = (containersWithBreadcrumb, callback) -> + formattedContainersWithBreadcrumb = [] + _.each(containersWithBreadcrumb, (container) -> + locationArray = container.labelBreadCrumb.split("<") + locationArrayString = JSON.stringify(locationArray) + container.locationArrayString = locationArrayString + formattedContainersWithBreadcrumb.push(container) + ) + return callback formattedContainersWithBreadcrumb + +formatContainers = (containers, modifiedBy, modifiedDate, callback) -> + formattedContainers = [] + _.each(containers, (container) -> + formattedContainer = { + "codeName": container.containerCode + "recordedBy": modifiedBy + "recordedDate": modifiedDate + "location": container.locationArrayString + "movedBy": modifiedBy + "movedDate": modifiedDate + "additionalValues": [] + } + formattedContainers.push(formattedContainer) ) + statusCode = 200 + return callback formattedContainers + #callback(formattedContainers, statusCode) + + +# getLocationBreadcrumb = (container, callback) -> +# exports.getBreadCrumbByContainerCodeInternal([container.containerCodeName], "<", (breadcrumb) -> +# labelBreadCrumb = breadcrumb[0].labelBreadCrumb +# callback labelBreadCrumb +# # console.log 'breadcrumb for move to locations' +# # console.log breadcrumb[0].labelBreadCrumb +# # #test breadCrumb = 'CAGE00002 +# getLocationBreadcrumb(container, (labelBreadCrumb) -> +# createLocationArray(labelBreadCrumb, (locationArrayString) -> +# #buildLocationArrayString(locationArray, (locationArrayString) -> +# formatContainer(container, locationArrayString, (formattedContainer, statusCode) -> +# callback statusCode, formattedContainer +# ) +# ) +# ) +# +# createLocationArray = (labelBreadCrumb, callback) -> +# locationArray = labelBreadCrumb.split("<") +# locationArrayString = JSON.stringify(locationArray) +# callback locationArrayString.toUpperCase() +# +# formatContainer = (container, locationArrayString, callback) -> +# +# formattedContainer = { +# "codeName": container.containerCodeName +# "recordedBy": container.modifiedBy +# "recordedDate": container.modifiedDate +# "location": locationArrayString +# "movedBy": container.modifiedBy +# "movedDate": container.modifiedDate +# "additionalValues": [] +# } +# +# statusCode = 200 +# callback(formattedContainer, statusCode) + +# buildLocationArrayString = (locationArray, callback) -> +# locationSeparator = '\",\"' +# locationArrayString = '[\"' + +# locationArray[0] + +# locationSeparator + +# locationArray[1] + +# locationSeparator + +# locationArray[2] + +# locationSeparator + +# locationArray[3] + +# '\"]' +# +# console.log 'locationArrayString ' +# console.log locationArrayString +# callback locationArrayString diff --git a/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee b/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee index ae887c6fd..54766d26c 100644 --- a/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee +++ b/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee @@ -1429,6 +1429,27 @@ class Container extends Backbone.Model additionalValues: additionalValues response.push responseObject return response + + getLocationHistory: -> + lsStates = @get('lsStates').getStatesByTypeAndKind 'metadata', 'location history' + response = [] + if lsStates? + lsStates.forEach (lsState) => + additionalValues = lsState.get('lsValues').filter (value) -> + (!value.get('ignored')) and + !((value.get('lsType')=='stringValue') and (value.get('lsKind')=='location')) and + !((value.get('lsType')=='codeValue') and (value.get('lsKind')=='moved by')) and + !((value.get('lsType')=='dateValue') and (value.get('lsKind')=='moved date')) + responseObject = + codeName: @get('codeName') + recordedBy: lsState.get('recordedBy') + recordedDate: lsState.get('recordedDate') + location: lsState.getValuesByTypeAndKind('stringValue', 'location')[0].get('stringValue') + movedBy: lsState.getValuesByTypeAndKind('codeValue', 'moved by')[0]?.get('codeValue') + movedDate: lsState.getValuesByTypeAndKind('dateValue', 'moved date')[0]?.get('dateValue') + additionalValues: additionalValues + response.push responseObject + return response getLocationHistory: -> lsStates = @get('lsStates').getStatesByTypeAndKind 'metadata', 'location history' diff --git a/public/lib/select2-bootstrap-theme-0.1.0-beta.8/dist/select2-bootstrap.css b/public/lib/select2-bootstrap-theme-0.1.0-beta.8/dist/select2-bootstrap.css new file mode 100755 index 000000000..caf2b9abd --- /dev/null +++ b/public/lib/select2-bootstrap-theme-0.1.0-beta.8/dist/select2-bootstrap.css @@ -0,0 +1,610 @@ +/*! Select2 Bootstrap Theme v0.1.0-beta.8 | MIT License | github.com/select2/select2-bootstrap-theme */ +.select2-container--bootstrap { + display: block; + /*------------------------------------*\ + #COMMON STYLES + \*------------------------------------*/ + /** + * Search field in the Select2 dropdown. + */ + /** + * No outline for all search fields - in the dropdown + * and inline in multi Select2s. + */ + /** + * Adjust Select2's choices hover and selected styles to match + * Bootstrap 3's default dropdown styles. + * + * @see http://getbootstrap.com/components/#dropdowns + */ + /** + * Clear the selection. + */ + /** + * Address disabled Select2 styles. + * + * @see https://select2.github.io/examples.html#disabled + * @see http://getbootstrap.com/css/#forms-control-disabled + */ + /*------------------------------------*\ + #DROPDOWN + \*------------------------------------*/ + /** + * Dropdown border color and box-shadow. + */ + /** + * Limit the dropdown height. + */ + /*------------------------------------*\ + #SINGLE SELECT2 + \*------------------------------------*/ + /*------------------------------------*\ + #MULTIPLE SELECT2 + \*------------------------------------*/ + /** + * Address Bootstrap control sizing classes + * + * 1. Reset Bootstrap defaults. + * 2. Adjust the dropdown arrow button icon position. + * + * @see http://getbootstrap.com/css/#forms-control-sizes + */ + /* 1 */ + /*------------------------------------*\ + #RTL SUPPORT + \*------------------------------------*/ +} +.select2-container--bootstrap .select2-selection { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + color: #555555; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + outline: 0; +} +.select2-container--bootstrap .select2-selection.form-control { + border-radius: 4px; +} +.select2-container--bootstrap .select2-search--dropdown .select2-search__field { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + color: #555555; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; +} +.select2-container--bootstrap .select2-search__field { + outline: 0; + /* Firefox 18- */ + /** + * Firefox 19+ + * + * @see http://stackoverflow.com/questions/24236240/color-for-styled-placeholder-text-is-muted-in-firefox + */ +} +.select2-container--bootstrap .select2-search__field::-webkit-input-placeholder { + color: #999; +} +.select2-container--bootstrap .select2-search__field:-moz-placeholder { + color: #999; +} +.select2-container--bootstrap .select2-search__field::-moz-placeholder { + color: #999; + opacity: 1; +} +.select2-container--bootstrap .select2-search__field:-ms-input-placeholder { + color: #999; +} +.select2-container--bootstrap .select2-results__option { + /** + * Disabled results. + * + * @see https://select2.github.io/examples.html#disabled-results + */ + /** + * Hover state. + */ + /** + * Selected state. + */ +} +.select2-container--bootstrap .select2-results__option[role=group] { + padding: 0; +} +.select2-container--bootstrap .select2-results__option[aria-disabled=true] { + color: #777777; + cursor: not-allowed; +} +.select2-container--bootstrap .select2-results__option[aria-selected=true] { + background-color: #f5f5f5; + color: #262626; +} +.select2-container--bootstrap .select2-results__option--highlighted[aria-selected] { + background-color: #337ab7; + color: #fff; +} +.select2-container--bootstrap .select2-results__option .select2-results__option { + padding: 6px 12px; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__group { + padding-left: 0; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option { + margin-left: -12px; + padding-left: 24px; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -24px; + padding-left: 36px; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -36px; + padding-left: 48px; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -48px; + padding-left: 60px; +} +.select2-container--bootstrap .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -60px; + padding-left: 72px; +} +.select2-container--bootstrap .select2-results__group { + color: #777777; + display: block; + padding: 6px 12px; + font-size: 12px; + line-height: 1.428571429; + white-space: nowrap; +} +.select2-container--bootstrap.select2-container--focus .select2-selection, .select2-container--bootstrap.select2-container--open .select2-selection { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + -webkit-transition: border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; + border-color: #66afe9; +} +.select2-container--bootstrap.select2-container--open { + /** + * Make the dropdown arrow point up while the dropdown is visible. + */ + /** + * Handle border radii of the container when the dropdown is showing. + */ +} +.select2-container--bootstrap.select2-container--open .select2-selection .select2-selection__arrow b { + border-color: transparent transparent #999 transparent; + border-width: 0 4px 4px 4px; +} +.select2-container--bootstrap.select2-container--open.select2-container--below .select2-selection { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-bottom-color: transparent; +} +.select2-container--bootstrap.select2-container--open.select2-container--above .select2-selection { + border-top-right-radius: 0; + border-top-left-radius: 0; + border-top-color: transparent; +} +.select2-container--bootstrap .select2-selection__clear { + color: #999; + cursor: pointer; + float: right; + font-weight: bold; + margin-right: 10px; +} +.select2-container--bootstrap .select2-selection__clear:hover { + color: #333; +} +.select2-container--bootstrap.select2-container--disabled .select2-selection { + border-color: #ccc; + -webkit-box-shadow: none; + box-shadow: none; +} +.select2-container--bootstrap.select2-container--disabled .select2-selection, +.select2-container--bootstrap.select2-container--disabled .select2-search__field { + cursor: not-allowed; +} +.select2-container--bootstrap.select2-container--disabled .select2-selection, +.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice { + background-color: #eeeeee; +} +.select2-container--bootstrap.select2-container--disabled .select2-selection__clear, +.select2-container--bootstrap.select2-container--disabled .select2-selection--multiple .select2-selection__choice__remove { + display: none; +} +.select2-container--bootstrap .select2-dropdown { + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + border-color: #66afe9; + overflow-x: hidden; + margin-top: -1px; +} +.select2-container--bootstrap .select2-dropdown--above { + margin-top: 1px; +} +.select2-container--bootstrap .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; +} +.select2-container--bootstrap .select2-selection--single { + height: 34px; + line-height: 1.428571429; + padding: 6px 24px 6px 12px; + /** + * Adjust the single Select2's dropdown arrow button appearance. + */ +} +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow { + position: absolute; + bottom: 0; + right: 12px; + top: 0; + width: 4px; +} +.select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { + border-color: #999 transparent transparent transparent; + border-style: solid; + border-width: 4px 4px 0 4px; + height: 0; + left: 0; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; +} +.select2-container--bootstrap .select2-selection--single .select2-selection__rendered { + color: #555555; + padding: 0; +} +.select2-container--bootstrap .select2-selection--single .select2-selection__placeholder { + color: #999; +} +.select2-container--bootstrap .select2-selection--multiple { + min-height: 34px; + padding: 0; + height: auto; + /** + * Make Multi Select2's choices match Bootstrap 3's default button styles. + */ + /** + * Minus 2px borders. + */ + /** + * Clear the selection. + */ +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__rendered { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + display: block; + line-height: 1.428571429; + list-style: none; + margin: 0; + overflow: hidden; + padding: 0; + width: 100%; + text-overflow: ellipsis; + white-space: nowrap; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__placeholder { + color: #999; + float: left; + margin-top: 5px; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { + color: #555555; + background: #fff; + border: 1px solid #ccc; + border-radius: 4px; + cursor: default; + float: left; + margin: 5px 0 0 6px; + padding: 0 6px; +} +.select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { + background: transparent; + padding: 0 12px; + height: 32px; + line-height: 1.428571429; + margin-top: 0; + min-width: 5em; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove { + color: #999; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 3px; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #333; +} +.select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { + margin-top: 6px; +} +.select2-container--bootstrap .select2-selection--single.input-sm, .input-group-sm .select2-container--bootstrap .select2-selection--single, .form-group-sm .select2-container--bootstrap .select2-selection--single { + border-radius: 3px; + font-size: 12px; + height: 30px; + line-height: 1.5; + padding: 5px 22px 5px 10px; + /* 2 */ +} +.select2-container--bootstrap .select2-selection--single.input-sm .select2-selection__arrow b, .input-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, .form-group-sm .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { + margin-left: -5px; +} +.select2-container--bootstrap .select2-selection--multiple.input-sm, .input-group-sm .select2-container--bootstrap .select2-selection--multiple, .form-group-sm .select2-container--bootstrap .select2-selection--multiple { + min-height: 30px; + border-radius: 3px; +} +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-selection__choice, .input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, .form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { + font-size: 12px; + line-height: 1.5; + margin: 4px 0 0 5px; + padding: 0 5px; +} +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-search--inline .select2-search__field, .input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, .form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { + padding: 0 10px; + font-size: 12px; + height: 28px; + line-height: 1.5; +} +.select2-container--bootstrap .select2-selection--multiple.input-sm .select2-selection__clear, .input-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear, .form-group-sm .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { + margin-top: 5px; +} +.select2-container--bootstrap .select2-selection--single.input-lg, .input-group-lg .select2-container--bootstrap .select2-selection--single, .form-group-lg .select2-container--bootstrap .select2-selection--single { + border-radius: 6px; + font-size: 18px; + height: 46px; + line-height: 1.3333333; + padding: 10px 31px 10px 16px; + /* 1 */ +} +.select2-container--bootstrap .select2-selection--single.input-lg .select2-selection__arrow, .input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow, .form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow { + width: 5px; +} +.select2-container--bootstrap .select2-selection--single.input-lg .select2-selection__arrow b, .input-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b, .form-group-lg .select2-container--bootstrap .select2-selection--single .select2-selection__arrow b { + border-width: 5px 5px 0 5px; + margin-left: -5px; + margin-left: -10px; + margin-top: -2.5px; +} +.select2-container--bootstrap .select2-selection--multiple.input-lg, .input-group-lg .select2-container--bootstrap .select2-selection--multiple, .form-group-lg .select2-container--bootstrap .select2-selection--multiple { + min-height: 46px; + border-radius: 6px; +} +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-selection__choice, .input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice, .form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__choice { + font-size: 18px; + line-height: 1.3333333; + border-radius: 4px; + margin: 9px 0 0 8px; + padding: 0 10px; +} +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-search--inline .select2-search__field, .input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field, .form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-search--inline .select2-search__field { + padding: 0 16px; + font-size: 18px; + height: 44px; + line-height: 1.3333333; +} +.select2-container--bootstrap .select2-selection--multiple.input-lg .select2-selection__clear, .input-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear, .form-group-lg .select2-container--bootstrap .select2-selection--multiple .select2-selection__clear { + margin-top: 10px; +} +.select2-container--bootstrap .select2-selection.input-lg.select2-container--open .select2-selection--single { + /** + * Make the dropdown arrow point up while the dropdown is visible. + */ +} +.select2-container--bootstrap .select2-selection.input-lg.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #999 transparent; + border-width: 0 5px 5px 5px; +} +.input-group-lg .select2-container--bootstrap .select2-selection.select2-container--open .select2-selection--single { + /** + * Make the dropdown arrow point up while the dropdown is visible. + */ +} +.input-group-lg .select2-container--bootstrap .select2-selection.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #999 transparent; + border-width: 0 5px 5px 5px; +} +.select2-container--bootstrap[dir="rtl"] { + /** + * Single Select2 + * + * 1. Makes sure that .select2-selection__placeholder is positioned + * correctly. + */ + /** + * Multiple Select2 + */ +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--single { + padding-left: 24px; + padding-right: 12px; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__rendered { + padding-right: 0; + padding-left: 0; + text-align: right; + /* 1 */ +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow { + left: 12px; + right: auto; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--single .select2-selection__arrow b { + margin-left: 0; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice, +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder { + float: right; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 0; + margin-right: 6px; +} +.select2-container--bootstrap[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; +} + +/*------------------------------------*\ + #ADDITIONAL GOODIES +\*------------------------------------*/ +/** + * Address Bootstrap's validation states + * + * If a Select2 widget parent has one of Bootstrap's validation state modifier + * classes, adjust Select2's border colors and focus states accordingly. + * You may apply said classes to the Select2 dropdown (body > .select2-container) + * via JavaScript match Bootstraps' to make its styles match. + * + * @see http://getbootstrap.com/css/#forms-control-validation + */ +.has-warning .select2-dropdown, +.has-warning .select2-selection { + border-color: #8a6d3b; +} +.has-warning .select2-container--focus .select2-selection, +.has-warning .select2-container--open .select2-selection { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + border-color: #66512c; +} +.has-warning.select2-drop-active { + border-color: #66512c; +} +.has-warning.select2-drop-active.select2-drop.select2-drop-above { + border-top-color: #66512c; +} + +.has-error .select2-dropdown, +.has-error .select2-selection { + border-color: #a94442; +} +.has-error .select2-container--focus .select2-selection, +.has-error .select2-container--open .select2-selection { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + border-color: #843534; +} +.has-error.select2-drop-active { + border-color: #843534; +} +.has-error.select2-drop-active.select2-drop.select2-drop-above { + border-top-color: #843534; +} + +.has-success .select2-dropdown, +.has-success .select2-selection { + border-color: #3c763d; +} +.has-success .select2-container--focus .select2-selection, +.has-success .select2-container--open .select2-selection { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + border-color: #2b542c; +} +.has-success.select2-drop-active { + border-color: #2b542c; +} +.has-success.select2-drop-active.select2-drop.select2-drop-above { + border-top-color: #2b542c; +} + +/** + * Select2 widgets in Bootstrap Input Groups + * + * When Select2 widgets are combined with other elements using Bootstraps + * "Input Group" component, we don't want specific edges of the Select2 + * container to have a border-radius. + * + * Use .select2-bootstrap-prepend and .select2-bootstrap-append on + * a Bootstrap 3 .input-group to let the contained Select2 widget know which + * edges should not be rounded as they are directly followed by another element. + * + * @see http://getbootstrap.com/components/#input-groups + */ +/** + * Mimick Bootstraps .input-group .form-control styles. + * + * @see https://github.com/twbs/bootstrap/blob/master/less/input-groups.less + */ +.input-group .select2-container--bootstrap { + display: table; + table-layout: fixed; + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; + /** + * Adjust z-index like Bootstrap does to show the focus-box-shadow + * above appended buttons in .input-group and .form-group. + */ +} +.input-group .select2-container--bootstrap.select2-container--open, .input-group .select2-container--bootstrap.select2-container--focus { + z-index: 3; +} + +.input-group.select2-bootstrap-prepend .select2-container--bootstrap .select2-selection { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.input-group.select2-bootstrap-append .select2-container--bootstrap .select2-selection { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} + +/** + * Adjust alignment of Bootstrap buttons in Bootstrap Input Groups to address + * Multi Select2's height which - depending on how many elements have been selected - + * may grow taller than its initial size. + * + * @see http://getbootstrap.com/components/#input-groups + */ +.select2-bootstrap-append .select2-container--bootstrap, +.select2-bootstrap-append .input-group-btn, +.select2-bootstrap-append .input-group-btn .btn, +.select2-bootstrap-prepend .select2-container--bootstrap, +.select2-bootstrap-prepend .input-group-btn, +.select2-bootstrap-prepend .input-group-btn .btn { + vertical-align: top; +} + +/** + * Temporary fix for https://github.com/select2/select2-bootstrap-theme/issues/9 + * + * Provides `!important` for certain properties of the class applied to the + * original ` +
+ +
+ +
+
diff --git a/modules/ServerAPI/src/client/Experiment.coffee b/modules/ServerAPI/src/client/Experiment.coffee index d05c07530..322692c97 100644 --- a/modules/ServerAPI/src/client/Experiment.coffee +++ b/modules/ServerAPI/src/client/Experiment.coffee @@ -143,11 +143,31 @@ class window.Experiment extends BaseEntity attribute: 'recordedDate' message: attrs.subclass+" date must be set" if attrs.subclass? - notebook = @getNotebook().get('stringValue') - if notebook is "" or notebook is undefined or notebook is null - errors.push - attribute: 'notebook' - message: "Notebook must be set" + saveNotebook = true #default + if window.conf.entity?.notebook?.save? + saveNotebook= window.conf.entity.notebook.save + requireNotebook = true #default + if window.conf.entity?.notebook?.require? + requireNotebook= window.conf.entity.notebook.require + if saveNotebook and requireNotebook + notebook = @getNotebook().get('stringValue') + if notebook is "" or notebook is undefined or notebook is null + errors.push + attribute: 'notebook' + message: "Notebook must be set" + saveNotebookPage = true #default + if window.conf.entity?.notebookPage?.save? + saveNotebookPage = window.conf.entity.notebookPage.save + requireNotebookPage = false #default + if window.conf.entity?.notebookPage?.require? + requireNotebookPage= window.conf.entity.notebookPage.require + if saveNotebookPage and requireNotebookPage + notebookPage = @getNotebookPage().get('stringValue') + if notebookPage is "" or notebookPage is undefined or notebookPage is null + errors.push + attribute: 'notebookPage' + message: "Notebook Page must be set" + scientist = @getScientist().get('codeValue') if scientist is "unassigned" or scientist is undefined or scientist is "" or scientist is null errors.push diff --git a/modules/ServerAPI/src/client/Experiment.html b/modules/ServerAPI/src/client/Experiment.html index 2e8516ce5..7ff0eaebb 100644 --- a/modules/ServerAPI/src/client/Experiment.html +++ b/modules/ServerAPI/src/client/Experiment.html @@ -70,13 +70,13 @@
- +
- +
diff --git a/modules/ServerAPI/src/client/Protocol.html b/modules/ServerAPI/src/client/Protocol.html index c8811517a..ade4c32cc 100644 --- a/modules/ServerAPI/src/client/Protocol.html +++ b/modules/ServerAPI/src/client/Protocol.html @@ -59,13 +59,13 @@
- +
- +
diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index 7b7b61fc5..377992073 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -172,7 +172,7 @@ updateExpt = (expt, testMode, callback) -> callback "saveFailed" ) -postExperiment = (exptToSave, testMode, callback) -> +exports.postExperimentInternal = (exptToSave, testMode, callback) -> console.log "posting experiment" serverUtilityFunctions = require './ServerUtilityFunctions.js' # exptToSave = req.body @@ -301,7 +301,7 @@ exports.postExperiment = (req, resp) -> newExptExptItxs = JSON.parse newExptExptItxs delete req.body.newExptExptItxs - postExperiment req.body, req.query.testMode, (response) => + exports.postExperimentInternal req.body, req.query.testMode, (response) => if response.codeName? and (newExptExptItxs.length > 0 or exptExptItxsToIgnore.length > 0) if response.lsKind is "study" _.each exptExptItxsToIgnore, (itx) => @@ -1022,7 +1022,7 @@ exports.postParentExperiment = (req, resp) -> #post parent experiment parentExperiment = JSON.parse req.body.parentExperiment - postExperiment parentExperiment, req.query.testMode, (saveParentExptResp) => + exports.postExperimentInternal parentExperiment, req.query.testMode, (saveParentExptResp) => console.log "post experiment response" console.log saveParentExptResp if typeof saveParentExptResp is "string" and saveParentExptResp.indexOf("saveFailed") > -1 From c294fb3252e0a11f5a61236e96d77d58ae6bd5f3 Mon Sep 17 00:00:00 2001 From: evagao Date: Wed, 10 May 2017 10:46:49 -0700 Subject: [PATCH 006/576] Added configs for showing/requiring notebook and notebook page attributes --- conf/config.properties.example | 7 ++ .../ServerAPI/src/client/BaseEntity.coffee | 65 +++++++++++++++++-- modules/ServerAPI/src/client/BaseEntity.html | 8 ++- .../ServerAPI/src/client/Experiment.coffee | 30 +++++++-- modules/ServerAPI/src/client/Experiment.html | 4 +- modules/ServerAPI/src/client/Protocol.html | 4 +- .../routes/ExperimentServiceRoutes.coffee | 6 +- 7 files changed, 104 insertions(+), 20 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index bc0752ea4..5fee97466 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -389,6 +389,13 @@ client.curvefit.modelfitparameter.classes=[{"code":"4 parameter D-R", "parameter # client.experiment.lockwhenapproved.filter=[{"lsType": "Bio Activity"}] +#Configs for whether notebook attributes for protocols and experiments should be visible and/or required +#Note that notebook page can't be visible if notebook is not visible and page can't be required if notebook is not required +client.entity.notebook.save=true +client.entity.notebook.require=true +client.entity.notebookPage.save=true +client.entity.notebookPage.require=false + # Protocols and experiments which should be hidden (to non-admin users) in the protocol/expt browser when the status is in the config below client.entity.hideStatuses=["deleted"] diff --git a/modules/ServerAPI/src/client/BaseEntity.coffee b/modules/ServerAPI/src/client/BaseEntity.coffee index 2ebdc270b..8b842b735 100644 --- a/modules/ServerAPI/src/client/BaseEntity.coffee +++ b/modules/ServerAPI/src/client/BaseEntity.coffee @@ -144,11 +144,32 @@ class window.BaseEntity extends Backbone.Model attribute: 'recordedDate' message: attrs.subclass+" date must be set" if attrs.subclass? - notebook = @getNotebook().get('stringValue') - if notebook is "" or notebook is undefined or notebook is null - errors.push - attribute: 'notebook' - message: "Notebook must be set" + saveNotebook = true #default + if window.conf.entity?.notebook?.save? + saveNotebook= window.conf.entity.notebook.save + requireNotebook = true #default + if window.conf.entity?.notebook?.require? + requireNotebook= window.conf.entity.notebook.require + if saveNotebook and requireNotebook + notebook = @getNotebook().get('stringValue') + if notebook is "" or notebook is undefined or notebook is null + errors.push + attribute: 'notebook' + message: "Notebook must be set" + + saveNotebookPage = true #default + if window.conf.entity?.notebookPage?.save? + saveNotebookPage = window.conf.entity.notebookPage.save + requireNotebookPage = false #default + if window.conf.entity?.notebookPage?.require? + requireNotebookPage= window.conf.entity.notebookPage.require + if saveNotebookPage and requireNotebookPage + notebookPage = @getNotebookPage().get('stringValue') + if notebookPage is "" or notebookPage is undefined or notebookPage is null + errors.push + attribute: 'notebookPage' + message: "Notebook Page must be set" + scientist = @getScientist().get('codeValue') if scientist is "unassigned" or scientist is undefined or scientist is "" or scientist is null errors.push @@ -278,8 +299,38 @@ class window.BaseEntityController extends AbstractThingFormController #TODO: che @$('.bv_'+subclass+'Kind').html(@model.get('lsKind')) #should get value from protocol create form @$('.bv_details').val(@model.getDetails().get('clobValue')) @$('.bv_comments').val(@model.getComments().get('clobValue')) - @$('.bv_notebook').val @model.getNotebook().get('stringValue') - @$('.bv_notebookPage').val @model.getNotebookPage().get('stringValue') + saveNotebook = true #default + if window.conf.entity?.notebook?.save? + saveNotebook= window.conf.entity.notebook.save + requireNotebook = true #default + if window.conf.entity?.notebook?.require? + requireNotebook= window.conf.entity.notebook.require + if saveNotebook + @$('.bv_notebook').val @model.getNotebook().get('stringValue') + if requireNotebook + console.log "require notebook" + @$('.bv_notebookLabel').html "*Notebook" + saveNotebookPage = true #default + if window.conf.entity?.notebookPage?.save? + saveNotebookPage = window.conf.entity.notebookPage.save + requireNotebookPage = false #default + if window.conf.entity?.notebookPage?.require? + requireNotebookPage= window.conf.entity.notebookPage.require + if saveNotebookPage + @$('.bv_notebookPage').val @model.getNotebookPage().get('stringValue') + if requireNotebookPage + @$('.bv_notebookPageLabel').html "*Notebook Page" + else + @$('.bv_notebookPageLabel').html "Notebook Page" + else + @$('.bv_group_notebookPage').hide() + else + @$('.bv_notebookLabel').html "Notebook" + + else + @$('.bv_group_notebook').hide() + @$('.bv_group_notebookPage').hide() + @$('.bv_status').val(@model.getStatus().get('codeValue')) if @model.isNew() @$('.bv_save').html("Save") diff --git a/modules/ServerAPI/src/client/BaseEntity.html b/modules/ServerAPI/src/client/BaseEntity.html index 646d2cd2c..446c5c110 100644 --- a/modules/ServerAPI/src/client/BaseEntity.html +++ b/modules/ServerAPI/src/client/BaseEntity.html @@ -51,11 +51,17 @@
- +
+
+ +
+ +
+
diff --git a/modules/ServerAPI/src/client/Experiment.coffee b/modules/ServerAPI/src/client/Experiment.coffee index d05c07530..322692c97 100644 --- a/modules/ServerAPI/src/client/Experiment.coffee +++ b/modules/ServerAPI/src/client/Experiment.coffee @@ -143,11 +143,31 @@ class window.Experiment extends BaseEntity attribute: 'recordedDate' message: attrs.subclass+" date must be set" if attrs.subclass? - notebook = @getNotebook().get('stringValue') - if notebook is "" or notebook is undefined or notebook is null - errors.push - attribute: 'notebook' - message: "Notebook must be set" + saveNotebook = true #default + if window.conf.entity?.notebook?.save? + saveNotebook= window.conf.entity.notebook.save + requireNotebook = true #default + if window.conf.entity?.notebook?.require? + requireNotebook= window.conf.entity.notebook.require + if saveNotebook and requireNotebook + notebook = @getNotebook().get('stringValue') + if notebook is "" or notebook is undefined or notebook is null + errors.push + attribute: 'notebook' + message: "Notebook must be set" + saveNotebookPage = true #default + if window.conf.entity?.notebookPage?.save? + saveNotebookPage = window.conf.entity.notebookPage.save + requireNotebookPage = false #default + if window.conf.entity?.notebookPage?.require? + requireNotebookPage= window.conf.entity.notebookPage.require + if saveNotebookPage and requireNotebookPage + notebookPage = @getNotebookPage().get('stringValue') + if notebookPage is "" or notebookPage is undefined or notebookPage is null + errors.push + attribute: 'notebookPage' + message: "Notebook Page must be set" + scientist = @getScientist().get('codeValue') if scientist is "unassigned" or scientist is undefined or scientist is "" or scientist is null errors.push diff --git a/modules/ServerAPI/src/client/Experiment.html b/modules/ServerAPI/src/client/Experiment.html index 2e8516ce5..7ff0eaebb 100644 --- a/modules/ServerAPI/src/client/Experiment.html +++ b/modules/ServerAPI/src/client/Experiment.html @@ -70,13 +70,13 @@
- +
- +
diff --git a/modules/ServerAPI/src/client/Protocol.html b/modules/ServerAPI/src/client/Protocol.html index c8811517a..ade4c32cc 100644 --- a/modules/ServerAPI/src/client/Protocol.html +++ b/modules/ServerAPI/src/client/Protocol.html @@ -59,13 +59,13 @@
- +
- +
diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index 7b7b61fc5..377992073 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -172,7 +172,7 @@ updateExpt = (expt, testMode, callback) -> callback "saveFailed" ) -postExperiment = (exptToSave, testMode, callback) -> +exports.postExperimentInternal = (exptToSave, testMode, callback) -> console.log "posting experiment" serverUtilityFunctions = require './ServerUtilityFunctions.js' # exptToSave = req.body @@ -301,7 +301,7 @@ exports.postExperiment = (req, resp) -> newExptExptItxs = JSON.parse newExptExptItxs delete req.body.newExptExptItxs - postExperiment req.body, req.query.testMode, (response) => + exports.postExperimentInternal req.body, req.query.testMode, (response) => if response.codeName? and (newExptExptItxs.length > 0 or exptExptItxsToIgnore.length > 0) if response.lsKind is "study" _.each exptExptItxsToIgnore, (itx) => @@ -1022,7 +1022,7 @@ exports.postParentExperiment = (req, resp) -> #post parent experiment parentExperiment = JSON.parse req.body.parentExperiment - postExperiment parentExperiment, req.query.testMode, (saveParentExptResp) => + exports.postExperimentInternal parentExperiment, req.query.testMode, (saveParentExptResp) => console.log "post experiment response" console.log saveParentExptResp if typeof saveParentExptResp is "string" and saveParentExptResp.indexOf("saveFailed") > -1 From fde9338c31ef2e2896596c6b606ffe54b1f4b25b Mon Sep 17 00:00:00 2001 From: goshiro2013 Date: Fri, 19 May 2017 12:23:59 -0700 Subject: [PATCH 007/576] add chemDoodle json call --- .../src/client/ACASFormChemicalStructure.coffee | 7 +++++++ modules/Components/src/client/ACASFormFields.coffee | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormChemicalStructure.coffee b/modules/Components/src/client/ACASFormChemicalStructure.coffee index ba9333fb0..b523b3667 100644 --- a/modules/Components/src/client/ACASFormChemicalStructure.coffee +++ b/modules/Components/src/client/ACASFormChemicalStructure.coffee @@ -21,6 +21,10 @@ class window.ACASFormChemicalStructureExampleController extends Backbone.View setMol: (molStr) => @sketcher.setMol molStr + getChemDoodleJSON: => + mol = @sketcher.getChemDoodleJSON() + alert mol + class window.ACASFormChemicalStructureController extends Backbone.View tagName: "DIV" template: _.template($("#ACASFormChemicalStructureControllerView").html()) @@ -52,6 +56,9 @@ class window.ACASFormChemicalStructureController extends Backbone.View molStruct = @windowObj.ChemDoodle.readMOL molStr @windowObj.sketcher.loadMolecule(molStruct); + getChemDoodleJSON: -> + mol = @windowObj.sketcher.getMolecule(); + @windowObj.ChemDoodle.writeJSON([mol],[]) #TODO Why is it making a call to WebHQ outside our server, and make it stop diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 78042ffb0..58728900c 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -201,6 +201,10 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField value: null ignored: true + setInputValue: (inputValue) -> + @$('input').val inputValue + + renderModelContent: => @$('input').val @getModel().get('value') if @getModel().has 'unitKind' @@ -345,7 +349,7 @@ class window.ACASFormLSThingInteractionFieldController extends ACASFormAbstractF else opts.thingType = @thingType opts.thingKind = @thingKind - @thingSelectController = new ThingLabelComboBoxController opts + @thingSelectController = new ThingLabelComboBoxController opt @thingSelectController.render() render: => @@ -444,6 +448,10 @@ class window.ACASFormLSStringValueFieldController extends ACASFormAbstractFieldC value: null ignored: true + setInputValue: (inputValue) -> + @$('input').val inputValue + + renderModelContent: => @$('input').val @getModel().get('value') super() From 0493d732479270becacff59a9f4943017ee00fd9 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Mon, 22 May 2017 11:53:43 -0700 Subject: [PATCH 008/576] Added routes to bulk post/put experiments, refactored post/put experiment routes to only be for base experiments, changed picklistselect2controller to allow user to define width of picklist and to not have an automatic first option --- modules/Components/src/client/PickList.coffee | 12 +- .../routes/ExperimentServiceRoutes.coffee | 328 ++++-------------- 2 files changed, 69 insertions(+), 271 deletions(-) diff --git a/modules/Components/src/client/PickList.coffee b/modules/Components/src/client/PickList.coffee index 76bfa8c79..37cd72780 100644 --- a/modules/Components/src/client/PickList.coffee +++ b/modules/Components/src/client/PickList.coffee @@ -228,6 +228,14 @@ class window.ComboBoxController extends PickListSelectController class window.PickListSelect2Controller extends PickListSelectController + initialize: => + if @options.width? + @width = @options.width + else + @width = "100%" + + super() + render: => # convert model objects to array of json objects which have 'id' and 'text' properties mappedData = [] @@ -242,7 +250,7 @@ class window.PickListSelect2Controller extends PickListSelectController data: mappedData openOnEnter: false allowClear: true - width: "100%" + width: @width @setSelectedCode @selectedCode @rendered = true @@ -256,7 +264,7 @@ class window.PickListSelect2Controller extends PickListSelectController result = $(@el).val() # if result is null then we'll return the "unassigned" instead if it # was inserted as the first option - if not result? and @insertFirstOption.get('code') is "unassigned" + if not result? and @insertFirstOption != false and @insertFirstOption.get('code') is "unassigned" result = "unassigned" result diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index 377992073..2b5f4e290 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -21,6 +21,8 @@ exports.setupAPIRoutes = (app) -> app.get '/api/experiments/genericSearch/:searchTerm', exports.genericExperimentSearch app.get '/api/experiments/:lsType/:lsKind', exports.experimentsByTypeKind app.post '/api/experiments/getExperimentCodeByLabel/:exptType/:exptKind', exports.getExperimentCodeByLabel + app.post '/api/bulkPostExperiments', exports.bulkPostExperiments + app.put '/api/bulkPostExperiments', exports.bulkPutExperiments exports.setupRoutes = (app, loginRoutes) -> app.get '/api/experiments/codename/:code', loginRoutes.ensureAuthenticated, exports.experimentByCodename @@ -46,6 +48,8 @@ exports.setupRoutes = (app, loginRoutes) -> app.put '/api/experiments/parentExperiment/:id', loginRoutes.ensureAuthenticated, exports.putParentExperiment app.get '/api/experiments/:lsType/:lsKind', loginRoutes.ensureAuthenticated, exports.experimentsByTypeKind app.post '/api/experiments/getExperimentCodeByLabel/:exptType/:exptKind', loginRoutes.ensureAuthenticated, exports.getExperimentCodeByLabel + app.post '/api/bulkPostExperiments', loginRoutes.ensureAuthenticated, exports.bulkPostExperiments + app.put '/api/bulkPostExperiments', loginRoutes.ensureAuthenticated, exports.bulkPutExperiments serverUtilityFunctions = require './ServerUtilityFunctions.js' csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' @@ -303,21 +307,15 @@ exports.postExperiment = (req, resp) -> exports.postExperimentInternal req.body, req.query.testMode, (response) => if response.codeName? and (newExptExptItxs.length > 0 or exptExptItxsToIgnore.length > 0) - if response.lsKind is "study" - _.each exptExptItxsToIgnore, (itx) => - itx.firstExperiment = {id: response.id} - _.each newExptExptItxs, (itx) => - itx.firstExperiment = {id: response.id} - else #is default/expt base - _.each exptExptItxsToIgnore, (itx) => - itx.secondExperiment = {id: response.id} - _.each newExptExptItxs, (itx) => - itx.secondExperiment = {id: response.id} + _.each exptExptItxsToIgnore, (itx) => + itx.secondExperiment = {id: response.id} + _.each newExptExptItxs, (itx) => + itx.secondExperiment = {id: response.id} console.log "exptExptItxsToIgnore" console.log exptExptItxsToIgnore console.log "newExptExptItxs" console.log JSON.stringify newExptExptItxs - createAndUpdateExptExptItxs JSON.stringify(exptExptItxsToIgnore), JSON.stringify(newExptExptItxs), req.query.testMode, (json) => + exports.createAndUpdateExptExptItxsInternal JSON.stringify(exptExptItxsToIgnore), JSON.stringify(newExptExptItxs), req.query.testMode, (json) => console.log "finished createAndUpdateExptExptItxs" console.log exptExptItxsToIgnore console.log newExptExptItxs @@ -326,46 +324,6 @@ exports.postExperiment = (req, resp) -> console.log "error creating and updating expt expt itxs" resp.statusCode = 500 resp.json "error creating and updating expt expt itxs" - else if response.lsKind is "default" - #if study step itx(s) was ignored/created, remove/add the study step(s) - #remove study step for ignored itx - studyStepItxToIgnore = _.filter exptExptItxsToIgnore, (itx) => - itx.lsType is "has member" and itx.lsKind is "parent_study child" - studyStepItxToCreate = _.filter newExptExptItxs, (itx) => - itx.lsType is "has member" and itx.lsKind is "parent_study child" - user = response.recordedBy - if studyStepItxToIgnore.length is 1 #only expect max of 1 study step itx to be affected - studyExptCodeNameToIgnoreStep = _.pluck(studyStepItxToIgnore, 'firstExperiment')[0].codeName - studyExptCodeNameToCreateStep = _.pluck(newExptExptItxs, 'firstExperiment')[0].codeName - secondExptCodeName = response.codeName - ignoreStudyStepInStudyExpt studyExptCodeNameToIgnoreStep, secondExptCodeName, (message1) => - if message1.indexOf 'Failed' > -1 - resp.statusCode = 500 - resp.json message1 - else if newExptExptItxs.length is 1 - createStudyStepInStudyExpt studyExptCodeNameToCreateStep, secondExptCodeName, user, (message2) => - if message2.indexOf 'Failed' > -1 - resp.statusCode = 500 - resp.json message2 - else - resp.json response - else - resp.json response - else if studyStepItxToCreate.length is 1 - studyExptCodeNameToCreateStep = _.pluck(newExptExptItxs, 'firstExperiment')[0].codeName - secondExptCodeName = response.codeName - console.log "first expt to create study step" - console.log _.pluck(studyStepItxToCreate,'firstExperiment')[0] - createStudyStepInStudyExpt studyExptCodeNameToCreateStep, secondExptCodeName, user, (message2) => - console.log "createStudyStepInStudyExpt - done" - console.log message2 - if message2.indexOf('Failed')> -1 - resp.statusCode = 500 - resp.json message2 - else - resp.json response - else - resp.json response else resp.json response else @@ -399,13 +357,15 @@ exports.putExperiment = (req, resp) -> console.log "put experiment" #remove the itxs attributes if needed - newExptExptItxs = "[]" - exptExptItxsToIgnore = "[]" + newExptExptItxs = [] + exptExptItxsToIgnore = [] if req.body.exptExptItxsToIgnore? exptExptItxsToIgnore = req.body.exptExptItxsToIgnore + exptExptItxsToIgnore = JSON.parse exptExptItxsToIgnore delete req.body.exptExptItxsToIgnore if req.body.newExptExptItxs? newExptExptItxs = req.body.newExptExptItxs + newExptExptItxs = JSON.parse newExptExptItxs delete req.body.newExptExptItxs exptToSave = req.body @@ -414,25 +374,13 @@ exports.putExperiment = (req, resp) -> completeExptUpdate = -> updateExpt exptToSave, req.query.testMode, (updatedExpt) -> - if updatedExpt.codeName? and (JSON.parse(newExptExptItxs).length > 0 or JSON.parse(exptExptItxsToIgnore).length > 0) - exptExptItxsToIgnore = JSON.parse exptExptItxsToIgnore - if updatedExpt.lsKind is "study" - _.each exptExptItxsToIgnore, (itx) => - itx.firstExperiment = {id: updatedExpt.id} - newExptExptItxs = JSON.parse newExptExptItxs - _.each newExptExptItxs, (itx) => - itx.firstExperiment = {id: updatedExpt.id} - else #is default/expt base - _.each exptExptItxsToIgnore, (itx) => - itx.secondExperiment = {id: updatedExpt.id} - newExptExptItxs = JSON.parse newExptExptItxs - _.each newExptExptItxs, (itx) => - itx.secondExperiment = {id: updatedExpt.id} - console.log "exptExptItxsToIgnore" - console.log JSON.stringify exptExptItxsToIgnore - console.log "newExptExptItxs" - console.log JSON.stringify newExptExptItxs - createAndUpdateExptExptItxs JSON.stringify(exptExptItxsToIgnore), JSON.stringify(newExptExptItxs), req.query.testMode, (json) => + if updatedExpt.codeName? and (newExptExptItxs.length > 0 or exptExptItxsToIgnore.length > 0) + #is default/expt base + _.each exptExptItxsToIgnore, (itx) => + itx.secondExperiment = {id: updatedExpt.id} + _.each newExptExptItxs, (itx) => + itx.secondExperiment = {id: updatedExpt.id} + exports.createAndUpdateExptExptItxsInternal JSON.stringify(exptExptItxsToIgnore), JSON.stringify(newExptExptItxs), req.query.testMode, (json) => console.log "finished createAndUpdateExptExptItxs" console.log exptExptItxsToIgnore console.log newExptExptItxs @@ -441,42 +389,6 @@ exports.putExperiment = (req, resp) -> console.log "error creating and updating expt expt itxs" resp.statusCode = 500 resp.json "error creating and updating expt expt itxs" - else if updatedExpt.lsKind is "default" - #if study step itx(s) was ignored/created, remove/add the study step(s) - #remove study step for ignored itx - studyStepItxToIgnore = _.filter exptExptItxsToIgnore, (itx) => - itx.lsType is "has member" and itx.lsKind is "parent_study child" - studyStepItxToCreate = _.filter newExptExptItxs, (itx) => - itx.lsType is "has member" and itx.lsKind is "parent_study child" - if studyStepItxToIgnore.length is 1 #only expect max of 1 study step itx to be affected - studyExptCodeNameToIgnoreStep = _.pluck(studyStepItxToIgnore, 'firstExperiment')[0].codeName - studyExptCodeNameToCreateStep = _.pluck(newExptExptItxs, 'firstExperiment')[0].codeName - secondExptCodeName = updatedExpt.codeName - user = updatedExpt.recordedBy - ignoreStudyStepInStudyExpt studyExptCodeNameToIgnoreStep, secondExptCodeName, (message1) => - if message1.indexOf('Failed') > -1 - resp.statusCode = 500 - resp.json message1 - else if newExptExptItxs.length is 1 - createStudyStepInStudyExpt studyExptCodeNameToCreateStep, secondExptCodeName, user, (message2) => - if message2.indexOf('Failed') > -1 - resp.statusCode = 500 - resp.json message2 - else - resp.json updatedExpt - else - resp.json updatedExpt - else if studyStepItxToCreate.length is 1 - studyExptCodeNameToCreateStep = _.pluck(newExptExptItxs, 'firstExperiment')[0].codeName - secondExptCodeName = updatedExpt.codeName - createStudyStepInStudyExpt studyExptCodeNameToCreateStep, secondExptCodeName, user, (message2) => - if message2.indexOf('Failed') > -1 - resp.statusCode = 500 - resp.json message2 - else - resp.json updatedExpt - else - resp.json updatedExpt else resp.json updatedExpt else @@ -783,7 +695,7 @@ exports.putExptExptItxs = (req, resp) -> resp.statusCode = 500 resp.json updatedExptExptItxs -createAndUpdateExptExptItxs = (exptExptItxsToIgnore, newExptExptItxs, testMode, callback) -> +exports.createAndUpdateExptExptItxsInternal = (exptExptItxsToIgnore, newExptExptItxs, testMode, callback) -> putExptExptItxs exptExptItxsToIgnore, testMode, (updatedExptExptItxs) -> if updatedExptExptItxs.indexOf("saveFailed") > -1 callback updatedExptExptItxs @@ -795,167 +707,19 @@ createAndUpdateExptExptItxs = (exptExptItxsToIgnore, newExptExptItxs, testMode, callback updatedExptExptItxs.concat newExptExptItxs exports.createAndUpdateExptExptItxs = (req, resp) -> - createAndUpdateExptExptItxs req.body.exptExptItxsToIgnore, req.body.newExptExptItxs, req.query.testMode, (json) => + exports.createAndUpdateExptExptItxsInternal req.body.exptExptItxsToIgnore, req.body.newExptExptItxs, req.query.testMode, (json) => if json.indexOf("saveFailed") > -1 resp.statusCode = 500 resp.json json -ignoreStudyStepInStudyExpt = (firstExptCodeName, secondExptCodeName, callback) -> - console.log "ignoreStudyStepInStudyExpt" - console.log "firstExptCodeName: " + firstExptCodeName - console.log "secondExptCodeName: " + secondExptCodeName - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"experiments/codename/"+firstExptCodeName - request = require 'request' - request( - method: 'GET' - url: baseurl - json: true - , (error, response, firstExptJson) => - if !error && response.statusCode == 200 - #find state to ignore - lsStates = _.filter firstExptJson.lsStates, (state) => - !state.ignored and state.lsType is "study tracking" and state.lsKind is "study steps" - stepToIgnore = _.filter lsStates, (state) => - filteredVals = _.filter state.lsValues, (val) => - val.lsType is 'codeValue' and val.lsKind is "step experiment" and val.codeValue is secondExptCodeName and !(val.ignored) - filteredVals.length > 0 - if stepToIgnore.length > 0 - stepToIgnore = stepToIgnore[0] - stepToIgnore.ignored = true - firstExptJson.lsStates = [stepToIgnore] - console.log "ignoreStudyStepInStudyExpt: step to ignore" - #put study expt - updateExpt firstExptJson, false, (updatedFirstExpt) => - if updatedFirstExpt.codeName? - #success, create new step for newly added step expt - #get study expt to add the study step - callback "success: ignored study step for old expt" - else - callback "Failed: error ignoring study step for study experiment" - else - callback "Failed: could not get study experiment to update" - ) - -createStudyStepInStudyExpt = (firstExptCodeName, secondExptCodeName, user, callback) -> - console.log "createStudyStepInStudyExpt" - console.log "firstExptCodeName: " + firstExptCodeName - console.log "secondExptCodeName: " + secondExptCodeName - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"experiments/codename/"+firstExptCodeName - request = require 'request' - request( - method: 'GET' - url: baseurl - json: true - , (error, response, firstExptJson) => - if !error && response.statusCode == 200 - lsStates = _.filter firstExptJson.lsStates, (state) => - !state.ignored and state.lsType is "study tracking" and state.lsKind is "study steps" - #find step order, filter for steps with uncategorized as step order - uncategorizedStates = _.filter lsStates, (state) => - filteredVals = _.filter state.lsValues, (val) => - val.lsType is 'stringValue' and val.lsKind is "step category" and val.stringValue is "Uncategorized" and !(val.ignored) - filteredVals.length > 0 - console.log "uncategorized states" - console.log JSON.stringify uncategorizedStates - stepOrder = uncategorizedStates.length - if uncategorizedStates.length > 0 - stepCategoryOrderVal = _.filter uncategorizedStates[0].lsValues, (val) => - val.lsType is 'numericValue' and val.lsKind is 'step category order' and !val.ignored - stepCategoryOrder = stepCategoryOrderVal[0].numericValue - else - #stepCategoryOrder = number of stepCategories - #group by stepCategory - groupedStepCategory = _.groupBy lsStates, (state) => - stepCategoryOrder = _.filter state.lsValues, (val) => - console.log "val - here" - console.log val - val.lsKind is 'step category order' and !val.ignored - stepCategoryOrder[0].numericValue - #find max key of returned object - console.log "groupedStepCategory" - console.log groupedStepCategory - stepCategoryOrder = Number(_.max Object.keys(groupedStepCategory))+1 - console.log "stepCategory Order: " - console.log stepCategoryOrder - firstExptJson.lsStates = [ - "ignored": false, - "lsKind": "study steps", - "lsType": "study tracking", - "lsValues": [ - { - "ignored": false, - "lsKind": "step order", - "lsType": "numericValue", - "numericValue": stepOrder, - "recordedBy": user, - "recordedDate": new Date().getTime(), - }, - { - "ignored": false, - "lsKind": "step category order", - "lsType": "numericValue", - "numericValue": stepCategoryOrder, - "recordedBy": user, - "recordedDate": new Date().getTime(), - }, - { - "ignored": false, - "lsKind": "step category", - "lsType": "stringValue", - "recordedBy": user, - "recordedDate": new Date().getTime(), - "stringValue": "Uncategorized", - }, - { - "ignored": false, - "lsKind": "step name", - "lsType": "stringValue", - "stringValue": secondExptCodeName - "recordedBy": user, - "recordedDate": new Date().getTime(), - }, - { - "ignored": false, - "lsKind": "step experiment", - "lsType": "codeValue", - "codeValue": secondExptCodeName - "recordedBy": user, - "recordedDate": new Date().getTime(), - } - ] - "recordedBy": user, - "recordedDate": new Date().getTime() - ] - - #put study expt - console.log "updatedFirstExpt - request" - console.log JSON.stringify firstExptJson -# console.log JSON.stringify firstExptJson2 - updateExpt firstExptJson, false, (updatedFirstExpt) => - console.log "updatedFirstExpt - response" - console.log JSON.stringify updatedFirstExpt - if updatedFirstExpt.codeName? - #success, create new step for newly added step expt - #get study expt to add the study step - callback "success: created new study step for study expt" - else - callback "Failed: error ignoring study step for study experiment" - else - callback "Failed: could not get study experiment to update" - ) - - - exports.experimentsByCodeNamesArray = (req, resp) -> - experimentsByCodeNamesArray req.body.data, req.query.option, req.query.testMode, (returnedExpts) -> + exports.experimentsByCodeNamesArrayInternal req.body.data, req.query.option, req.query.testMode, (returnedExpts) -> if returnedExpts.indexOf("Failed") > -1 resp.statusCode = 500 resp.json returnedExpts -experimentsByCodeNamesArray = (codeNamesArray, returnOption, testMode, callback) -> +exports.experimentsByCodeNamesArrayInternal = (codeNamesArray, returnOption, testMode, callback) -> if testMode or global.specRunnerTestmode callback JSON.stringify "stubsMode not implemented" else @@ -996,7 +760,7 @@ exports.getExptExptItxsToDisplay = (req, resp) -> exptExptItxs = _.filter JSON.parse(exptExptItxs), (itx) -> !itx.ignored secondExpts = _.pluck (_.pluck exptExptItxs, 'secondExperiment'), 'codeName' - experimentsByCodeNamesArray secondExpts, "stubwithprot", req.query.testMode, (returnedExpts) -> + exports.experimentsByCodeNamesArrayInternal secondExpts, "stubwithprot", req.query.testMode, (returnedExpts) -> #add returnedExpts information into the secondExperiment attribute _.each exptExptItxs, (itx) -> secondExptInfo = _.where(returnedExpts, experimentCodeName: itx.secondExperiment.codeName)[0] @@ -1045,7 +809,7 @@ exports.postParentExperiment = (req, resp) -> console.log childExperiments #bulk post of all the experiments - bulkPostExperiments childExperiments, (saveChildExptsResp) => + exports.bulkPostExperimentsInternal childExperiments, (saveChildExptsResp) => if typeof saveChildExptsResp is "string" and saveChildExptsResp.indexOf("saveFailed") > -1 resp.statusCode = 500 resp.json saveChildExptsResp @@ -1071,8 +835,11 @@ exports.postParentExperiment = (req, resp) -> #return resp.json saveParentExptResp +exports.bulkPostExperiments = (req, resp) -> + exports.bulkPostExperimentsInternal req.body, (response) => + resp.json response -bulkPostExperiments = (exptsArray, callback) -> +exports.bulkPostExperimentsInternal = (exptsArray, callback) -> console.log "bulkPostExperiments" console.log JSON.stringify exptsArray config = require '../conf/compiled/conf.js' @@ -1084,14 +851,37 @@ bulkPostExperiments = (exptsArray, callback) -> json: true , (error, response, json) => if !error && response.statusCode == 201 -#respond with the saved parent response - savedChildExperiments = json - console.log "savedChildExperiments" - console.log savedChildExperiments - callback savedChildExperiments + callback json else console.log "got error posting child experiments" - callback JSON.stringify "post child experiments saveFailed: " + JSON.stringify error + callback JSON.stringify "bulk post experiments saveFailed: " + JSON.stringify error + ) + +exports.bulkPutExperiments = (req, resp) -> + exports.bulkPutExperimentsInternal req.body, (response) => + resp.json response + +exports.bulkPutExperimentsInternal = (exptsArray, callback) -> + console.log "bulkPutExperiments" + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"/experiments/jsonArray" + console.log "bulkPutExperimentsInternal" + console.log baseurl + console.log exptsArray + request( + method: 'PUT' + url: baseurl + body: exptsArray + json: true + , (error, response, json) => + console.log "bulkPutExperimentsInternal" + console.log response.statusCode + if !error && response.statusCode == 200 + callback json + else + console.log "got error bulk updating experiments" + console.log error + callback JSON.stringify "bulk update experiments saveFailed: " + JSON.stringify error ) exports.putParentExperiment = (req, resp) -> From 6dccf7b27f94dd9d6fa2f7e126270fc27a7c6f3b Mon Sep 17 00:00:00 2001 From: Matthew Shaw Date: Wed, 24 May 2017 14:54:38 -0600 Subject: [PATCH 009/576] Updating how ACAS starts up so websockets can be used. Configured websockets to have access to the passport / connect session. --- app_api_template.coffee | 6 ++--- app_template.coffee | 25 +++++++++++++++---- .../src/server/PrepareModuleIncludes.coffee | 7 +++++- .../routes/RequiredClientScripts_template.js | 3 ++- package.json | 4 +++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app_api_template.coffee b/app_api_template.coffee index c6eae3270..48643f964 100644 --- a/app_api_template.coffee +++ b/app_api_template.coffee @@ -45,12 +45,12 @@ startApp = -> console.error 'Caught api exception: ' + err.stack return - ###TO_BE_REPLACED_BY_PREPAREMODULEINCLUDES### - - http.createServer(app).listen(app.get('port'), -> + httpServer = http.createServer(app).listen(app.get('port'), -> console.log("ACAS API server listening on port " + app.get('port')) ) + io = require('socket.io')(httpServer) + ###TO_BE_REPLACED_BY_PREPAREMODULEINCLUDES### csUtilities.logUsage("ACAS API server started", "started", "") diff --git a/app_template.coffee b/app_template.coffee index 555355120..5f1942e5f 100644 --- a/app_template.coffee +++ b/app_template.coffee @@ -57,7 +57,8 @@ startApp = -> # next() loginRoutes = require './routes/loginRoutes' - + MemoryStore = express.session.MemoryStore; + sessionStore = new MemoryStore(); global.app = express() app.configure -> app.set 'port', config.all.client.port @@ -70,6 +71,7 @@ startApp = -> app.use express.session secret: 'acas needs login' cookie: maxAge: 365 * 24 * 60 * 60 * 1000 + store: sessionStore # MemoryStore is used automatically if no "store" field is set, but we need a handle on the sessionStore object for Socket.IO, so we'll manually create the store so we have a handle on the object app.use flash() app.use passport.initialize() app.use passport.session pauseStream: true @@ -86,11 +88,11 @@ startApp = -> # index routes indexRoutes = require './routes/index.js' indexRoutes.setupRoutes(app, loginRoutes) - ###TO_BE_REPLACED_BY_PREPAREMODULEINCLUDES### + if not config.all.client.use.ssl - http.createServer(app).listen(app.get('port'), -> - console.log("Express server listening on port " + app.get('port')) + httpServer = http.createServer(app).listen(app.get('port'), -> + console.log("ACAS API server listening on port " + app.get('port')) ) else console.log "------ Starting in SSL Mode" @@ -101,11 +103,24 @@ startApp = -> cert: fs.readFileSync config.all.server.ssl.cert.file.path ca: fs.readFileSync config.all.server.ssl.cert.authority.file.path passphrase: config.all.server.ssl.cert.passphrase - https.createServer(sslOptions, app).listen(app.get('port'), -> + httpServer = https.createServer(sslOptions, app).listen(app.get('port'), -> console.log("Express server listening on port " + app.get('port')) ) #TODO hack to prevent bug: https://github.com/mikeal/request/issues/418 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" + io = require('socket.io')(httpServer) + bundle = require('socket.io-bundle') + passportSocketIo = require('passport.socketio') + cookieParser = require('cookie-parser') + + io.use(passportSocketIo.authorize({ + key: 'connect.sid', + secret: 'acas needs login', + store: sessionStore, + passport: passport, + cookieParser: cookieParser + })) + ###TO_BE_REPLACED_BY_PREPAREMODULEINCLUDES### options = if stubsMode then ["stubsMode"] else [] options.push ['--color'] diff --git a/modules/BuildUtilities/src/server/PrepareModuleIncludes.coffee b/modules/BuildUtilities/src/server/PrepareModuleIncludes.coffee index dc69d0562..d81ca69f1 100644 --- a/modules/BuildUtilities/src/server/PrepareModuleIncludes.coffee +++ b/modules/BuildUtilities/src/server/PrepareModuleIncludes.coffee @@ -110,8 +110,13 @@ prepRouteIncludes = (apiMode) -> if apiMode includeStr += '\tif (routeSet_'+routeNum+'.setupAPIRoutes) {\n' includeStr += '\t\trouteSet_'+routeNum+'.setupAPIRoutes(app); }\n' + includeStr += '\tif (routeSet_'+routeNum+'.setupChannels) {\n' + includeStr += '\trouteSet_'+routeNum+'.setupChannels(io, loginRoutes); }\n' else - includeStr += '\trouteSet_'+routeNum+'.setupRoutes(app, loginRoutes);\n' + includeStr += '\tif (routeSet_'+routeNum+'.setupRoutes) {\n' + includeStr += '\trouteSet_'+routeNum+'.setupRoutes(app, loginRoutes);\n }' + includeStr += '\tif (routeSet_'+routeNum+'.setupChannels) {\n' + includeStr += '\trouteSet_'+routeNum+'.setupChannels(io, loginRoutes);\n }' routeLines += includeStr routeNum++ diff --git a/modules/BuildUtilities/src/server/routes/RequiredClientScripts_template.js b/modules/BuildUtilities/src/server/routes/RequiredClientScripts_template.js index e19e354ee..bbee713e2 100644 --- a/modules/BuildUtilities/src/server/routes/RequiredClientScripts_template.js +++ b/modules/BuildUtilities/src/server/routes/RequiredClientScripts_template.js @@ -25,7 +25,8 @@ exports.requiredScripts = [ '/lib/spin/js/spin.js', '/lib/spin/js/jquery-spin.js', '/lib/handsontable/dist/handsontable.full.js', - '/lib/select2-4.0.3/dist/js/select2.full.js' + '/lib/select2-4.0.3/dist/js/select2.full.js', + '/socket.io/socket.io.js' ]; exports.applicationScripts = [ diff --git a/package.json b/package.json index ac9580f0f..858a8ee2e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "backbone": "^1.2.3", "backbone-validation": "^0.11.5", "chai": "^3.5.0", + "cookie-parser": "^1.4.3", "connect-flash": "~0.1.0", "cron": "*", "csv-parse": "^1.1.1", @@ -51,6 +52,9 @@ "properties-parser": "*", "request": "2.34.0", "shelljs": "0.6.0", + "socket.io": "^1.7.3", + "passport.socketio": "^3.7.0", + "socket.io-bundle": "^0.1.2 ", "style-loader": "*", "temporary": ">= 0.0.0", "underscore": ">= 0.0.0", From 2ebf553af2bebf0d8e86df23c4f9fc5d89f7f8e5 Mon Sep 17 00:00:00 2001 From: Matthew Shaw Date: Wed, 24 May 2017 16:48:15 -0600 Subject: [PATCH 010/576] Adding feature to allow user to change their user name. --- app_template.coffee | 2 + .../ModuleMenus/src/client/ModuleMenus.coffee | 34 ++++++++++++++++ .../src/client/ModuleMenusView.html | 25 ++++++++++++ .../server/routes/ModuleMenusRoutes.coffee | 40 +++++++++++++++++++ package.json | 1 + 5 files changed, 102 insertions(+) create mode 100644 modules/ModuleMenus/src/server/routes/ModuleMenusRoutes.coffee diff --git a/app_template.coffee b/app_template.coffee index 5f1942e5f..2e42ff394 100644 --- a/app_template.coffee +++ b/app_template.coffee @@ -112,6 +112,8 @@ startApp = -> bundle = require('socket.io-bundle') passportSocketIo = require('passport.socketio') cookieParser = require('cookie-parser') +# sharedsession = require("express-socket.io-session") +# io.use(sharedsession(express.session)) io.use(passportSocketIo.authorize({ key: 'connect.sid', diff --git a/modules/ModuleMenus/src/client/ModuleMenus.coffee b/modules/ModuleMenus/src/client/ModuleMenus.coffee index 292709773..9d89ff327 100644 --- a/modules/ModuleMenus/src/client/ModuleMenus.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenus.coffee @@ -5,6 +5,8 @@ class window.ModuleMenusController extends Backbone.View events: -> 'click .bv_headerName': "handleHome" 'click .bv_toggleModuleMenuControl': "handleToggleMenus" + 'click .bv_loginUserFirstName': 'handleLoginUserFirstNameClick' + 'click .bv_changeNameBtn': 'handleChangeNameBtnClick' # 'click .bv_showModuleMenuControl': "handleShowMenus" window.onbeforeunload = () -> @@ -58,6 +60,15 @@ class window.ModuleMenusController extends Backbone.View modLink = '
  • '+module.displayName+'
  • ' @$('.bv_externalACASModules').append modLink + @socket = io('/user:loggedin') + @socket.on('connect', @handleConnected) + @socket.on('connect_error', @handleConnectError) + @socket.on('loggedOn', @handleLoggedOn) + @socket.on('loggedOff', @handleLoggedOn) + @socket.on('usernameUpdated', @handleNameChanged) + + @disconnectedAfterLogin = false + render: => if window.AppLaunchParams.deployMode? unless window.AppLaunchParams.deployMode.toUpperCase() =="PROD" @@ -88,5 +99,28 @@ class window.ModuleMenusController extends Backbone.View @$('.bv_mainModuleWellWrapper').removeClass 'span9' @$('.bv_mainModuleWellWrapper').addClass 'span11' + handleConnected: => + console.log "handleConnected" + + handleConnectError: => + @disconnectedAfterLogin = true + console.log "handleConnectError" + + handleLoggedOn: (numberOfLogins) -> + console.log "you're loggedin in this many places: ", numberOfLogins + + handleLoggedOff: (numberOfLogins) -> + console.log "you're loggedin in this many places: ", numberOfLogins + + handleLoginUserFirstNameClick: => + @$(".bv_changeUserName").modal "show" + handleChangeNameBtnClick: (e) => + e.preventDefault() + firstName = @$(".bv_firstName").val() + @socket.emit('changeUserName', firstName) + @$(".bv_firstName").val('') + @$(".bv_changeUserName").modal "hide" + handleNameChanged: (updatedFirstName) => + @$(".bv_loginUserFirstName").html updatedFirstName diff --git a/modules/ModuleMenus/src/client/ModuleMenusView.html b/modules/ModuleMenus/src/client/ModuleMenusView.html index 5bbe8c10e..38dcc798b 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusView.html +++ b/modules/ModuleMenus/src/client/ModuleMenusView.html @@ -5,6 +5,7 @@ @@ -41,56 +42,16 @@

    Tare Single Vial

    -
    -
    - -
    -
    - -
    -
    diff --git a/modules/ServerAPI/src/server/ControllerRedirectConf.coffee b/modules/ServerAPI/src/server/ControllerRedirectConf.coffee index a5e432b2b..8f51b21b1 100644 --- a/modules/ServerAPI/src/server/ControllerRedirectConf.coffee +++ b/modules/ServerAPI/src/server/ControllerRedirectConf.coffee @@ -29,6 +29,11 @@ "project": deepLink: "project" relatedFilesRelativePath: "entities/projects" + THING: + entityName: "example thing" + "Example Thing": + deepLink: "example_thing" + relatedFilesRelativePath: "entities/exampleThings" PT: entityName: "parent thing" default: From 12db280ece09477420bf2463cefda2b07bfd5468 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Fri, 14 Jul 2017 09:09:12 -0700 Subject: [PATCH 100/576] cleanup log statements --- modules/Components/src/client/ACASFormMultiLabel.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/Components/src/client/ACASFormMultiLabel.coffee b/modules/Components/src/client/ACASFormMultiLabel.coffee index c75686fe4..d44f69e20 100644 --- a/modules/Components/src/client/ACASFormMultiLabel.coffee +++ b/modules/Components/src/client/ACASFormMultiLabel.coffee @@ -61,8 +61,6 @@ class window.ACASFormMultiLabelListController extends ACASFormAbstractFieldContr @addNewLabel() addNewLabel: (skipAmDirtyTrigger) => - console.log "addNewLabel" - console.log @opts keyBase = @modelKey newModel = new Label lsType: @opts.modelDefaults.type From 5c36ae1cbdffde3b8f97c2e4017555ea9f149461 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Fri, 14 Jul 2017 09:38:34 -0700 Subject: [PATCH 101/576] cleanup code --- modules/Components/src/client/ACASFormFields.coffee | 7 +++---- modules/Components/src/client/ACASFormFieldsView.html | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 473dd5ab9..64ce1cada 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -564,7 +564,7 @@ class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldCon @createNewFileChooser() @$('.bv_deleteSavedFile').hide() else - @$('.bv_File').html ''+@getModel().get('comments')+'' + @$('.bv_file').html ''+@getModel().get('comments')+'' @$('.bv_deleteSavedFile').show() createNewFileChooser: -> @@ -572,9 +572,8 @@ class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldCon @fileController.render() else @fileController = new LSFileChooserController - el: @$('.bv_File') - formId: 'fieldBlah', - maxNumberOfFiles: 1, + el: @$('.bv_file') + maxNumberOfFiles: 1 requiresValidation: false url: UtilityFunctions::getFileServiceURL() allowedFileTypes: @allowedFileTypes diff --git a/modules/Components/src/client/ACASFormFieldsView.html b/modules/Components/src/client/ACASFormFieldsView.html index c6ceb1808..4292def95 100644 --- a/modules/Components/src/client/ACASFormFieldsView.html +++ b/modules/Components/src/client/ACASFormFieldsView.html @@ -59,7 +59,7 @@ From d7b925916641ab978653dba9a654f9b5aafbee78 Mon Sep 17 00:00:00 2001 From: Marjorie Principato Date: Fri, 14 Jul 2017 11:56:39 -0700 Subject: [PATCH 102/576] Fixes #162. Makes it so that moduleMenus looks at a config parameter to tell what the module menus should be --- conf/config.properties.example | 1 + modules/ModuleMenus/src/client/ModuleMenus.coffee | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 431a6ad42..f9d39b7d0 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -324,6 +324,7 @@ server.security.authstrategy=database client.moduleMenus.logoText=ACAS client.moduleMenus.homePageMessage=Welcome to ACAS client.moduleMenus.copyrightMessage=© John McNeil & Company 2012-2017 +# client.moduleMenus.menuConfigurationSettings=ModuleMenusConfiguration # If summary statistics is an included feature, set the following to true client.moduleMenus.summaryStats=false # For navigation to external ACAS modules from ACAS homepage diff --git a/modules/ModuleMenus/src/client/ModuleMenus.coffee b/modules/ModuleMenus/src/client/ModuleMenus.coffee index 292709773..c5f2ecdc0 100644 --- a/modules/ModuleMenus/src/client/ModuleMenus.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenus.coffee @@ -17,7 +17,12 @@ class window.ModuleMenusController extends Backbone.View $(@el).html @template() - @moduleLauncherList = new ModuleLauncherList(@options.menuListJSON) + if window.conf.moduleMenus.menuConfigurationSettings? + menuListJSON = window[window.conf.moduleMenus.menuConfigurationSettings] + else + menuListJSON = @options.menuListJSON + + @moduleLauncherList = new ModuleLauncherList(menuListJSON) @moduleLauncherMenuListController = new ModuleLauncherMenuListController el: @$('.bv_modLaunchMenuWrapper') collection: @moduleLauncherList From b58b068f357107056adb225b6531046f0e231bd5 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 14 Jul 2017 13:12:38 -0700 Subject: [PATCH 103/576] Added default value of false for newly required config client.entity.saveInitialsCorpName. Fixes #164 --- conf/config.properties.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/config.properties.example b/conf/config.properties.example index 95851ae25..390fe3dfd 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -525,3 +525,6 @@ client.projectEditor.restrictedCheckbox.show=true client.projectEditor.isRestricted.default=false client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER + +# For whether protocols and experiments should have sequential user defined corpName labels +client.entity.saveInitialsCorpName=false From a63a7c541085ab256f07780d66f3bbbe01f00442 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Fri, 14 Jul 2017 15:09:04 -0700 Subject: [PATCH 104/576] feature/state_table_display: bug fixes for state display table, add option to configure whether cells in state display table are editable --- .../client/ACASFormStateDisplayUpdate.coffee | 39 ++++++++++++------- .../src/client/ACASFormStateDisplayView.html | 2 +- modules/Components/src/client/style.css | 4 ++ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee index fafde6332..95c3ead51 100644 --- a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee +++ b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee @@ -30,10 +30,13 @@ class window.ACASFormStateDisplayUpdateCellController extends Backbone.View else val = @collection.findWhere ignored: false content = val.get(val.get('lsType')) - if val.get('lsType') == 'dateValue' - content = UtilityFunctions::convertMSToYMDDate val.get('dateValue') + if val.get('lsType') == 'dateValue' + content = UtilityFunctions::convertMSToYMDDate val.get('dateValue') + $(@el).html content $(@el).addClass if @collection.length > 1 then "valueWasEdited" else "" + if @options.cellDef.editable? and !@options.cellDef.editable + $(@el).addClass 'valueNotEditable' @ @@ -95,6 +98,8 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View @$("tbody").append rowController.render().el rowController.on 'cellClicked', (values) => + if @currentCellEditor? + @currentCellEditor.undelegateEvents() @currentCellEditor = new ACASFormStateDisplayValueEditController collection: values el: @$('.bv_valueEditor') @@ -159,7 +164,7 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View modifiedDate: new Date().getTime() isDirty: true valInfo.newValue.set - recoerdedBy: window.AppLaunchParams.loginUser.username + recordedBy: window.AppLaunchParams.loginUser.username recordedDate: new Date().getTime() stateToUpdate.get('lsValues').add valInfo.newValue @@ -219,19 +224,22 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View render: => $(@el).empty() - $(@el).html @template() - @$('.bv_header').html "\"#{@valueDef.fieldSettings.formLabel}\" Value History and Edit" - @collection.comparator = 'id' - @collection.sort() - @collection.each (val) => - oldRow = new ACASFormStateDisplayOldValueController - model: val - @$("tbody").append oldRow.render().el - @setupEditor() - @$('.bv_aCASFormStateDisplayValueEdit').modal - backdrop: "static" - @$('.bv_aCASFormStateDisplayValueEdit').modal "show" + #only display editor if value is editable + if !@valueDef.editable? or (@valueDef.editable? and @valueDef.editable) + $(@el).html @template() + + @$('.bv_header').html "\"#{@valueDef.fieldSettings.formLabel}\" Value History and Edit" + @collection.comparator = 'id' + @collection.sort() + @collection.each (val) => + oldRow = new ACASFormStateDisplayOldValueController + model: val + @$("tbody").append oldRow.render().el + @setupEditor() + @$('.bv_aCASFormStateDisplayValueEdit').modal + backdrop: "static" + @$('.bv_aCASFormStateDisplayValueEdit').modal "show" reasonForUpdateChanged: -> @comment = @commentPrefix + @$('.bv_reasonForUpdate').val() @@ -270,6 +278,7 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View codeType: @valueDef.modelDefaults.codeType codeKind: @valueDef.modelDefaults.codeKind codeOrigin: @valueDef.modelDefaults.codeOrigin + @newValue.set 'value', currentValue.get(currentValue.get('lsType')) if currentValue? opts = modelKey: @valueDef.modelDefaults.kind diff --git a/modules/Components/src/client/ACASFormStateDisplayView.html b/modules/Components/src/client/ACASFormStateDisplayView.html index 806516371..b1f528c54 100644 --- a/modules/Components/src/client/ACASFormStateDisplayView.html +++ b/modules/Components/src/client/ACASFormStateDisplayView.html @@ -30,7 +30,7 @@

    launching controller must provide or set table label

    Value Changed By Changed Date - Reason + Reason For Correction diff --git a/modules/Components/src/client/style.css b/modules/Components/src/client/style.css index 3429cf280..31d237a74 100644 --- a/modules/Components/src/client/style.css +++ b/modules/Components/src/client/style.css @@ -44,4 +44,8 @@ .bv_stateDisplayUpdateTable .valueWasEdited { color: red; +} + +.bv_stateDisplayUpdateTable .valueNotEditable { + color: #9D9D9D; } \ No newline at end of file From b6e973e3214e7370da7ccbb6ee00dd3cea63e350 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 14 Jul 2017 16:48:34 -0700 Subject: [PATCH 105/576] Added clobValue support in StateTable --- modules/Components/src/client/ACASFormStateTable.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 2531b4446..8d4d2c3eb 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -280,6 +280,8 @@ class window.ACASFormStateTableController extends Backbone.View switch valueDef.modelDefaults.type when 'stringValue' value.set stringValue: if cellContent? then cellContent else "" + when 'clobValue' + value.set clobValue: if cellContent? then cellContent else "" when 'numericValue' numVal = parseFloat(cellContent) if isNaN(numVal) or isNaN(Number(numVal)) From 78ce6578fb01945aecd8d48492b8ce6e19c68d83 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Mon, 17 Jul 2017 09:33:30 -0700 Subject: [PATCH 106/576] feature/state_table_display: added pointer as cursor for editable cells in state display table --- .../Components/src/client/ACASFormStateDisplayUpdate.coffee | 3 ++- modules/Components/src/client/style.css | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee index 95c3ead51..4cef35e49 100644 --- a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee +++ b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee @@ -37,7 +37,8 @@ class window.ACASFormStateDisplayUpdateCellController extends Backbone.View $(@el).addClass if @collection.length > 1 then "valueWasEdited" else "" if @options.cellDef.editable? and !@options.cellDef.editable $(@el).addClass 'valueNotEditable' - + else + $(@el).addClass 'valueEditable' @ diff --git a/modules/Components/src/client/style.css b/modules/Components/src/client/style.css index 31d237a74..f84209f93 100644 --- a/modules/Components/src/client/style.css +++ b/modules/Components/src/client/style.css @@ -48,4 +48,8 @@ .bv_stateDisplayUpdateTable .valueNotEditable { color: #9D9D9D; +} + +.bv_stateDisplayUpdateTable .valueEditable { + cursor: pointer; } \ No newline at end of file From bc1db299468180c9dc86baa36556f1c38c4caf9a Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 17 Jul 2017 15:49:23 -0700 Subject: [PATCH 107/576] Fixes #167 --- modules/Components/src/client/PickList.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/Components/src/client/PickList.coffee b/modules/Components/src/client/PickList.coffee index 13f5a5d6c..17e7f68be 100644 --- a/modules/Components/src/client/PickList.coffee +++ b/modules/Components/src/client/PickList.coffee @@ -261,7 +261,7 @@ class window.PickListSelect2Controller extends PickListSelectController # 'options.propertyMap' is not specified, it defaults to using the # 'acasPropertyMap' initialize: (options) -> - if @options.width? + if @options?.width? @width = @options.width else @width = "100%" @@ -285,7 +285,7 @@ class window.PickListSelect2Controller extends PickListSelectController obj.text = obj[@propertyMap.text] mappedData.push(obj) @placeholder = "" - if @options.placeholder? + if @options?.placeholder? @placeholder = @options.placeholder $(@el).select2 placeholder: @placeholder From eccb9a9c7af432ac6cad4e67789a273841e4f394 Mon Sep 17 00:00:00 2001 From: mprincipato Date: Tue, 18 Jul 2017 11:06:24 -0700 Subject: [PATCH 108/576] Fixes #162. Makes it so that moduleMenus looks at a config parameter to tell what the module menus should be (#163) --- conf/config.properties.example | 1 + modules/ModuleMenus/src/client/ModuleMenus.coffee | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 8e2d1d52c..0cb750a17 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -326,6 +326,7 @@ client.moduleMenus.logoText=ACAS client.moduleMenus.logoImageFilePath= client.moduleMenus.homePageMessage=Welcome to ACAS client.moduleMenus.copyrightMessage=© John McNeil & Company 2012-2017 +# client.moduleMenus.menuConfigurationSettings=ModuleMenusConfiguration # If summary statistics is an included feature, set the following to true client.moduleMenus.summaryStats=false # For navigation to external ACAS modules from ACAS homepage diff --git a/modules/ModuleMenus/src/client/ModuleMenus.coffee b/modules/ModuleMenus/src/client/ModuleMenus.coffee index 41118e07e..87d209f5c 100644 --- a/modules/ModuleMenus/src/client/ModuleMenus.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenus.coffee @@ -17,7 +17,12 @@ class window.ModuleMenusController extends Backbone.View $(@el).html @template() - @moduleLauncherList = new ModuleLauncherList(@options.menuListJSON) + if window.conf.moduleMenus.menuConfigurationSettings? + menuListJSON = window[window.conf.moduleMenus.menuConfigurationSettings] + else + menuListJSON = @options.menuListJSON + + @moduleLauncherList = new ModuleLauncherList(menuListJSON) @moduleLauncherMenuListController = new ModuleLauncherMenuListController el: @$('.bv_modLaunchMenuWrapper') collection: @moduleLauncherList From 7cc2808ece53d5b2321d277a6e8f8d6975887e06 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Tue, 18 Jul 2017 15:26:16 -0700 Subject: [PATCH 109/576] feature/state_table_display: added options to state display table to have functions for overriding display of value and for autoupdating other values in the state, and for configuring the text in the GUI, updated getContainer by type/kind service to work for stubs --- .../src/client/ACASFormStateDisplayUpdate.coffee | 14 +++++++++++++- .../src/client/ACASFormStateDisplayView.html | 4 ++-- .../server/routes/InventoryServiceRoutes.coffee | 11 ++++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee index 4cef35e49..6328d01d1 100644 --- a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee +++ b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee @@ -30,6 +30,8 @@ class window.ACASFormStateDisplayUpdateCellController extends Backbone.View else val = @collection.findWhere ignored: false content = val.get(val.get('lsType')) + if @options.cellDef.displayOverride? + content = @options.cellDef.displayOverride content if val.get('lsType') == 'dateValue' content = UtilityFunctions::convertMSToYMDDate val.get('dateValue') @@ -169,6 +171,8 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View recordedDate: new Date().getTime() stateToUpdate.get('lsValues').add valInfo.newValue + if valInfo.valueDef?.autoUpdate? + updatedStates = valInfo.valueDef.autoUpdate valInfo, @ @trigger 'thingSaveRequested', valInfo.comment class window.ACASFormStateDisplayOldValueController extends Backbone.View @@ -176,8 +180,12 @@ class window.ACASFormStateDisplayOldValueController extends Backbone.View template: _.template($("#ACASFormStateDisplayOldValueView").html()) render: => + value = @model.get(@model.get('lsType')) + if @options.valueDef.displayOverride? + value = @options.valueDef.displayOverride value + attrs = - value: @model.get(@model.get('lsType')) + value: value attrs.valueClass = if @model.get 'ignored' then "valueWasEdited" else "" if @model.has('modifiedBy') && @model.get('modifiedBy')!="" @@ -236,11 +244,14 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View @collection.each (val) => oldRow = new ACASFormStateDisplayOldValueController model: val + valueDef: @valueDef @$("tbody").append oldRow.render().el @setupEditor() @$('.bv_aCASFormStateDisplayValueEdit').modal backdrop: "static" @$('.bv_aCASFormStateDisplayValueEdit').modal "show" + @$('.bv_valueHeader').html @tableDef.valueHeader + @$('.bv_changeValueHeader').html @tableDef.changeValueHeader reasonForUpdateChanged: -> @comment = @commentPrefix + @$('.bv_reasonForUpdate').val() @@ -263,6 +274,7 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View newValue: @newValue comment: @comment stateID: @options.stateID + valueDef: @valueDef @$('.bv_aCASFormStateDisplayValueEdit').modal "hide" $(@el).empty() diff --git a/modules/Components/src/client/ACASFormStateDisplayView.html b/modules/Components/src/client/ACASFormStateDisplayView.html index b1f528c54..f868f12c0 100644 --- a/modules/Components/src/client/ACASFormStateDisplayView.html +++ b/modules/Components/src/client/ACASFormStateDisplayView.html @@ -27,7 +27,7 @@

    launching controller must provide or set table label

    -
    +
    -
    +
    From a1eec74e490b5fd9cf611acf68169bdeeca0e782 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 28 Sep 2017 10:24:48 -0700 Subject: [PATCH 200/576] Added standalone services saveWellToWelInteractions and createDaughterVials, and refactored CSV loading and validating routes to use more atomic internal functions. Refactored some of the utility functions to follow the (err, data) node standard for callback functions. --- .../routes/InventoryServiceRoutes.coffee | 384 ++++++++++++------ 1 file changed, 265 insertions(+), 119 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index ddea9e109..cdaeeb16e 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -59,6 +59,8 @@ exports.setupAPIRoutes = (app) -> app.post '/api/getContainerLogsByContainerCodes', exports.getContainerLogsByContainerCodes app.post '/api/loadParentVialsFromCSV', exports.loadParentVialsFromCSV app.post '/api/loadDaughterVialsFromCSV', exports.loadDaughterVialsFromCSV + app.post '/api/saveWellToWellInteractions', exports.saveWellToWellInteractions + app.post '/api/createDaughterVials', exports.createDaughterVials exports.setupRoutes = (app, loginRoutes) -> @@ -110,6 +112,8 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/updateContainerHistoryLogs', loginRoutes.ensureAuthenticated, exports.updateContainerHistoryLogs app.post '/api/loadParentVialsFromCSV', loginRoutes.ensureAuthenticated, exports.loadParentVialsFromCSV app.post '/api/loadDaughterVialsFromCSV', loginRoutes.ensureAuthenticated, exports.loadDaughterVialsFromCSV + app.post '/api/saveWellToWellInteractions', loginRoutes.ensureAuthenticated, exports.saveWellToWellInteractions + app.post '/api/createDaughterVials', loginRoutes.ensureAuthenticated, exports.createDaughterVials exports.getContainersInLocation = (req, resp) -> @@ -2583,7 +2587,9 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> transactionId: null if exists validationResponse.results.path = path - createParentVialFileEntryArray csvFileName, (fileEntryArray) -> + createParentVialFileEntryArray csvFileName, (err, fileEntryArray) -> + if err? + callback err summaryInfo = prepareSummaryInfo fileEntryArray checkRequiredAttributes fileEntryArray, (requiredAttributeErrors) -> if requiredAttributeErrors? @@ -2629,7 +2635,9 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) commit: false if exists createResponse.results.path = path - createParentVialFileEntryArray csvFileName, (fileEntryArray) -> + createParentVialFileEntryArray csvFileName, (err, fileEntryArray) -> + if err? + callback err summaryInfo = prepareSummaryInfo fileEntryArray getContainerTubeDefinitionCode (definitionCode) -> if !definitionCode? @@ -2684,24 +2692,40 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) callback createResponse exports.loadDaughterVialsFromCSV = (req, resp) -> - exports.loadDaughterVialsFromCSVInternal req.body.fileToParse, req.body.dryRunMode, req.body.user, (response) -> - resp.json response + exports.loadDaughterVialsFromCSVInternal req.body.fileToParse, req.body.dryRunMode, req.body.user, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response exports.loadDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callback) -> if dryRun - exports.validateDaughterVialsFromCSVInternal csvFileName, dryRun, (validationResponse) -> - callback validationResponse + exports.validateDaughterVialsFromCSVInternal csvFileName, dryRun, (err, validationResponse) -> + if err? + callback err + else + callback null, validationResponse else - exports.validateDaughterVialsFromCSVInternal csvFileName, dryRun, (validationResponse) -> - if validationResponse.hasError - callback validationResponse + exports.validateDaughterVialsFromCSVInternal csvFileName, dryRun, (err, validationResponse) -> + if err? + callback err + else if validationResponse.hasError + callback null, validationResponse else - exports.createDaughterVialsFromCSVInternal csvFileName, dryRun, user, (createVialsResponse) -> - callback createVialsResponse + exports.createDaughterVialsFromCSVInternal csvFileName, dryRun, user, (err, createVialsResponse) -> + if err? + callback err + else + callback null, createVialsResponse exports.validateDaughterVialsFromCSV = (req, resp) -> - exports.validateDaughterVialsFromCSVInternal req.body.csvFileName, (validationResponse) -> - resp.json validationResponse + exports.validateDaughterVialsFromCSVInternal req.body.csvFileName, (err, validationResponse) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json validationResponse exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) -> getFileExists csvFileName, (exists, path) -> @@ -2716,38 +2740,19 @@ exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) - transactionId: null if exists validationResponse.results.path = path - createDaughterVialFileEntryArray csvFileName, (fileEntryArray) -> + createDaughterVialFileEntryArray csvFileName, (err, fileEntryArray) -> + if err? + callback err summaryInfo = prepareSummaryInfo fileEntryArray - checkRequiredAttributes fileEntryArray, (requiredAttributeErrors) -> - if requiredAttributeErrors? - validationResponse.errorMessages.push requiredAttributeErrors... - checkDataTypeErrors fileEntryArray, (dataTypeErrors) -> - if dataTypeErrors? - validationResponse.errorMessages.push dataTypeErrors... - sourceBarcodes = _.pluck fileEntryArray, 'sourceVialBarcode' - checkBarcodesExist sourceBarcodes, (existingBarcodes, newBarcodes) -> - console.log 'checking source vial barcodes which should exist' - console.log existingBarcodes - console.log newBarcodes - if newBarcodes? and newBarcodes.length > 0 - error = - level: 'error' - message: "The following source barcodes do not exist: " + newBarcodes.join ', ' - validationResponse.errorMessages.push error - destinationBarcodes = _.pluck fileEntryArray, 'destinationVialBarcode' - checkBarcodesExist destinationBarcodes, (existingBarcodes, newBarcodes) -> - if existingBarcodes? and existingBarcodes.length > 0 - error = - level: 'error' - message: "The following destination barcodes already exist: " + existingBarcodes.join ', ' - validationResponse.errorMessages.push error - checkParentWellContent fileEntryArray, (parentWellContentErrors) -> - if parentWellContentErrors? - validationResponse.errorMessages.push parentWellContentErrors... - if validationResponse.errorMessages.length < 1 - validationResponse.hasError = false - validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo - callback validationResponse + exports.validateDaughterVialsInternal fileEntryArray, (err, errorsAndWarnings) -> + if err? + callback err + else + validationResponse.errorMessages.push errorsAndWarnings... + if validationResponse.errorMessages.length < 1 + validationResponse.hasError = false + validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo + callback null, validationResponse else error = level: 'error' @@ -2767,80 +2772,30 @@ exports.createDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callbac errorMessages: [] transactionId: null commit: false - if exists - createResponse.results.path = path - createDaughterVialFileEntryArray csvFileName, (fileEntryArray) -> - summaryInfo = prepareSummaryInfo fileEntryArray - getContainerTubeDefinitionCode (definitionCode) -> - if !definitionCode? - error = - level: 'error' - message: 'Could not find definition container for tube' - createResponse.errorMessages.push error - tubesToCreate = [] - _.each fileEntryArray, (entry) -> - tube = - barcode: entry.destinationVialBarcode - definition: definitionCode - recordedBy: user - createdUser: entry.preparedBy - createdDate: entry.createdDate - physicalState: entry.physicalState - wells: [ - wellName: "A001" - batchCode: entry.batchCode - amount: parseFloat(entry.amount) - amountUnits: entry.amountUnits - physicalState: entry.physicalState - recordedBy: user - recordedDate: (new Date()).getTime() - ] - if entry.physicalState == 'solution' - tube.wells[0].batchConcentration = parseFloat(entry.concentration) - tube.wells[0].batchConcUnits = entry.concUnits - tube.wells[0].solventCode = entry.solvent - tubesToCreate.push tube - console.log JSON.stringify tubesToCreate - exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> - console.log statusCode - console.log json - if statusCode != 200 - createResponse.hasError = true - console.error json - error = - level: 'error' - message: json - createResponse.errorMessages.push error - else - createResponse.hasError = false - createResponse.commit = true - if !createResponse.hasError - #TODO: create interactions or values of daughter to parent relation - parentVialsToDecrement = [] - _.each fileEntryArray, (entry) -> - toDecrement = - barcode: entry.sourceVialBarcode - amountToDecrement: parseFloat(entry.amount) - amountToDecrementUnits: entry.amountUnits - parentVialsToDecrement.push toDecrement - decrementAmountsFromVials parentVialsToDecrement, user, (decrementVialsResponse, decrementVialsStatusCode) -> - if decrementVialsStatusCode != 200 - error = - level: 'error' - message: decrementVialsResponse - createResponse.errorMessages.push error - createResponse.hasError = true - createResponse.results.htmlSummary = prepareCreateDaughterVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo - callback createResponse - else - createResponse.results.htmlSummary = prepareCreateDaughterVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo - callback createResponse - else + if !exists error = level: 'error' message: "File cannot be found" createResponse.errorMessages.push error callback createResponse + else + createResponse.results.path = path + createDaughterVialFileEntryArray csvFileName, (err, fileEntryArray) -> + if err? + callback err + summaryInfo = prepareSummaryInfo fileEntryArray + exports.createDaughterVialsInternal fileEntryArray, user, (err, response) -> + if err? + error = + level: 'error' + message: err + createResponse.errorMessages.push error + else + createResponse.hasError = false + createResponse.commit = true + createResponse.results.htmlSummary = prepareCreateDaughterVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo + callback null, createResponse + getFileExists = (csvFileName, callback) => @@ -2882,7 +2837,7 @@ createParentVialFileEntryArray = (csvFileName, callback) => rowCount++ ) .on('end', () -> - return callback csvFileEntries, 200 + return callback null, csvFileEntries ) createDaughterVialFileEntryArray = (csvFileName, callback) => @@ -2910,7 +2865,7 @@ createDaughterVialFileEntryArray = (csvFileName, callback) => rowCount++ ) .on('end', () -> - return callback csvFileEntries, 200 + return callback null, csvFileEntries ) checkRequiredAttributes = (fileEntryArray, callback) -> @@ -3082,9 +3037,9 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> exports.getWellContentByContainerLabelsInternal vialBarcodes, 'container', 'tube', 'barcode', 'barcode', (wellContentList, statusCode) -> wellsToUpdate = [] _.each parentVialsToDecrement, (toDecrement) -> - wellCode = (_.findWhere plateWellCodes, {plateBarcode: toDecrement.barcode}).wellCodeName - oldContainerWellContent = _.findWhere wellContentList, {containerCodeName: wellCode} + oldContainerWellContent = _.findWhere wellContentList, {label: toDecrement.barcode} oldWellContent = oldContainerWellContent.wellContent[0] + wellCode = oldWellContent.containerCodeName newWellContent = containerCodeName: wellCode amount: oldWellContent.amount - toDecrement.amountToDecrement @@ -3096,7 +3051,7 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> if updateWellsStatusCode != 204 callback "Error: #{updateWellsResponse}" else - callback updateWellsResponse, 200 + callback null, updateWellsResponse prepareCreateDaughterVialsHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo) -> errors = _.where errorMessages, {level: 'error'} @@ -3128,4 +3083,195 @@ prepareSummaryInfo = (fileEntryArray) -> batchCodes = _.pluck fileEntryArray, 'batchCode' if batchCodes? summaryInfo.totalBatchCodes = (_.uniq batchCodes).length - summaryInfo \ No newline at end of file + summaryInfo + + +exports.saveWellToWellInteractions = (req, resp) -> + if req.session?.passport?.user?.username? + user = req.session.passport.user.username + else + user = 'anonymous' + exports.saveWellToWellInteractionsInternal req.body, user, (err, data) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json data + +exports.saveWellToWellInteractionsInternal = (interactionsToSave, user, callback) -> + barcodes = [] + barcodes.push (_.pluck interactionsToSave, 'firstContainerBarcode')... + barcodes.push (_.pluck interactionsToSave, 'secondContainerBarcode')... + console.log barcodes + exports.getWellCodesByPlateBarcodesInternal barcodes, (plateWellCodes) -> + wellCodes = _.pluck plateWellCodes, 'wellCodeName' + exports.getContainersByCodeNamesInternal wellCodes, (wells, statusCode) -> + if statusCode? && statusCode != 200 + callback wells + else + formattedItxList = [] + _.each interactionsToSave, (itx) -> + requiredParams = ['firstContainerBarcode', 'firstWellLabel', 'secondContainerBarcode', 'secondWellLabel', 'interactionType', 'interactionKind'] + for param in requiredParams + if !itx[param]? + callback "Error: all entries must include #{param}" + firstWellCode = (_.findWhere plateWellCodes, {plateBarcode: itx.firstContainerBarcode, wellLabel: itx.firstWellLabel}).wellCodeName + firstWell = (_.findWhere wells, {containerCodeName: firstWellCode}).container + secondWellCode = (_.findWhere plateWellCodes, {plateBarcode: itx.secondContainerBarcode, wellLabel: itx.secondWellLabel}).wellCodeName + secondWell = (_.findWhere wells, {containerCodeName: secondWellCode}).container + formattedItx = + lsType: itx.interactionType + lsKind: itx.interactionKind + recordedBy: user + recordedDate: new Date().getTime() + firstContainer: firstWell + secondContainer: secondWell + if itx.interactionStates? + _.each itx.interactionStates, (state) -> + state.recordedBy = user + state.recordedDate = new Date().getTime() + _.each state.lsValues, (value) -> + value.recordedBy = user + value.recordedDate = new Date().getTime() + formattedItx.lsStates = itx.interactionStates + formattedItxList.push formattedItx + baseurl = config.all.client.service.persistence.fullpath+"itxcontainercontainers/jsonArray" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: formattedItxList + json: true + timeout: 86400000 + , (error, response, json) => + if !error && response.statusCode == 201 + callback null, json + else + console.error 'error trying to save container container interactions' + console.error error + console.log response.statusCode + console.error json + callback "Error trying to save container container interactions: #{error}" + ) + +exports.validateDaughterVialsInternal = (vialsToValidate, callback) -> + errorMessages = [] + checkRequiredAttributes vialsToValidate, (requiredAttributeErrors) -> + if requiredAttributeErrors? + errorMessages.push requiredAttributeErrors... + checkDataTypeErrors vialsToValidate, (dataTypeErrors) -> + if dataTypeErrors? + errorMessages.push dataTypeErrors... + sourceBarcodes = _.pluck vialsToValidate, 'sourceVialBarcode' + checkBarcodesExist sourceBarcodes, (existingBarcodes, newBarcodes) -> + if newBarcodes? and newBarcodes.length > 0 + error = + level: 'error' + message: "The following source barcodes do not exist: " + newBarcodes.join ', ' + errorMessages.push error + destinationBarcodes = _.pluck vialsToValidate, 'destinationVialBarcode' + checkBarcodesExist destinationBarcodes, (existingBarcodes, newBarcodes) -> + if existingBarcodes? and existingBarcodes.length > 0 + error = + level: 'error' + message: "The following destination barcodes already exist: " + existingBarcodes.join ', ' + errorMessages.push error + checkParentWellContent vialsToValidate, (parentWellContentErrors) -> + if parentWellContentErrors? + errorMessages.push parentWellContentErrors... + callback null, errorMessages + +exports.createDaughterVials = (req, resp) -> + if req.session?.passport?.user?.username? + user = req.session.passport.user.username + else + user = 'anonymous' + exports.validateDaughterVialsInternal req.body, (err, errorsAndWarnings) -> + if err? + resp.statusCode = 500 + resp.json err + else + errors = _.where errorsAndWarnings, {level: 'error'} + warnings = _.where errorsAndWarnings, {level: 'warning'} + if errors? and errors.length > 0 + resp.statusCode = 400 + resp.json errorsAndWarnings + else + exports.createDaughterVialsInternal req.body, user, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> + getContainerTubeDefinitionCode (definitionCode) -> + if !definitionCode? + callback 'Could not find definition container for tube' + tubesToCreate = [] + _.each vialsToCreate, (entry) -> + tube = + barcode: entry.destinationVialBarcode + definition: definitionCode + recordedBy: user + createdUser: entry.preparedBy + createdDate: entry.createdDate + physicalState: entry.physicalState + wells: [ + wellName: "A001" + batchCode: entry.batchCode + amount: parseFloat(entry.amount) + amountUnits: entry.amountUnits + physicalState: entry.physicalState + recordedBy: user + recordedDate: (new Date()).getTime() + ] + if entry.physicalState == 'solution' + tube.wells[0].batchConcentration = parseFloat(entry.concentration) + tube.wells[0].batchConcUnits = entry.concUnits + tube.wells[0].solventCode = entry.solvent + tubesToCreate.push tube + console.log JSON.stringify tubesToCreate + exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> + console.log statusCode + console.log json + if statusCode != 200 + callback json + else + interactionsToCreate = [] + _.each vialsToCreate, (entry) -> + interaction = + interactionType: 'added to' + interactionKind: 'well_well' + firstContainerBarcode: entry.sourceVialBarcode + secondContainerBarcode: entry.destinationVialBarcode + firstWellLabel: 'A001' + secondWellLabel: 'A001' + interactionStates: [ + lsType: 'metadata' + lsKind: 'information' + lsValues: [ + lsType: 'numericValue' + lsKind: 'amount added' + numericValue: parseFloat(entry.amount) + unitKind: entry.amountUnits + ] + ] + interactionsToCreate.push interaction + exports.saveWellToWellInteractionsInternal interactionsToCreate, user, (err, itxResponse) -> + if err? + callback err + else + parentVialsToDecrement = [] + _.each vialsToCreate, (entry) -> + toDecrement = + barcode: entry.sourceVialBarcode + amountToDecrement: parseFloat(entry.amount) + amountToDecrementUnits: entry.amountUnits + parentVialsToDecrement.push toDecrement + decrementAmountsFromVials parentVialsToDecrement, user, (err, decrementVialsResponse) -> + if err? + callback err + else + #TODO see what this service should respond with + callback null, 'successfully created daughter vials' \ No newline at end of file From 327d45f453c8688a491aab7e6d199d7bc797a2e8 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Thu, 28 Sep 2017 11:24:43 -0700 Subject: [PATCH 201/576] more aliquot inventory progress --- .../src/server/routes/ServerUtilityFunctions.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee b/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee index ea627a209..d16754d1b 100644 --- a/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee +++ b/modules/ServerAPI/src/server/routes/ServerUtilityFunctions.coffee @@ -2160,6 +2160,12 @@ class ContainerTube extends ContainerPlate stateKind: 'information' type: 'stringValue' kind: 'comments' + , + key: 'locationName' + stateType: 'metadata' + stateKind: 'information' + type: 'stringValue' + kind: 'locationName' ] class AnalysisGroup extends Backbone.Model From c21fceedbbe2ff0b2f4fad4296dc5d66b5df6a8b Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 28 Sep 2017 13:50:56 -0700 Subject: [PATCH 202/576] feature/acas-author-manager: added author editing and deleting roles, fixed bug in testUserHasRole and testUserHasRoleTypeKindName, added Author Browser module, deep link to author module by username rather than codeName (roo route doesn't exist for get by codename), disable username editing, added node proxies for deleting author and searching for author --- conf/config.properties.example | 4 + .../src/client/UtilityFunctions.coffee | 4 +- .../client/ModuleMenusConfiguration.coffee | 3 + .../ServerAPI/src/client/AuthorBrowser.coffee | 234 ++++++++++++++++++ .../ServerAPI/src/client/AuthorBrowser.html | 139 +++++++++++ .../ServerAPI/src/client/AuthorEditor.coffee | 24 +- .../src/server/routes/AuthorRoutes.coffee | 96 ++++--- 7 files changed, 461 insertions(+), 43 deletions(-) create mode 100644 modules/ServerAPI/src/client/AuthorBrowser.coffee create mode 100644 modules/ServerAPI/src/client/AuthorBrowser.html diff --git a/conf/config.properties.example b/conf/config.properties.example index d8194b707..8234bb8d3 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -415,6 +415,10 @@ client.protocol.showAssayTreeRule=false client.protocol.showCurveDisplayParams=true client.experiment.uneditableFileTypes=["source file", "annotation file"] +#Controls privileges for who can create/edit and delete authors +client.author.editingRoles= +client.author.deletingRoles= + # Default status for experiments loaded thorugh experiment loader server.sel.experimentStatus=approved server.sel.protocolStatus=created diff --git a/modules/Components/src/client/UtilityFunctions.coffee b/modules/Components/src/client/UtilityFunctions.coffee index 10731f3a6..aaee23e19 100644 --- a/modules/Components/src/client/UtilityFunctions.coffee +++ b/modules/Components/src/client/UtilityFunctions.coffee @@ -4,7 +4,7 @@ class window.UtilityFunctions testUserHasRole: (user, roleNames) -> - if not user.roles? then return true + if not user.roles? then return false if not roleNames? || roleNames.length == 0 then return true match = false for roleName in roleNames @@ -17,7 +17,7 @@ class window.UtilityFunctions testUserHasRoleTypeKindName: (user, roleInfo) -> #roleInfo = list of objects with role type, kind, and name - if not user.roles? then return true + if not user.roles? then return false if not roleInfo? || roleInfo.length == 0 then return true match = false for role in roleInfo diff --git a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee index 75a315261..cd742d140 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee @@ -44,6 +44,9 @@ window.ModuleMenusConfiguration = , isHeader: false, menuName: "Project Browser" mainControllerClassName: "ProjectBrowserController" + , + isHeader: false, menuName: "Author Browser" + mainControllerClassName: "AuthorBrowserController" , isHeader: true menuName: "Admin" diff --git a/modules/ServerAPI/src/client/AuthorBrowser.coffee b/modules/ServerAPI/src/client/AuthorBrowser.coffee new file mode 100644 index 000000000..e8439b8e9 --- /dev/null +++ b/modules/ServerAPI/src/client/AuthorBrowser.coffee @@ -0,0 +1,234 @@ +class window.AuthorSearch extends Backbone.Model + defaults: + protocolCode: null + authorCode: null + +class window.AuthorSearch extends Backbone.Model + defaults: + protocolCode: null + authorCode: null + +class window.AuthorSimpleSearchController extends AbstractFormController + template: _.template($("#AuthorSimpleSearchView").html()) + genericSearchUrl: "/api/genericSearch/authors" + + events: + 'keyup .bv_authorSearchTerm': 'updateAuthorSearchTerm' + 'click .bv_doSearch': 'handleDoSearchClicked' + + render: => + $(@el).empty() + $(@el).html @template() + + updateAuthorSearchTerm: (e) => + ENTER_KEY = 13 + authorSearchTerm = $.trim(@$(".bv_authorSearchTerm").val()) + if authorSearchTerm isnt "" + @$(".bv_doSearch").attr("disabled", false) + if e.keyCode is ENTER_KEY + $(':focus').blur() + @handleDoSearchClicked() + else + @$(".bv_doSearch").attr("disabled", true) + + handleDoSearchClicked: => + $(".bv_authorTableController").addClass "hide" + $(".bv_errorOccurredPerformingSearch").addClass "hide" + authorSearchTerm = $.trim(@$(".bv_authorSearchTerm").val()) + $(".bv_exptSearchTerm").val "" + if authorSearchTerm isnt "" + $(".bv_noMatchingAuthorsFoundMessage").addClass "hide" + $(".bv_authorBrowserSearchInstructions").addClass "hide" + $(".bv_searchAuthorsStatusIndicator").removeClass "hide" + if !window.conf.browser.enableSearchAll and authorSearchTerm is "*" + $(".bv_moreSpecificAuthorSearchNeeded").removeClass "hide" + else + $(".bv_searchingAuthorsMessage").removeClass "hide" + $(".bv_exptSearchTerm").html authorSearchTerm + $(".bv_moreSpecificAuthorSearchNeeded").addClass "hide" + @doSearch authorSearchTerm + + doSearch: (authorSearchTerm) => + # disable the search text field while performing a search + @$(".bv_authorSearchTerm").attr "disabled", true + @$(".bv_doSearch").attr "disabled", true + @trigger 'find' + unless authorSearchTerm is "" + $.ajax + type: 'POST' + url: @genericSearchUrl + contentType: "application/json" + dataType: "json" + data: + JSON.stringify + queryString: authorSearchTerm + success: (author) => + @trigger "searchReturned", author + error: (result) => + @trigger "searchReturned", null + complete: => + # re-enable the search text field regardless of if any results found + @$(".bv_authorSearchTerm").attr "disabled", false + @$(".bv_doSearch").attr "disabled", false + + + +class window.AuthorRowSummaryController extends Backbone.View + tagName: 'tr' + className: 'dataTableRow' + events: + "click": "handleClick" + + handleClick: => + @trigger "gotClick", @model + $(@el).closest("table").find("tr").removeClass "info" + $(@el).addClass "info" + + initialize: -> + @template = _.template($('#AuthorRowSummaryView').html()) + + render: => + toDisplay = + userName: @model.get('userName') + firstName: @model.get('firstName') + lastName: @model.get('lastName') + emailAddress: @model.get('emailAddress') + $(@el).html(@template(toDisplay)) + + @ + +class window.AuthorSummaryTableController extends Backbone.View + initialize: -> + + selectedRowChanged: (row) => + @trigger "selectedRowUpdated", row + + render: => + @template = _.template($('#AuthorSummaryTableView').html()) + $(@el).html @template + if @collection.models.length is 0 + $(".bv_noMatchingAuthorsFoundMessage").removeClass "hide" + # display message indicating no results were found + else + $(".bv_noMatchingAuthorsFoundMessage").addClass "hide" + @collection.each (auth) => + prsc = new AuthorRowSummaryController + model: auth + prsc.on "gotClick", @selectedRowChanged + @$("tbody").append prsc.render().el + + @$("table").dataTable oLanguage: + sSearch: "Filter results: " #rename summary table's search bar + + @ + + +class window.AuthorBrowserController extends Backbone.View + events: + "click .bv_deleteAuthor": "handleDeleteAuthorClicked" + "click .bv_editAuthor": "handleEditAuthorClicked" + "click .bv_confirmDeleteAuthorButton": "handleConfirmDeleteAuthorClicked" + "click .bv_cancelDelete": "handleCancelDeleteClicked" + + initialize: -> + template = _.template($("#AuthorBrowserView").html()) + $(@el).empty() + $(@el).html template + @searchController = new AuthorSimpleSearchController + model: new AuthorSearch() + el: @$('.bv_authorSearchController') + @searchController.render() + @searchController.on "searchReturned", @setupAuthorSummaryTable + @$('.bv_queryToolDisplayName').html window.conf.service.result.viewer.displayName + + setupAuthorSummaryTable: (authors) => + @destroyAuthorSummaryTable() + + $(".bv_searchingAuthorsMessage").addClass "hide" + if authors is null + @$(".bv_errorOccurredPerformingSearch").removeClass "hide" + + else if authors.length is 0 + @$(".bv_noMatchingAuthorsFoundMessage").removeClass "hide" + @$(".bv_authorTableController").html "" + else + $(".bv_searchAuthorsStatusIndicator").addClass "hide" + @$(".bv_authorTableController").removeClass "hide" + @authorSummaryTable = new AuthorSummaryTableController + collection: new AuthorList authors + + @authorSummaryTable.on "selectedRowUpdated", @selectedAuthorUpdated + $(".bv_authorTableController").html @authorSummaryTable.render().el + + selectedAuthorUpdated: (author) => + @trigger "selectedAuthorUpdated" + @authorController = new AuthorEditorController + model: new Author author.attributes + readOnly: true + + $('.bv_authorController').html @authorController.render().el + $(".bv_authorController").removeClass("hide") + $(".bv_authorControllerContainer").removeClass("hide") + + @$('.bv_editAuthor').show() + if window.conf.author?.editingRoles? + editingRoles = window.conf.author.editingRoles.split(",") + if !UtilityFunctions::testUserHasRole(window.AppLaunchParams.loginUser, editingRoles) + @$('.bv_editAuthor').hide() + + @$('.bv_deleteAuthor').show() + if window.conf.author?.deletingRoles? + deletingRoles= window.conf.author.deletingRoles.split(",") + if !UtilityFunctions::testUserHasRole(window.AppLaunchParams.loginUser, deletingRoles) + @$('.bv_deleteAuthor').hide() + + handleDeleteAuthorClicked: => + @$(".bv_authorUserName").html @authorController.model.get("userName") + @$(".bv_deleteButtons").removeClass "hide" + @$(".bv_okayButton").addClass "hide" + @$(".bv_errorDeletingAuthorMessage").addClass "hide" + @$(".bv_deleteWarningMessage").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_authorDeletedSuccessfullyMessage").addClass "hide" + $(".bv_confirmDeleteAuthor").removeClass "hide" + $('.bv_confirmDeleteAuthor').modal({ + keyboard: false, + backdrop: true + }) + + handleConfirmDeleteAuthorClicked: => + @$(".bv_deleteWarningMessage").addClass "hide" + @$(".bv_deletingStatusIndicator").removeClass "hide" + @$(".bv_deleteButtons").addClass "hide" + $.ajax( + url: "/api/authors/#{@authorController.model.get("id")}", + type: 'DELETE', + success: (result) => + @$(".bv_okayButton").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_authorDeletedSuccessfullyMessage").removeClass "hide" + @searchController.handleDoSearchClicked() + error: (result) => + @$(".bv_okayButton").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_errorDeletingAuthorMessage").removeClass "hide" + ) + + handleCancelDeleteClicked: => + @$(".bv_confirmDeleteAuthor").modal('hide') + + handleEditAuthorClicked: => + window.open("/author/codeName/#{@authorController.model.get("userName")}",'_blank'); + + destroyAuthorSummaryTable: => + if @authorSummaryTable? + @authorSummaryTable.remove() + if @authorController? + @authorController.remove() + $(".bv_authorController").addClass("hide") + $(".bv_authorControllerContainer").addClass("hide") + $(".bv_noMatchingAuthorsFoundMessage").addClass("hide") + + render: => + + @ diff --git a/modules/ServerAPI/src/client/AuthorBrowser.html b/modules/ServerAPI/src/client/AuthorBrowser.html new file mode 100644 index 000000000..b294d090e --- /dev/null +++ b/modules/ServerAPI/src/client/AuthorBrowser.html @@ -0,0 +1,139 @@ + + + + + + + diff --git a/modules/ServerAPI/src/client/AuthorEditor.coffee b/modules/ServerAPI/src/client/AuthorEditor.coffee index fabdc1f69..c67094978 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.coffee +++ b/modules/ServerAPI/src/client/AuthorEditor.coffee @@ -91,6 +91,9 @@ class window.Author extends Backbone.Model return new AuthorRoleList trimmedProjectRoles +class window.AuthorList extends Backbone.Collection + model: Author + class window.AuthorRoleController extends AbstractFormController template: _.template($("#AuthorRoleView").html()) @@ -303,13 +306,13 @@ class window.AuthorEditorController extends AbstractFormController if window.AppLaunchParams.moduleLaunchParams.moduleName == @moduleLaunchName $.ajax type: 'GET' - url: "/api/authorByCodename/"+window.AppLaunchParams.moduleLaunchParams.code + url: "/api/authorByUsername/"+window.AppLaunchParams.moduleLaunchParams.code dataType: 'json' error: (err) => alert 'Could not get author object with this code. Creating a new author' @completeInitialization() success: (authorObj) => - @model = new Author authorObj[0] + @model = new Author authorObj @completeInitialization() else @completeInitialization() @@ -326,16 +329,13 @@ class window.AuthorEditorController extends AbstractFormController if @options.readOnly? @readOnly = @options.readOnly else - if window.conf.security?.authstrategy? and window.conf.security.authstrategy is 'database' - #if authstrategy is database, then only admins can create/edit - acasAdminRole = window.conf.roles.acas.adminRole - if acasAdminRole != "" and !UtilityFunctions::testUserHasRole(window.AppLaunchParams.loginUser, [acasAdminRole]) + #create/edit based on whether user has create/edit role + @readOnly = false + if window.conf.author?.editingRoles? + editingRoles = window.conf.author.editingRoles.split(",") + if !UtilityFunctions::testUserHasRole(window.AppLaunchParams.loginUser, editingRoles) @readOnly = true - else - @readOnly = false - else - #else anyone can create/edit - @readOnly = false + $(@el).empty() $(@el).html @template() @$('.bv_save').attr('disabled', 'disabled') @@ -375,9 +375,11 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_group_activationDate').hide() @$('.bv_group_enabled').hide() if @model.isNew() + @$('.bv_userName').removeAttr 'disabled' @$('.bv_save').html("Save") @$('.bv_newEntity').hide() else + @$('.bv_userName').attr 'disabled', 'disabled' @$('.bv_save').html("Update") @$('.bv_newEntity').show() @$('.bv_save').attr('disabled', 'disabled') diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 0c5b47c02..416c844b0 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -1,14 +1,16 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.get '/api/authorByUsername/:username', exports.getAuthorByUsername - app.get '/api/authorByCodename/:codename', exports.getAuthorByCodename app.get '/api/authorModulePreferences/:userName/:moduleName', exports.getAuthorModulePreferences app.put '/api/authorModulePreferences/:userName/:moduleName', exports.updateAuthorModulePreferences + app.post '/api/genericSearch/authors', exports.genericAuthorSearch + app.delete '/api/authors/:id', exports.deleteAuthor exports.setupRoutes = (app, loginRoutes) -> app.get '/api/authorByUsername/:username', loginRoutes.ensureAuthenticated, exports.getAuthorByUsername - app.get '/api/authorByCodename/:codename', exports.getAuthorByCodename app.get '/api/authorModulePreferences/:userName/:moduleName', loginRoutes.ensureAuthenticated, exports.getAuthorModulePreferences app.put '/api/authorModulePreferences/:userName/:moduleName', loginRoutes.ensureAuthenticated, exports.updateAuthorModulePreferences + app.post '/api/genericSearch/authors', loginRoutes.ensureAuthenticated, exports.genericAuthorSearch + app.delete '/api/authors/:id', loginRoutes.ensureAuthenticated, exports.deleteAuthor serverUtilityFunctions = require './ServerUtilityFunctions.js' _ = require 'underscore' @@ -58,34 +60,6 @@ exports.getAuthorByUsernameInternal = (username, callback) -> callback JSON.stringify("getContainersInLocation failed"), 500 ) -exports.getAuthorByCodename = (req, resp) -> - exports.getAuthorByCodenameInternal req.params.codeName, (json, statusCode) -> - resp.statusCode = statusCode - resp.json json - -exports.getAuthorByCodenameInternal = (codename, callback) -> - if global.specRunnerTestmode - authorServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/AuthorServiceTestJSON.js' - resp.json authorServiceTestJSON.getAuthorByUsernameInternalResponse - else - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"authors?find=ByCodeName&codeName="+codename - request = require 'request' - request( - method: 'GET' - url: baseurl - json: true - , (error, response, json) => - if !error && response.statusCode == 200 - callback json, 200 - else - console.error 'got ajax error trying to get author by codename' - console.error error - console.error json - console.error response - callback JSON.stringify("get author by codename failed"), 500 - ) - exports.getAuthorModulePreferences = (req, resp) -> exports.getAuthorModulePreferencesInternal req.params.userName, req.params.moduleName, (json, statusCode) -> resp.statusCode = statusCode @@ -193,6 +167,68 @@ exports.createNewAuthorInternal = (author, cb) -> cb null, json ) +exports.genericAuthorSearch = (req, resp) -> + if req.query.testMode is true or global.specRunnerTestmode is true + resp.end JSON.stringify "Stubs mode not implemented yet for author search" + else + config = require '../conf/compiled/conf.js' + console.log "search req - generic author" + console.log req + unless req.body.queryDTO? + req.body.queryDTO = {} + # req.body needs queryString and queryDTO + baseurl = config.all.client.service.persistence.fullpath+"authors/genericBrowserSearch" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: req.body + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + #filter out ignored authors + if json.numberOfResults > 0 + nonIgnoredAuthors = _.filter json.results, (auth) => + !auth.ignored + resp.json nonIgnoredAuthors + else + resp.json [] + else + console.log 'got ajax error trying to search for authors' + console.log error + console.log json + console.log response + resp.statusCode = 500 + resp.end json + ) + +exports.deleteAuthor = (req, resp) -> + if global.specRunnerTestmode + res.end JSON.stringify "stubs mode for deleting author not implemented" + else + config = require '../conf/compiled/conf.js' + authorId = req.params.id + baseurl = config.all.client.service.persistence.fullpath+"authors/"+authorId + console.log baseurl + request = require 'request' + + request( + method: 'DELETE' + url: baseurl + json: true + , (error, response, json) => + console.log response.statusCode + if !error && response.statusCode == 200 + console.log "deleted author" + resp.json json + else + console.log 'got ajax error trying to delete author' + console.log error + console.log response + resp.statusCode = 500 + resp.end json + ) + class Author extends Backbone.Model lsProperties: {} From 28ca17e9cc125457b5fef2d5ed2be4a99a250a9a Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 28 Sep 2017 15:22:34 -0700 Subject: [PATCH 203/576] Updated docker-compose.yml and .env to bump up tomcat to openjdk8 and Roo + CmpdReg Roo to "develop", which now requires Java 8. --- .env | 2 +- docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 08100ff60..b6a34d3d5 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -ACAS_TAG=latest \ No newline at end of file +ACAS_TAG=develop diff --git a/docker-compose.yml b/docker-compose.yml index 5243463b5..d8d7b8c6c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,7 @@ services: ports: - "5432:5432" tomcat: - image: mcneilco/tomcat-maven:openjdk7 + image: mcneilco/tomcat-maven:openjdk8 restart: always depends_on: - db @@ -86,7 +86,7 @@ services: - /usr/local/tomcat/webapps/acas command: /bin/true cmpdreg: - image: mcneilco/acas-cmpdreg-roo-server-oss:indigo + image: mcneilco/acas-cmpdreg-roo-server-oss:${ACAS_TAG} volumes: - /usr/local/tomcat/webapps/cmpdreg # Add chemaxon license files here From 6a8d3c9f9e7b0bf9d0156853d8d42b4fc3494855 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 29 Sep 2017 09:13:41 -0700 Subject: [PATCH 204/576] Added two new inventory services: getParentVialByDaughterVialBarcode which takes daughterVialBarcode as a query param, and returns a flat object of the barcode/label and codenames for the parent vial, parent well, daughter vial, and daughter well. advancedSearchContainers: POST route that exposes the deep value/label/interaction searching route in Node. --- .../routes/InventoryServiceRoutes.coffee | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 16e257b31..578b96c94 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -6,6 +6,7 @@ config = require '../conf/compiled/conf.js' RUN_CUSTOM_FLAG = "0" fs = require('fs') parse = require('csv-parse') +request = require 'request' exports.setupAPIRoutes = (app) -> @@ -62,6 +63,8 @@ exports.setupAPIRoutes = (app) -> app.post '/api/loadDaughterVialsFromCSV', exports.loadDaughterVialsFromCSV app.post '/api/saveWellToWellInteractions', exports.saveWellToWellInteractions app.post '/api/createDaughterVials', exports.createDaughterVials + app.post '/api/advancedSearchContainers', exports.advancedSearchContainers + app.get '/api/getParentVialByDaughterVialBarcode', exports.getParentVialByDaughterVialBarcode exports.setupRoutes = (app, loginRoutes) -> @@ -116,6 +119,8 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/loadDaughterVialsFromCSV', loginRoutes.ensureAuthenticated, exports.loadDaughterVialsFromCSV app.post '/api/saveWellToWellInteractions', loginRoutes.ensureAuthenticated, exports.saveWellToWellInteractions app.post '/api/createDaughterVials', loginRoutes.ensureAuthenticated, exports.createDaughterVials + app.post '/api/advancedSearchContainers', loginRoutes.ensureAuthenticated, exports.advancedSearchContainers + app.get '/api/getParentVialByDaughterVialBarcode', loginRoutes.ensureAuthenticated, exports.getParentVialByDaughterVialBarcode exports.getContainersInLocation = (req, resp) -> @@ -3343,4 +3348,77 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> callback err else #TODO see what this service should respond with - callback null, 'successfully created daughter vials' \ No newline at end of file + callback null, 'successfully created daughter vials' + +exports.advancedSearchContainers = (req, resp) -> + exports.advancedSearchContainersInternal req.body, req.query.format, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.advancedSearchContainersInternal = (itxSearchBody, format, callback) -> + baseurl = config.all.client.service.persistence.fullpath+"containers/advancedSearchContainers" + if format? + baseurl += "?with=#{format}" + request( + method: 'POST' + url: baseurl + body: itxSearchBody + json: true + timeout: 86400000 + headers: 'content-type': 'application/json' + , (error, response, json) => + if !error && response.statusCode == 200 + console.debug "returned successfully from #{baseurl}" + callback null, json + else + console.error 'got ajax error trying to get getWellCodesByContainerCodes' + console.error error + console.error json + console.error response + callback JSON.stringify "getWellCodesByContainerCodes failed" + ) + +exports.getParentVialByDaughterVialBarcode = (req, resp) -> + exports.getParentVialByDaughterVialBarcodeInternal req.query.daughterVialBarcode, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callback) -> + exports.getWellCodesByPlateBarcodesInternal [daughterVialBarcode], (plateWellCodes) -> + plateWellCode = plateWellCodes[0] + responseStub = + daughterVialBarcode: plateWellCode.plateBarcode + daughterVialCodeName: plateWellCode.plateCodeName + daughterWellCodeName: plateWellCode.wellCodeName + daughterWellLabel: plateWellCode.wellLabel + itxSearch = + lsType: 'well' + lsKind: 'default' + secondInteractions: [ + interactionType: 'added to' + interactionKind: 'well_well' + thingType: 'well' + thingKind: 'default' + thingCodeName: responseStub.daughterWellCodeName + ] + format = 'nestedstub' + exports.advancedSearchContainersInternal itxSearch, format, (err, advSearchReturn) -> + parentWell = advSearchReturn.results[0] + responseStub.parentWellCodeName = parentWell.codeName + parentWellLabel = _.findWhere parentWell.lsLabels, {lsType: 'name', lsKind: 'well name', ignored: false} + responseStub.parentWellLabel = parentWellLabel.labelText + parentVialItx = _.findWhere parentWell.firstContainers, {lsType: 'has member', lsKind: 'container_well', ignored: false} + if !parentVialItx? + callback 'Parent vial not found' + else + parentVial = parentVialItx.firstContainer + responseStub.parentVialCodeName = parentVial.codeName + parentVialBarcode = _.findWhere parentVial.lsLabels, {lsType: 'barcode', lsKind: 'barcode', ignored: false} + responseStub.parentVialBarcode = parentVialBarcode.labelText + callback null, responseStub \ No newline at end of file From 53098880afdc5554dbb26738f1678c76c19d6622 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Fri, 29 Sep 2017 17:26:36 -0700 Subject: [PATCH 205/576] Added feature to metalot to optionally load lot inventory iframe --- modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css | 9 +++++++-- modules/CmpdReg/src/client/custom/configuration.json | 5 ++++- modules/CmpdReg/src/client/src/MetaLot.js | 5 +++++ .../CmpdReg/src/client/templates/LotForm/MetaLotView.inc | 3 ++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css index 0715a7810..6b3fb9f98 100755 --- a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css +++ b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css @@ -219,6 +219,12 @@ div { .MetaLotView div.floatingPanel.newIsotopeView div { height: 20px; } + +.MetaLotView .bv_lotInventory iframe { + width: 700px; + height: 1000px; +} + .NewLotSuccessView .labelCorpName { margin-left: 10px; margin-bottom: 40px; @@ -1030,7 +1036,6 @@ input.FormInput.supplier, .FormInput.supplierID, .FormInput.percentEE, .FormInpu width: 160px; } - /*** SYNTH DATE COLUMN ***/ .MetaLotView .LotForm_LotViewSynth { @@ -1113,7 +1118,7 @@ div.MetaLotViewButtons.buttons { .LotForm_LotView a.editAnalyticalFiles { background: no-repeat url('../images/view_edit_files.png'); - position: absolute; + /*position: absolute;*/ left: 556px; bottom: 40px; margin-left: 150px; diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index 17ba6861f..fc112affd 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -41,7 +41,8 @@ "useProjectRolesToRestrictLotDetails": false, "showTareWeight": false, "showTotalAmoundStored": false, - "disableAliasEdit": false + "disableAliasEdit": false, + "showLotInventory": true }, "serverSettings": { "corpPrefix": "CMPD", @@ -109,3 +110,5 @@ ] } } + + diff --git a/modules/CmpdReg/src/client/src/MetaLot.js b/modules/CmpdReg/src/client/src/MetaLot.js index c9b6f2ee8..5f4627f64 100755 --- a/modules/CmpdReg/src/client/src/MetaLot.js +++ b/modules/CmpdReg/src/client/src/MetaLot.js @@ -229,6 +229,11 @@ $(function() { if (!this.allowedToUpdate()) { this.$('.saveButton').hide(); } + console.log("about to load inventory"); + console.log(window.configuration.metaLot.showLotInventory); + if (window.configuration.metaLot.showLotInventory) { + this.$('.bv_lotInventory').append("") + } } diff --git a/modules/CmpdReg/src/client/templates/LotForm/MetaLotView.inc b/modules/CmpdReg/src/client/templates/LotForm/MetaLotView.inc index 8032d3268..5a4a533ee 100755 --- a/modules/CmpdReg/src/client/templates/LotForm/MetaLotView.inc +++ b/modules/CmpdReg/src/client/templates/LotForm/MetaLotView.inc @@ -11,5 +11,6 @@
    - +
    + From 3aea4d405fd01c75f61e46e5afe91e0a02e9c580 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 2 Oct 2017 13:15:10 -0700 Subject: [PATCH 206/576] AuthorRoutes: Added fully featured save and update author routes which check the user has permissions to make the edit they're trying to make, validates duplicate username and email on save and edit, and persists changes in author roles. In database mode, saving a new author also triggers an account activation email to be sent to the entered email address. AuthorEditor: uncommented save method, and provided urlRoot now that save and update services exist. Fixed copy/paste error. Tweaked roles lists so they sort by save order, just so they stay consistent. ModuleMenus: moved author browser down into Admin section and made CmpdReg Admin collapsible. --- .../client/ModuleMenusConfiguration.coffee | 9 +- .../ServerAPI/src/client/AuthorEditor.coffee | 19 +- .../ServerAPI/src/client/AuthorEditor.html | 2 +- .../src/server/routes/AuthorRoutes.coffee | 369 +++++++++++++++++- 4 files changed, 384 insertions(+), 15 deletions(-) diff --git a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee index 93ea24bd1..55a4bc2dc 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee @@ -50,9 +50,6 @@ window.ModuleMenusConfiguration = , isHeader: false, menuName: "Project Browser" mainControllerClassName: "ProjectBrowserController" - , - isHeader: false, menuName: "Author Browser" - mainControllerClassName: "AuthorBrowserController" , isHeader: true menuName: "Admin" @@ -63,6 +60,11 @@ window.ModuleMenusConfiguration = mainControllerClassName: "AuthorEditorController" autoLaunchName: "author" requireUserRoles: [] + , + isHeader: false + menuName: "Author Browser" + mainControllerClassName: "AuthorBrowserController" + requireUserRoles: [] , isHeader: false menuName: "Admin Panel" @@ -88,6 +90,7 @@ window.ModuleMenusConfiguration = isHeader: true menuName: "CmpdReg Admin" requireUserRoles: [] + collapsible: true , isHeader: false menuName: "CmpdReg Vendors" diff --git a/modules/ServerAPI/src/client/AuthorEditor.coffee b/modules/ServerAPI/src/client/AuthorEditor.coffee index c67094978..ea315aa2e 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.coffee +++ b/modules/ServerAPI/src/client/AuthorEditor.coffee @@ -22,7 +22,7 @@ class window.AuthorRoleList extends Backbone.Collection modelErrors class window.Author extends Backbone.Model -# urlRoot: TODO: fill in once have service + urlRoot: '/api/author' defaults: -> lsType: "author" @@ -70,25 +70,24 @@ class window.Author extends Backbone.Model systemRoles = _.filter @get('authorRoles'), (role) => if role.roleEntry?.lsType? role.roleEntry.lsType is "System" - - trimmedSystemRoles = _.pluck systemRoles, 'roleEntry' + sortedSystemRoles = _.sortBy systemRoles, 'id' + trimmedSystemRoles = _.pluck sortedSystemRoles, 'roleEntry' return new AuthorRoleList trimmedSystemRoles getLdapRoles: => ldapRoles = _.filter @get('authorRoles'), (role) => if role.roleEntry?.lsType? role.roleEntry.lsType is "LDAP" - - trimmedLdapRoles = _.pluck ldapRoles, 'roleEntry' + sortedLdapRoles = _.sortBy ldapRoles, 'id' + trimmedLdapRoles = _.pluck sortedLdapRoles, 'roleEntry' return new AuthorRoleList trimmedLdapRoles getProjectRoles: => projectRoles = _.filter @get('authorRoles'), (role) => if role.roleEntry?.lsType? role.roleEntry.lsType is "Project" - - trimmedProjectRoles = _.pluck projectRoles, 'roleEntry' - + sortedProjectRoles = _.sortBy projectRoles, 'id' + trimmedProjectRoles = _.pluck sortedProjectRoles, 'roleEntry' return new AuthorRoleList trimmedProjectRoles class window.AuthorList extends Backbone.Collection @@ -154,7 +153,7 @@ class window.AuthorRoleController extends AbstractFormController @authorRoleList = new PickListList response @finishSetupAuthorRoleSelect() error: (err) => - alert 'could not get the nhp test subjects' + alert 'could not get the list of author roles' @serviceReturn = null @@ -503,7 +502,7 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_saving').show() console.log "handleSaveClicked" console.log JSON.stringify @model -# @model.save() TODO: uncomment when service works + @model.save() prepareToSaveSystemRoles: => authorRoles = [] diff --git a/modules/ServerAPI/src/client/AuthorEditor.html b/modules/ServerAPI/src/client/AuthorEditor.html index 3b65c1169..c375f2c26 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.html +++ b/modules/ServerAPI/src/client/AuthorEditor.html @@ -111,5 +111,5 @@ diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 416c844b0..2604c9743 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -4,6 +4,8 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.put '/api/authorModulePreferences/:userName/:moduleName', exports.updateAuthorModulePreferences app.post '/api/genericSearch/authors', exports.genericAuthorSearch app.delete '/api/authors/:id', exports.deleteAuthor + app.post '/api/author', exports.saveAuthor + app.put '/api/author/:id', exports.updateAuthor exports.setupRoutes = (app, loginRoutes) -> app.get '/api/authorByUsername/:username', loginRoutes.ensureAuthenticated, exports.getAuthorByUsername @@ -11,11 +13,15 @@ exports.setupRoutes = (app, loginRoutes) -> app.put '/api/authorModulePreferences/:userName/:moduleName', loginRoutes.ensureAuthenticated, exports.updateAuthorModulePreferences app.post '/api/genericSearch/authors', loginRoutes.ensureAuthenticated, exports.genericAuthorSearch app.delete '/api/authors/:id', loginRoutes.ensureAuthenticated, exports.deleteAuthor + app.post '/api/author', loginRoutes.ensureAuthenticated, exports.saveAuthor + app.put '/api/author/:id', loginRoutes.ensureAuthenticated, exports.updateAuthor serverUtilityFunctions = require './ServerUtilityFunctions.js' _ = require 'underscore' Backbone = require 'backbone' $ = require 'jquery' +request = require 'request' +config = require '../conf/compiled/conf.js' Label = serverUtilityFunctions.Label LabelList = serverUtilityFunctions.LabelList Value = serverUtilityFunctions.Value @@ -160,9 +166,11 @@ exports.createNewAuthorInternal = (author, cb) -> body: author json: true timeout: 6000000 - , (error, response, json) => + , (err, response, json) => if err? cb err, null + else if response.statusCode != 201 + cb json, null else cb null, json ) @@ -505,3 +513,362 @@ class Author extends Backbone.Model exports.Author = Author AppLaunchParams = loginUser:username:"acas" + +exports.saveAuthor = (req, resp) -> + if req.session?.passport?.user? + user = req.session.passport.user + else + user = + username: 'anonymous' + roles: [] + exports.saveAuthorInternal req.body, user, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.updateAuthor = (req, resp) -> + if req.session?.passport?.user? + user = req.session.passport.user + else + user = + username: 'anonymous' + roles: [] + exports.updateAuthorAndRolesInternal req.body, user, (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.saveAuthorInternal = (author, user, callback) -> + validateAuthorAttributes author, (authorValidationErrors) -> + if authorValidationErrors? + callback authorValidationErrors + else + checkUserCanCreateOrEditAuthor user, (err, userCanCreate) -> + if err? + callback err + else if !userCanCreate + console.error "ALERT: User #{user.username} attempted to create an author without having the proper roles." + callback 'You do not have permissions to create authors! This incident will be reported to your system administrator.' + else + checkUserNameAndEmailAreUnique author, (userNameEmailUniqueError) -> + if userNameEmailUniqueError? + callback userNameEmailUniqueError + else + parseSystemRoles author, (err, author, systemRoles) -> + if err? + callback err + else + author.recordedBy = user.username + createOrSignupAuthorInternal author, (err, savedAuthor) -> + if err? + callback err + else + fetchSystemRoles systemRoles, (err, systemRoles) -> + flatAuthorRoles = [] + _.each systemRoles, (role) -> + flatAuthorRole = + roleType: role.lsType + roleKind: role.lsKind + roleName: role.roleName + userName: author.userName + flatAuthorRoles.push flatAuthorRole + saveAuthorRoles flatAuthorRoles, (err, savedRoles) -> + if err? + callback err + else + exports.getAuthorByUsernameInternal author.userName, (response, statusCode) -> + if statusCode != 200 + callback err + else + callback null, response + +exports.updateAuthorAndRolesInternal = (author, user, callback) -> + validateAuthorAttributes author, (authorValidationErrors) -> + if authorValidationErrors? + callback authorValidationErrors + else + checkUserCanCreateOrEditAuthor user, (err, userCanEdit) -> + if err? + callback err + else if !userCanEdit + console.error "ALERT: User #{user.username} attempted to edit an author without having the proper roles." + callback 'You do not have permissions to edit authors! This incident will be reported to your system administrator.' + else + parseSystemRoles author, (err, author, systemRoles) -> + if err? + callback err + else + exports.getAuthorByUsernameInternal author.userName, (savedAuthor, statusCode) -> + if statusCode != 200 + callback savedAuthor + else + checkIfEmailHasChangedAndIsUnique author, savedAuthor, (err) -> + if err? + callback err + else + parseSystemRoles savedAuthor, (err, savedAuthor, savedSystemRoles) -> + diffSystemRolesWithSaved author.userName, systemRoles, savedSystemRoles, (err, rolesToAdd, rolesToDelete) -> + if rolesToAdd.length > 0 or rolesToDelete.length > 0 + checkUserCanEditSystemRoles user, (err, userCanEditRoles) -> + if err? + callback err + else if !userCanEditRoles + console.error "ALERT: User #{user.username} attempted to edit system roles without having the proper authorities." + callback 'You do not have permissions to edit system roles! This incident will be reported to your system administrator.' + else + exports.updateAuthorInternal author, (updatedAuthor, statusCode) -> + if statusCode != 200 + callback updatedAuthor + else + saveAuthorRoles rolesToAdd, (err, savedRoles) -> + if err? + callback err + else + deleteAuthorRoles rolesToDelete, (err, deletedRoles) -> + if err? + callback err + else + #save successful. Fetch the new author and return. + exports.getAuthorByUsernameInternal author.userName, (response, statusCode) -> + if statusCode != 200 + callback err + else + callback null, response + +validateAuthorAttributes = (author, callback) -> + requiredAttrs = ['firstName', 'lastName', 'userName', 'emailAddress'] + missingAttrs = [] + for attr in requiredAttrs + if !author[attr]? or author[attr].length < 1 + missingAttrs.push attr + if missingAttrs.length > 0 + callback missingAttrs + else + callback null + +parseUserRoles = (user) -> + userRoles = [] + if user.roles? + _.each user.roles, (authorRole) -> + userRoles.push authorRole.roleEntry.roleName + return userRoles + +checkUserCanCreateOrEditAuthor = (user, callback) -> + userRoles = parseUserRoles user + authStrategy = config.all.server.security.authstrategy + adminRole = config.all.client.roles.acas.adminRole + if !config.all.client.author.editingRoles? + editingRoles = [] + else + editingRoles = config.all.client.author.editingRoles.split(",") + if !adminRole? or adminRole.length < 1 + callback null, true + else + if authStrategy == 'database' and (userRoles.indexOf(adminRole) > -1) + callback null, true + else if authStrategy == 'database' + callback null, false + else if editingRoles? and editingRoles.length > 1 + hasEditingRole = ((_.intersection userRoles, editingRoles).length > 0) + callback null, hasEditingRole + else + callback null, true + +checkUserCanEditSystemRoles = (user, callback) -> + userRoles = parseUserRoles user + authStrategy = config.all.server.security.authstrategy + adminRole = config.all.client.roles.acas.adminRole + if !adminRole? or adminRole.length < 1 + callback null, true + else + if (userRoles.indexOf(adminRole) > -1) + callback null, true + else + callback null, false + +checkUserNameAndEmailAreUnique = (author, callback) -> + checkUserNameIsUnique author, (err, userNameUnique) -> + if err? + callback err + else + checkEmailIsUnique author, (err, emailIsUnique) -> + if err? + callback err + else + if !userNameUnique and !emailIsUnique + callback 'That username and email address are both already in use.' + else if !userNameUnique + callback 'That username is already in use.' + else if !emailIsUnique + callback 'That email address is already in use.' + else + callback null + +checkUserNameIsUnique = (author, callback) -> + baseurl = config.all.client.service.persistence.fullpath+"authors?find=ByUserName&userName="+author.userName + request( + method: 'GET' + url: baseurl + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + if json.length < 1 + callback null, true + else + console.debug json + callback null, false + else + console.error 'got error checking if author username exists' + console.error error + console.error json + console.error response + callback JSON.stringify("failed checking author username") + ) + +checkEmailIsUnique = (author, callback) -> + baseurl = config.all.client.service.persistence.fullpath+"authors?find=ByEmailAddress&emailAddress="+author.emailAddress + request( + method: 'GET' + url: baseurl + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + if json.length < 1 + callback null, true + else + console.debug json + callback null, false + else + console.error 'got error checking if author emailAddress exists' + console.error error + console.error json + console.error response + callback JSON.stringify("failed checking author emailAddress") + ) + +checkIfEmailHasChangedAndIsUnique = (author, savedAuthor, callback) -> + if author.emailAddress == savedAuthor.emailAddress + callback null + else + checkEmailIsUnique author (err, isUnique) -> + if err? + callback err + else if isUnique + callback null + else + callback 'That email address is already in use.' + +parseSystemRoles = (author, callback) -> + roleEntries = _.pluck author.authorRoles, 'roleEntry' + systemRoles = _.where roleEntries, {lsType: 'System'} + delete author['authorRoles'] + callback null, author, systemRoles + + +diffSystemRolesWithSaved = (userName, systemRoles, savedSystemRoles, callback) -> + rolesToAdd = _.filter systemRoles, (sysRole) -> + !(_.findWhere savedSystemRoles, (id: sysRole.id))? + rolesToDelete = _.filter savedSystemRoles, (savedSysRole) -> + !(_.findWhere systemRoles, (id: savedSysRole.id))? + fetchSystemRoles rolesToAdd, (err, rolesToAdd) -> + if err? + callback err + else + flatAuthRolesToAdd = [] + _.each rolesToAdd, (role) -> + flatAuthorRole = + roleType: role.lsType + roleKind: role.lsKind + roleName: role.roleName + userName: userName + flatAuthRolesToAdd.push flatAuthorRole + flatAuthRolesToDelete = [] + _.each rolesToDelete, (role) -> + flatAuthorRole = + roleType: role.lsType + roleKind: role.lsKind + roleName: role.roleName + userName: userName + flatAuthRolesToDelete.push flatAuthorRole + callback null, flatAuthRolesToAdd, flatAuthRolesToDelete + +saveAuthorRoles = (rolesToCreate, cb) -> + request( + method: 'POST' + url: config.all.client.service.persistence.fullpath + "authorroles/saveRoles" + body: rolesToCreate + json: true + timeout: 6000000 + , (err, response, json) => + if err? + cb err, null + else + cb null, json + ) + +deleteAuthorRoles = (rolesToDelete, cb) -> + request( + method: 'POST' + url: config.all.client.service.persistence.fullpath + "authorroles/deleteRoles" + body: rolesToDelete + json: true + timeout: 6000000 + , (err, response, json) => + if err? + cb err, null + else + cb null, json + ) + +fetchSystemRoles = (incompleteSystemRoles, callback) -> + request( + method: 'GET' + url: config.all.client.service.persistence.fullpath + 'lsRoles?lsType=System' + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + allSystemRoles = json + ids = {} + _.each incompleteSystemRoles, (partialRole) -> + ids[partialRole.id] = true + filteredSystemRoles = _.filter allSystemRoles, (val) -> + return ids[val.id] + callback null, filteredSystemRoles + else + console.error 'got error trying to get fetch system roles' + console.error error + console.error json + callback 'failed to fetch system roles' + ) + + +createOrSignupAuthorInternal = (author, cb) -> + authStrategy = config.all.server.security.authstrategy + if authStrategy == 'database' + exports.signupNewAuthorInternal author, (err, savedAuthor) -> + cb err, savedAuthor + else + exports.createNewAuthorInternal author, (err, savedAuthor) -> + cb err, savedAuthor + +exports.signupNewAuthorInternal = (author, cb) -> + config = require '../conf/compiled/conf.js' + request = require 'request' + request( + method: 'POST' + url: config.all.client.service.persistence.fullpath + "authors/signupAuthor" + body: author + json: true + timeout: 6000000 + , (err, response, json) => + if err? + cb err, null + else if response.statusCode != 201 + cb json, null + else + cb null, json + ) \ No newline at end of file From 63f2f424ea7c42338af4e10477b216ed9a789644 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 2 Oct 2017 13:21:55 -0700 Subject: [PATCH 207/576] loginRoutes: Fixed change password route to use the username from the authenticated user in the session rather than allowing input from a form, and also cleaned up some English. passwordChange.jade: Removed "email or username" input from change password form. Change Password is behind the login page, so the only password being changed should be the one of the logged in user. CustomerSpecificServerFunctions: Switched changeAuth to use a new Roo API route "authorization/changePassword" instead of the basic Roo change password route that was not working. AuthorRoutes: Added new route to be used in account activation emails, which activates the user, then redirects them to passwordChange, which goes through login first. --- .../src/server/routes/loginRoutes.coffee | 5 ++-- .../ServerAPI/src/client/passwordChange.jade | 4 ---- .../CustomerSpecificServerFunctions.coffee | 24 ++++++++++++------- .../src/server/routes/AuthorRoutes.coffee | 12 ++++++++++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/modules/Login/src/server/routes/loginRoutes.coffee b/modules/Login/src/server/routes/loginRoutes.coffee index 545fca03a..8c5b914eb 100644 --- a/modules/Login/src/server/routes/loginRoutes.coffee +++ b/modules/Login/src/server/routes/loginRoutes.coffee @@ -159,7 +159,7 @@ exports.resetAuthenticationService = (req, resp) -> exports.changeAuthenticationService = (req, resp) -> callback = (results) -> console.log results - if results.indexOf("You password has been successfully been changed")>=0 + if results.indexOf("Your password has successfully been changed")>=0 req.flash 'error','Your new password is set.' resp.redirect '/login' else if results.indexOf("connection_error")>=0 @@ -172,7 +172,8 @@ exports.changeAuthenticationService = (req, resp) -> if global.specRunnerTestmode callback("Success") else - csUtilities.changeAuth req.body.user, req.body.oldPassword, req.body.newPassword, req.body.newPasswordAgain, callback + user = req.session.passport.user.username + csUtilities.changeAuth user, req.body.oldPassword, req.body.newPassword, req.body.newPasswordAgain, callback exports.resetpage = (req, res) -> user = null diff --git a/modules/ServerAPI/src/client/passwordChange.jade b/modules/ServerAPI/src/client/passwordChange.jade index 8042ba935..4b5404a73 100644 --- a/modules/ServerAPI/src/client/passwordChange.jade +++ b/modules/ServerAPI/src/client/passwordChange.jade @@ -10,10 +10,6 @@ block content h3 Change Password div.well form.form-horizontal(action="/passwordChange", method="post") - div.control-group - label.control-label Email or Username: - div.controls - input.span5(type="text", name="username") div.control-group label.control-label Old Password: div.controls diff --git a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee index 07b0c017a..fba0fe0ec 100644 --- a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee +++ b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee @@ -75,22 +75,28 @@ exports.resetAuth = (email, retFun) -> exports.changeAuth = (user, passOld, passNew, passNewAgain, retFun) -> config = require "#{ACAS_HOME}/conf/compiled/conf.js" request = require 'request' + body = + username: user + oldPassword: passOld + newPassword: passNew + newPasswordAgain: passNewAgain request( headers: accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' method: 'POST' - url: config.all.server.roologin.changeLink - form: - username: user - oldPassword: passOld - newPassword: passNew - newPasswordAgain: passNewAgain - json: false + url: config.all.client.service.persistence.fullpath+"authorization/changePassword" + body: body + json: true , (error, response, json) => + console.log error + console.log response.statusCode + console.log json if !error && response.statusCode == 200 - retFun JSON.stringify json + retFun "Your password has successfully been changed" + else if response.statusCode == 400 + retFun "Invalid password or new password does not match" else - console.log 'got ajax error trying authenticate a user' + console.log 'got ajax error trying change password' console.log error console.log json console.log response diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 2604c9743..7db5ecf10 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -15,6 +15,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.delete '/api/authors/:id', loginRoutes.ensureAuthenticated, exports.deleteAuthor app.post '/api/author', loginRoutes.ensureAuthenticated, exports.saveAuthor app.put '/api/author/:id', loginRoutes.ensureAuthenticated, exports.updateAuthor + app.get '/activateUser', exports.activateUserAndRedirectToChangePassword serverUtilityFunctions = require './ServerUtilityFunctions.js' _ = require 'underscore' @@ -845,6 +846,17 @@ fetchSystemRoles = (incompleteSystemRoles, callback) -> callback 'failed to fetch system roles' ) +exports.activateUserAndRedirectToChangePassword = (req, resp) -> + request( + method: 'GET' + url: config.all.client.service.persistence.fullpath + "authorization/activateUser?emailAddress=#{req.query.emailAddress}&activate=#{req.query.activate}" + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + resp.redirect '/passwordChange' + else + #redirect to error page + ) createOrSignupAuthorInternal = (author, cb) -> authStrategy = config.all.server.security.authstrategy From bbd4a8c557cf748ce832cfee034b20ed22bf5c4e Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 2 Oct 2017 16:48:31 -0700 Subject: [PATCH 208/576] Refactored reset password to use new Roo API route and fixed retFun for successful response. --- .../server/CustomerSpecificServerFunctions.coffee | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee index fba0fe0ec..8a0380015 100644 --- a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee +++ b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee @@ -57,15 +57,17 @@ exports.resetAuth = (email, retFun) -> headers: accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' method: 'POST' - url: config.all.server.roologin.resetLink - form: - emailAddress: email - json: false + url: config.all.client.service.persistence.fullpath+"authorization/resetPassword" + body: email + json: true , (error, response, json) => + console.log error + console.log response.statusCode + console.log json if !error && response.statusCode == 200 - retFun JSON.stringify json + retFun "Your new password has been sent to your email address." else - console.log 'got ajax error trying authenticate a user' + console.log 'got ajax error trying reset password' console.log error console.log json console.log response From 4d0cbc92fc289778218319239703790f3cb2cd2a Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 2 Oct 2017 18:21:01 -0700 Subject: [PATCH 209/576] fixes to make inventory work --- .../src/client/css/NewCmpdReg_nocompile.css | 1 + .../routes/InventoryServiceRoutes.coffee | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css index 6b3fb9f98..12e359042 100755 --- a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css +++ b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css @@ -1122,6 +1122,7 @@ div.MetaLotViewButtons.buttons { left: 556px; bottom: 40px; margin-left: 150px; + padding-right: 39px; /* margin-top: 5px; margin-left: 15px; diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 16e257b31..3b8619bb8 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -2548,16 +2548,16 @@ exports.getTubesFromBatchCodeInternal = (input, callback) => { "lsType": "container" , "lsKind": "tube", - "values":[ - { - "stateType":"metadata", - "stateKind":"information", - "valueType": "codeValue", - "valueKind": "status", - "operator": "!=", - "value":"expired" - } - ], +# "values":[ +# { +# "stateType":"metadata", +# "stateKind":"information", +# "valueType": "codeValue", +# "valueKind": "status", +# "operator": "!=", +# "value":"expired" +# } +# ], "secondInteractions":[ { "interactionType": "has member", @@ -2708,7 +2708,7 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) if err? callback err summaryInfo = prepareSummaryInfo fileEntryArray - getContainerTubeDefinitionCode (definitionCode) -> + exports.getContainerTubeDefinitionCode (definitionCode) -> if !definitionCode? error = level: 'error' @@ -2996,7 +2996,7 @@ checkBarcodesExist = (barcodes, callback) -> newBarcodes.push containerCodeEntry.requestLabel callback existingBarcodes, newBarcodes -checkParentWellContent = (fileEntryArray, callback) -> +exports.checkParentWellContent = (fileEntryArray, callback) -> #The purpose of this function is to check that the source vial content is compatible with the daughter content being loaded in, including #physical state must match #amount in parent vial would be negative if the daughter amount is removed @@ -3097,7 +3097,7 @@ prepareCreateParentVialsHTMLSummary = (hasError, hasWarning, errorMessages, summ htmlSummary += warningsBlock htmlSummary -getContainerTubeDefinitionCode = (callback) -> +exports.getContainerTubeDefinitionCode = (callback) -> exports.containersByTypeKindInternal 'definition container', 'tube', 'codetable', false, false, (definitionContainers) -> callback definitionContainers[0].code @@ -3232,11 +3232,11 @@ exports.validateDaughterVialsInternal = (vialsToValidate, callback) -> if dataTypeErrors? errorMessages.push dataTypeErrors... sourceBarcodes = _.pluck vialsToValidate, 'sourceVialBarcode' - checkBarcodesExist sourceBarcodes, (existingBarcodes, newBarcodes) -> - if newBarcodes? and newBarcodes.length > 0 + checkBarcodesExist sourceBarcodes, (existingSourceBarcodes, missingSourceBarcodes) -> + if missingSourceBarcodes? and missingSourceBarcodes.length > 0 error = level: 'error' - message: "The following source barcodes do not exist: " + newBarcodes.join ', ' + message: "The following source barcodes do not exist: " + missingSourceBarcodes.join ', ' errorMessages.push error destinationBarcodes = _.pluck vialsToValidate, 'destinationVialBarcode' checkBarcodesExist destinationBarcodes, (existingBarcodes, newBarcodes) -> @@ -3245,10 +3245,13 @@ exports.validateDaughterVialsInternal = (vialsToValidate, callback) -> level: 'error' message: "The following destination barcodes already exist: " + existingBarcodes.join ', ' errorMessages.push error - checkParentWellContent vialsToValidate, (parentWellContentErrors) -> - if parentWellContentErrors? - errorMessages.push parentWellContentErrors... + if missingSourceBarcodes.length > 0 callback null, errorMessages + else + checkParentWellContent vialsToValidate, (parentWellContentErrors) -> + if parentWellContentErrors? + errorMessages.push parentWellContentErrors... + callback null, errorMessages exports.createDaughterVials = (req, resp) -> if req.session?.passport?.user?.username? @@ -3274,7 +3277,7 @@ exports.createDaughterVials = (req, resp) -> resp.json response exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> - getContainerTubeDefinitionCode (definitionCode) -> + exports.getContainerTubeDefinitionCode (definitionCode) -> if !definitionCode? callback 'Could not find definition container for tube' tubesToCreate = [] From 681daebee593e89e9a47c1cb64e0a39a0bf71977 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 3 Oct 2017 09:49:40 -0700 Subject: [PATCH 210/576] Tweaked passwordChange page to make the ACAS text appear the same, but be a link so a user has a way to get out of the passwordChange page. --- modules/ServerAPI/src/client/passwordChange.jade | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/client/passwordChange.jade b/modules/ServerAPI/src/client/passwordChange.jade index 4b5404a73..191d27632 100644 --- a/modules/ServerAPI/src/client/passwordChange.jade +++ b/modules/ServerAPI/src/client/passwordChange.jade @@ -3,7 +3,9 @@ block content div.navbar.navbar-inverse.navbar-fixed-top div.navbar-inner div.container-fluid - div.brand ACAS + ul.nav + li + a.bv_headerName(href="/") ACAS div.container.loginContainer div.span8.offset1 From 3ac15763cbbc8051af5a94251450aae0eaf10630 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 3 Oct 2017 14:34:32 -0700 Subject: [PATCH 211/576] Commented out extra logging from createTube service, fixed dryRun and level->errorLevel attributes for bulk vial loaders to communicate properly with BasicFileValidateAndSave. Fixed datatype of amount and concentration to be numeric and to do parsing in the CSV file read. Consolidated summary HTML helper functions. Also removed superfluous README from ServerAPI that is no longer accurate. --- .../README.txt | 6 - .../routes/InventoryServiceRoutes.coffee | 149 ++++++++---------- 2 files changed, 69 insertions(+), 86 deletions(-) delete mode 100644 modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/createLiveDesignLiveReportForACAS/README.txt diff --git a/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/createLiveDesignLiveReportForACAS/README.txt b/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/createLiveDesignLiveReportForACAS/README.txt deleted file mode 100644 index 8e73f31d9..000000000 --- a/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/createLiveDesignLiveReportForACAS/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -#The create_lr_for_acas.py script relies on an up-to-date ldclient library. -#To install, first download the ldclient from the LD server that is paired with this ACAS server using: -#wget https://relevant-ld-server.onschrodinger.com/livedesign/ldclient.tar.gz -#Then unpack the war and make a symlink as follows: -#tar -zxf ldclient.tar.gz -#ln -s ldclient-version-build/ldclient ldclient \ No newline at end of file diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 578b96c94..8fff7c784 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -2263,12 +2263,12 @@ exports.createTubesInternal = (tubes, callCustom, callback) -> json: true timeout: 6000000 , (error, response, json) => - console.log "error" - console.log error - console.log "response" - console.log response - console.log "json" - console.log json +# console.log "error" +# console.log error +# console.log "response" +# console.log response +# console.log "json" +# console.log json if !error && response.statusCode == 200 # If call custom doesn't equal 0 then call custom callCustom = callCustom != "0" @@ -2633,7 +2633,7 @@ exports.loadParentVialsFromCSV = (req, resp) -> resp.json response exports.loadParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) -> - if dryRun + if dryRun == 'true' exports.validateParentVialsFromCSVInternal csvFileName, dryRun, (validationResponse) -> callback validationResponse else @@ -2674,14 +2674,14 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> checkBatchCodesExist fileEntryArray, (missingBatchCodeErrors) -> if missingBatchCodeErrors? and missingBatchCodeErrors.length > 0 error = - level: 'error' + errorLevel: 'error' message: "The following batches do not exist: "+ missingBatchCodeErrors.join ', ' validationResponse.errorMessages.push error barcodes = _.pluck fileEntryArray, 'destinationVialBarcode' checkBarcodesExist barcodes, (existingBarcodes, newBarcodes) -> if existingBarcodes? and existingBarcodes.length > 0 error = - level: 'error' + errorLevel: 'error' message: "The following barcodes already exist: " + existingBarcodes.join ', ' validationResponse.errorMessages.push error if validationResponse.errorMessages.length < 1 @@ -2690,7 +2690,7 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> callback validationResponse else error = - level: 'error' + errorLevel: 'error' message: "File cannot be found" validationResponse.errorMessages.push error callback validationResponse @@ -2716,7 +2716,7 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) getContainerTubeDefinitionCode (definitionCode) -> if !definitionCode? error = - level: 'error' + errorLevel: 'error' message: 'Could not find definition container for tube' createResponse.errorMessages.push error tubesToCreate = [] @@ -2731,14 +2731,14 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) wells: [ wellName: "A001" batchCode: entry.batchCode - amount: parseFloat(entry.amount) + amount: entry.amount amountUnits: entry.amountUnits physicalState: entry.physicalState recordedBy: user recordedDate: (new Date()).getTime() ] if entry.physicalState == 'solution' - tube.wells[0].batchConcentration = parseFloat(entry.concentration) + tube.wells[0].batchConcentration = entry.concentration tube.wells[0].batchConcUnits = entry.concUnits tube.wells[0].solventCode = entry.solvent tubesToCreate.push tube @@ -2750,17 +2750,17 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) createResponse.hasError = true console.error json error = - level: 'error' + errorLevel: 'error' message: json createResponse.errorMessages.push error else createResponse.hasError = false createResponse.commit = true - createResponse.results.htmlSummary = prepareCreateParentVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo + createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo callback createResponse else error = - level: 'error' + errorLevel: 'error' message: "File cannot be found" createResponse.errorMessages.push error callback createResponse @@ -2774,7 +2774,7 @@ exports.loadDaughterVialsFromCSV = (req, resp) -> resp.json response exports.loadDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callback) -> - if dryRun + if dryRun == 'true' exports.validateDaughterVialsFromCSVInternal csvFileName, dryRun, (err, validationResponse) -> if err? callback err @@ -2829,7 +2829,7 @@ exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) - callback null, validationResponse else error = - level: 'error' + errorLevel: 'error' message: "File cannot be found" validationResponse.errorMessages.push error callback validationResponse @@ -2848,7 +2848,7 @@ exports.createDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callbac commit: false if !exists error = - level: 'error' + errorLevel: 'error' message: "File cannot be found" createResponse.errorMessages.push error callback createResponse @@ -2861,13 +2861,13 @@ exports.createDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callbac exports.createDaughterVialsInternal fileEntryArray, user, (err, response) -> if err? error = - level: 'error' + errorLevel: 'error' message: err createResponse.errorMessages.push error else createResponse.hasError = false createResponse.commit = true - createResponse.results.htmlSummary = prepareCreateDaughterVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo + createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo callback null, createResponse @@ -2897,12 +2897,12 @@ createParentVialFileEntryArray = (csvFileName, callback) => fileEntry = batchCode: csvrow[PARENT_COMPOUND_LOT_INDEX].trim() destinationVialBarcode: csvrow[PARENT_DESTINATION_VIAL_INDEX].trim() - amount: csvrow[PARENT_AMOUNT_INDEX].trim() + amount: parseFloat(csvrow[PARENT_AMOUNT_INDEX].trim()) amountUnits: csvrow[PARENT_AMOUNT_UNITS_INDEX].trim() preparedBy: csvrow[PARENT_PREPARED_BY_INDEX].trim() preparedDate: csvrow[PARENT_PREPARED_DATE_INDEX].trim() physicalState: csvrow[PARENT_PHYSICAL_STATE_INDEX].trim() - concentration: csvrow[PARENT_CONCENTRATION_INDEX].trim() + concentration: parseFloat(csvrow[PARENT_CONCENTRATION_INDEX].trim()) concUnits:csvrow[PARENT_CONC_UNITS_INDEX].trim() solvent: csvrow[PARENT_SOLVENT_INDEX].trim() rowNumber: rowCount @@ -2925,12 +2925,12 @@ createDaughterVialFileEntryArray = (csvFileName, callback) => fileEntry = sourceVialBarcode: csvrow[DAUGHTER_SOURCE_VIAL_INDEX].trim() destinationVialBarcode: csvrow[DAUGHTER_DESTINATION_VIAL_INDEX].trim() - amount: csvrow[DAUGHTER_AMOUNT_INDEX].trim() + amount: parseFloat(csvrow[DAUGHTER_AMOUNT_INDEX].trim()) amountUnits: csvrow[DAUGHTER_AMOUNT_UNITS_INDEX].trim() preparedBy: csvrow[DAUGHTER_PREPARED_BY_INDEX].trim() preparedDate: csvrow[DAUGHTER_PREPARED_DATE_INDEX].trim() physicalState: csvrow[DAUGHTER_PHYSICAL_STATE_INDEX].trim() - concentration: csvrow[DAUGHTER_CONCENTRATION_INDEX].trim() + concentration: parseFloat(csvrow[DAUGHTER_CONCENTRATION_INDEX].trim()) concUnits: csvrow[DAUGHTER_CONC_UNITS_INDEX].trim() solvent: csvrow[DAUGHTER_SOLVENT_INDEX].trim() rowNumber: rowCount @@ -2957,7 +2957,7 @@ checkRequiredAttributes = (fileEntryArray, callback) -> missingAttributes.push attr if missingAttributes.length > 0 error = - level: 'error' + errorLevel: 'error' message: "Row #{entry.rowNumber} is missing the required attributes: " + missingAttributes.join ', ' requiredAttributeErrors.push error callback requiredAttributeErrors @@ -2965,14 +2965,14 @@ checkRequiredAttributes = (fileEntryArray, callback) -> checkDataTypeErrors = (fileEntryArray, callback) -> dataTypeErrors = [] _.each fileEntryArray, (entry) -> - if entry.amount? and entry.amount.length > 0 and isNaN(parseFloat(entry.amount)) + if entry.amount? and entry.amount.length > 0 and isNaN(entry.amount) error = - level: 'error' + errorLevel: 'error' message: "Row #{entry.rowNumber} contains the invalid amount: #{entry.amount}" dataTypeErrors.push error - if entry.concentration? and entry.concentration.length > 0 and isNaN(parseFloat(entry.concentration)) + if entry.concentration? and entry.concentration.length > 0 and isNaN(entry.concentration) error = - level: 'error' + errorLevel: 'error' message: "Row #{entry.rowNumber} contains the invalid concentration: #{entry.concentration}" dataTypeErrors.push error callback dataTypeErrors @@ -3016,27 +3016,27 @@ checkParentWellContent = (fileEntryArray, callback) -> parentWellContent = parentVialAndWellContent.wellContent[0] if parentWellContent.physicalState != fileEntry.physicalState error = - level: 'error' + errorLevel: 'error' message: "Daughter vial #{fileEntry.destinationVialBarcode} must be of the same physical state as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.physicalState}." errorMessages.push error - if fileEntry.physicalState == 'solution' and (Math.abs(parseFloat(fileEntry.concentration) - parentWellContent.batchConcentration) > 0.0001 or fileEntry.concUnits != parentWellContent.batchConcUnits) + if fileEntry.physicalState == 'solution' and (Math.abs(fileEntry.concentration - parentWellContent.batchConcentration) > 0.0001 or fileEntry.concUnits != parentWellContent.batchConcUnits) error = - level: 'error' + errorLevel: 'error' message: "Daughter vial #{fileEntry.destinationVialBarcode} must have the same concentration as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.batchConcentration} #{parentWellContent.batchConcUnits}." errorMessages.push error if parentWellContent.amountUnits != fileEntry.amountUnits error = - level: 'error' + errorLevel: 'error' message: "Daughter vial #{fileEntry.destinationVialBarcode} must use the same amount units as parent vial #{fileEntry.sourceVialBarcode}, which is in #{parentWellContent.amountUnits}." errorMessages.push error - else if parentWellContent.amount < parseFloat(fileEntry.amount) + else if parentWellContent.amount < fileEntry.amount error = - level: 'warning' + errorLevel: 'warning' message: "Creating daughter vial #{fileEntry.destinationVialBarcode} will remove more than the total amount currently in parent vial #{fileEntry.sourceVialBarcode}, leaving a negative amount in the parent vial." errorMessages.push error else error = - level: 'error' + errorLevel: 'error' message: "Could not find a well for barcode #{fileEntry.sourceVialBarcode}" errorMessages.push error console.log errorMessages @@ -3046,8 +3046,8 @@ checkParentWellContent = (fileEntryArray, callback) -> prepareValidationHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo) -> - errors = _.where errorMessages, {level: 'error'} - warnings = _.where errorMessages, {level: 'warning'} + errors = _.where errorMessages, {errorLevel: 'error'} + warnings = _.where errorMessages, {errorLevel: 'warning'} errorHeader = "

    Please fix the following errors and use the 'Back' button at the bottom of this screen to upload a new version of the file.

    " if hasWarning successHeader = "

    Please review the warnings and summary before uploading.

    " @@ -3079,11 +3079,17 @@ prepareValidationHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo htmlSummary += htmlSummaryInfo htmlSummary -prepareCreateParentVialsHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo) -> - errors = _.where errorMessages, {level: 'error'} - warnings = _.where errorMessages, {level: 'warning'} +prepareCreateVialsHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo) -> + errors = _.where errorMessages, {errorLevel: 'error'} + warnings = _.where errorMessages, {errorLevel: 'warning'} errorHeader = "

    An error occurred during uploading. If the messages below are unhelpful, you will need to contact your system administrator.

    " - #TODO implement HTML summary for successful upload + htmlSummaryInfo = "

    Summary

    Information:

    \n
      \n " + htmlSummaryInfo += "
    • Total Vials: #{summaryInfo.totalNumberOfVials}
    • " + htmlSummaryInfo += "
    • Solid Vials: #{summaryInfo.numSolidVials}
    • " + htmlSummaryInfo += "
    • Liquid Vials: #{summaryInfo.numLiquidVials}
    • " + if summaryInfo.totalBatchCodes? + htmlSummaryInfo += "
    • Unique Corporate Batch ID's: #{summaryInfo.totalBatchCodes}
    • " + htmlSummaryInfo += "\n
    " successHeader = "

    Upload completed.

    " errorsBlock = "\n

    Errors: #{errors.length}

    \n
      " _.each errors, (error) -> @@ -3100,6 +3106,8 @@ prepareCreateParentVialsHTMLSummary = (hasError, hasWarning, errorMessages, summ htmlSummary += successHeader if hasWarning htmlSummary += warningsBlock + if !hasError + htmlSummary += htmlSummaryInfo htmlSummary getContainerTubeDefinitionCode = (callback) -> @@ -3127,28 +3135,6 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> else callback null, updateWellsResponse -prepareCreateDaughterVialsHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo) -> - errors = _.where errorMessages, {level: 'error'} - warnings = _.where errorMessages, {level: 'warning'} - errorHeader = "

      An error occurred during uploading. If the messages below are unhelpful, you will need to contact your system administrator.

      " - successHeader = "

      Upload completed.

      " - errorsBlock = "\n

      Errors: #{errors.length}

      \n
        " - _.each errors, (error) -> - errorsBlock += "
      • #{error.message}
      • " - errorsBlock += "
      " - warningsBlock = "\n

      Warnings: #{warnings.length}

      \n

      Warnings provide information on issues found in the upload file. You can proceed with warnings; however, it is recommended that, if possible, you make the changes suggested by the warnings and upload a new version of the file by using the 'Back' button at the bottom of this screen.

      \n
        " - _.each warnings, (warning) -> - warningsBlock += "
      • #{warning.message}
      • " - warningsBlock += "
      " - htmlSummary = "" - if hasError - htmlSummary += errorHeader + errorsBlock - else - htmlSummary += successHeader - if hasWarning - htmlSummary += warningsBlock - htmlSummary - prepareSummaryInfo = (fileEntryArray) -> summaryInfo = totalNumberOfVials: fileEntryArray.length @@ -3237,23 +3223,26 @@ exports.validateDaughterVialsInternal = (vialsToValidate, callback) -> if dataTypeErrors? errorMessages.push dataTypeErrors... sourceBarcodes = _.pluck vialsToValidate, 'sourceVialBarcode' - checkBarcodesExist sourceBarcodes, (existingBarcodes, newBarcodes) -> - if newBarcodes? and newBarcodes.length > 0 + checkBarcodesExist sourceBarcodes, (existingSourceBarcodes, missingSourceBarcodes) -> + if missingSourceBarcodes? and missingSourceBarcodes.length > 0 error = - level: 'error' - message: "The following source barcodes do not exist: " + newBarcodes.join ', ' + errorLevel: 'error' + message: "The following source barcodes do not exist: " + missingSourceBarcodes.join ', ' errorMessages.push error destinationBarcodes = _.pluck vialsToValidate, 'destinationVialBarcode' checkBarcodesExist destinationBarcodes, (existingBarcodes, newBarcodes) -> if existingBarcodes? and existingBarcodes.length > 0 error = - level: 'error' + errorLevel: 'error' message: "The following destination barcodes already exist: " + existingBarcodes.join ', ' errorMessages.push error - checkParentWellContent vialsToValidate, (parentWellContentErrors) -> - if parentWellContentErrors? - errorMessages.push parentWellContentErrors... + if missingSourceBarcodes.length > 0 callback null, errorMessages + else + checkParentWellContent vialsToValidate, (parentWellContentErrors) -> + if parentWellContentErrors? + errorMessages.push parentWellContentErrors... + callback null, errorMessages exports.createDaughterVials = (req, resp) -> if req.session?.passport?.user?.username? @@ -3265,8 +3254,8 @@ exports.createDaughterVials = (req, resp) -> resp.statusCode = 500 resp.json err else - errors = _.where errorsAndWarnings, {level: 'error'} - warnings = _.where errorsAndWarnings, {level: 'warning'} + errors = _.where errorsAndWarnings, {errorLevel: 'error'} + warnings = _.where errorsAndWarnings, {errorLevel: 'warning'} if errors? and errors.length > 0 resp.statusCode = 400 resp.json errorsAndWarnings @@ -3294,14 +3283,14 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> wells: [ wellName: "A001" batchCode: entry.batchCode - amount: parseFloat(entry.amount) + amount: entry.amount amountUnits: entry.amountUnits physicalState: entry.physicalState recordedBy: user recordedDate: (new Date()).getTime() ] if entry.physicalState == 'solution' - tube.wells[0].batchConcentration = parseFloat(entry.concentration) + tube.wells[0].batchConcentration = entry.concentration tube.wells[0].batchConcUnits = entry.concUnits tube.wells[0].solventCode = entry.solvent tubesToCreate.push tube @@ -3327,7 +3316,7 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> lsValues: [ lsType: 'numericValue' lsKind: 'amount added' - numericValue: parseFloat(entry.amount) + numericValue: entry.amount unitKind: entry.amountUnits ] ] @@ -3340,7 +3329,7 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> _.each vialsToCreate, (entry) -> toDecrement = barcode: entry.sourceVialBarcode - amountToDecrement: parseFloat(entry.amount) + amountToDecrement: entry.amount amountToDecrementUnits: entry.amountUnits parentVialsToDecrement.push toDecrement decrementAmountsFromVials parentVialsToDecrement, user, (err, decrementVialsResponse) -> @@ -3421,4 +3410,4 @@ exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callb responseStub.parentVialCodeName = parentVial.codeName parentVialBarcode = _.findWhere parentVial.lsLabels, {lsType: 'barcode', lsKind: 'barcode', ignored: false} responseStub.parentVialBarcode = parentVialBarcode.labelText - callback null, responseStub \ No newline at end of file + callback null, responseStub From da82ecc79529600b6ff195ed0207740e71e87d20 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 3 Oct 2017 14:53:39 -0700 Subject: [PATCH 212/576] Added reporting of model save errors, and added guard clause for if systemRoleListController fails to initialize. --- modules/ServerAPI/src/client/AuthorEditor.coffee | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/ServerAPI/src/client/AuthorEditor.coffee b/modules/ServerAPI/src/client/AuthorEditor.coffee index ea315aa2e..926163f66 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.coffee +++ b/modules/ServerAPI/src/client/AuthorEditor.coffee @@ -502,13 +502,19 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_saving').show() console.log "handleSaveClicked" console.log JSON.stringify @model - @model.save() + @model.save null, + success: (model, response) -> + console.log 'save successful' + , + error: (model, response) -> + alert JSON.parse response.responseText prepareToSaveSystemRoles: => authorRoles = [] - @systemRoleListController.collection.each (role) => - authorRoles.push - roleEntry: role + if @systemRoleListController? + @systemRoleListController.collection.each (role) => + authorRoles.push + roleEntry: role ar = @model.get 'authorRoles' ar.push authorRoles... @model.set 'authorRoles', ar From 1be8d91cc2ac9720cea10f30dbd25a967c1ec86c Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 3 Oct 2017 15:31:38 -0700 Subject: [PATCH 213/576] Added code to decrementAmountsFromVials used in Daughter Vial creation to save changes to the parent change log. --- .../server/routes/InventoryServiceRoutes.coffee | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 8fff7c784..0213cb31b 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3118,6 +3118,7 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> vialBarcodes = _.pluck parentVialsToDecrement, 'barcode' exports.getWellContentByContainerLabelsInternal vialBarcodes, 'container', 'tube', 'barcode', 'barcode', (wellContentList, statusCode) -> wellsToUpdate = [] + changes = [] _.each parentVialsToDecrement, (toDecrement) -> oldContainerWellContent = _.findWhere wellContentList, {label: toDecrement.barcode} oldWellContent = oldContainerWellContent.wellContent[0] @@ -3127,13 +3128,24 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> amount: oldWellContent.amount - toDecrement.amountToDecrement recordedBy: user wellsToUpdate.push newWellContent + change = + codeName: oldContainerWellContent.containerCodeName + recordedBy: user + recordedDate: (new Date()).getMilliseconds() + entryType: 'UPDATE' + entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" + changes.push change console.log wellsToUpdate exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> console.log updateWellsStatusCode if updateWellsStatusCode != 204 callback "Error: #{updateWellsResponse}" else - callback null, updateWellsResponse + exports.containerLogsInternal changes, 0, (logs, statusCode) -> + if statusCode != 200 + callback logs + else + callback null, updateWellsResponse prepareSummaryInfo = (fileEntryArray) -> summaryInfo = @@ -3331,6 +3343,7 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> barcode: entry.sourceVialBarcode amountToDecrement: entry.amount amountToDecrementUnits: entry.amountUnits + daughterVialBarcode: entry.destinationVialBarcode parentVialsToDecrement.push toDecrement decrementAmountsFromVials parentVialsToDecrement, user, (err, decrementVialsResponse) -> if err? From 82dad187c8d899a84cbbb4ae386e677079f13e62 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Wed, 4 Oct 2017 11:15:33 -0700 Subject: [PATCH 214/576] tweaks to support lot inventory --- .../src/server/routes/InventoryServiceRoutes.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index f7f674e0d..130c9b57d 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3251,7 +3251,7 @@ exports.validateDaughterVialsInternal = (vialsToValidate, callback) -> if missingSourceBarcodes.length > 0 callback null, errorMessages else - checkParentWellContent vialsToValidate, (parentWellContentErrors) -> + exports.checkParentWellContent vialsToValidate, (parentWellContentErrors) -> if parentWellContentErrors? errorMessages.push parentWellContentErrors... callback null, errorMessages @@ -3411,6 +3411,11 @@ exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callb ] format = 'nestedstub' exports.advancedSearchContainersInternal itxSearch, format, (err, advSearchReturn) -> + if err? + callback err + return + if advSearchReturn.results.length < 1 + callback null, responseStub parentWell = advSearchReturn.results[0] responseStub.parentWellCodeName = parentWell.codeName parentWellLabel = _.findWhere parentWell.lsLabels, {lsType: 'name', lsKind: 'well name', ignored: false} From 9a85dba4597a0519216d6a4c73c06923c6a520b9 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 4 Oct 2017 11:38:04 -0700 Subject: [PATCH 215/576] Fixed mistakes in default configuration.json --- modules/CmpdReg/src/client/custom/configuration.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index 5c25477f2..f2b99776a 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -35,7 +35,7 @@ "useExactMass": true, "useProjectRolesToRestrictLotDetails": false, "showTareWeight": false, - "showTotalAmoundStored": false, + "showTotalAmountStored": false, "disableAliasEdit": false, "showLotInventory": true, "disableEditMyLots": false @@ -63,7 +63,7 @@ "unitTestDB": true, "initalDBLoad": true, "projectRestrictions": false, - "jchemVersion": "16.4.11.0" + "jchemVersion": "16.4.25.0" }, "bulkLoadSettings": { "useProjectRoles":false, From dd53f5eba79e5e062a2099d3007fca7e713f2a30 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Thu, 5 Oct 2017 20:01:22 -0700 Subject: [PATCH 216/576] fixed issues found in testing --- modules/Components/src/client/ACASFormFields.coffee | 5 +++++ modules/Components/src/client/AbstractFormController.coffee | 1 + 2 files changed, 6 insertions(+) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index f2544030f..249cdc8a9 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -102,6 +102,8 @@ class window.ACASFormAbstractFieldController extends Backbone.View @setPlaceholder @options.placeholder if @options.firstSelectText? @firstSelectText = @options.firstSelectText + if @options.tabIndex? + @setTabIndex @options.tabIndex @required = if @options.required? then @options.required else false setFormLabel: (value) -> @@ -134,6 +136,9 @@ class window.ACASFormAbstractFieldController extends Backbone.View enableInput: -> @$('input').removeAttr 'disabled' + setTabIndex: (index) -> + @$('input, select').attr 'tabindex', index + class window.ACASFormLSLabelFieldController extends ACASFormAbstractFieldController ### Launching controller must: diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 9736a722a..d2425404a 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -144,6 +144,7 @@ class window.AbstractThingFormController extends AbstractFormController modelDefaults: field.modelDefaults allowedFileTypes: field.fieldSettings.allowedFileTypes extendedLabel: field.fieldSettings.extendedLabel + tabIndex: field.fieldSettings.tabIndex switch field.fieldSettings.fieldType when 'label' From 42d4ca656af7361902bee378c89ba65ebc997a35 Mon Sep 17 00:00:00 2001 From: Fiona McNeil Date: Fri, 6 Oct 2017 08:56:06 -0700 Subject: [PATCH 217/576] Changed iframe height. --- modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css index 12e359042..c5e25f056 100755 --- a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css +++ b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css @@ -222,7 +222,7 @@ div { .MetaLotView .bv_lotInventory iframe { width: 700px; - height: 1000px; + height: 700px; } .NewLotSuccessView .labelCorpName { From 56aad0b87760e461f81fc582774da713dd7faa96 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 6 Oct 2017 16:59:43 -0700 Subject: [PATCH 218/576] locking down packages with "*" to specific versions --- package.json | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 11867916a..d495ebd5f 100644 --- a/package.json +++ b/package.json @@ -17,57 +17,57 @@ "dependencies": { "assert": "~1.1.1", "async": "^0.9.0", - "asyncblock": "*", + "asyncblock": "2.2.11", "backbone": "^1.2.3", "backbone-validation": "^0.11.5", - "bluebird": "*", - "bootstrap": "*", + "bluebird": "3.5.0", + "bootstrap": "3.3.7", "chai": "^3.5.0", "cookie-parser": "^1.4.3", "connect-flash": "~0.1.0", - "cron": "*", + "cron": "1.3.0", "csv-parse": "^1.1.1", "each": ">= 0.0.0", - "exports-loader": "*", + "exports-loader": "0.6.4", "express": "3.0.2", - "extract-text-webpack-plugin": "*", - "file-loader": "*", - "flat": "*", - "forever-monitor": "*", - "glob": "*", + "extract-text-webpack-plugin": "3.0.0", + "file-loader": "0.11.2", + "flat": "4.0.0", + "forever-monitor": "1.7.1", + "glob": "7.1.2", "jade": "0.35.0", "jasmine-core": "^2.4.1", "jasmine-jquery": "^2.1.1", "jquery": "^2.2.0", "jquery-file-upload-middleware": "https://github.com/mcneilco/jquery-file-upload-middleware.git", "json2csv": "^3.9.1", - "jszip": "*", + "jszip": "3.1.4", "less": "^2.6.0", "lodash": "^4.0.0", - "marked": "*", - "mocha": "*", + "marked": "0.3.6", + "mocha": "3.5.3", "mochawesome": "^2.0.2", - "mongojs": "*", - "ncp": "*", - "open-graph": "*", + "mongojs": "2.4.1", + "ncp": "2.0.0", + "open-graph": "0.2.3", "passport": ">= 0.0.0", "passport-local": ">= 0.0.0", - "properties": "*", - "properties-parser": "*", + "properties": "1.2.1", + "properties-parser": "0.3.1", "request": "2.34.0", - "request-promise": "*", + "request-promise": "4.2.1", "shelljs": "0.6.0", "socket.io": "^1.7.3", "passport.socketio": "^3.7.0", "promise": "7.1.1", - "style-loader": "*", + "style-loader": "0.18.2", "temporary": ">= 0.0.0", "underscore": ">= 0.0.0", "underscore-deep-extend": ">= 0.0.0", - "url-loader": "*", - "winston": "*", - "winston-mongodb": "*", - "yamljs": "*", + "url-loader": "0.5.9", + "winston": "0.5.9", + "winston-mongodb": "2.0.10", + "yamljs": "0.3.0", "webshot": "0.18.0", "archiver": "2.0.0" }, From 6913457ab4e7da35c727d26713442e3680c86462 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 6 Oct 2017 17:02:15 -0700 Subject: [PATCH 219/576] fixing winston package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d495ebd5f..d4d905fa9 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "underscore": ">= 0.0.0", "underscore-deep-extend": ">= 0.0.0", "url-loader": "0.5.9", - "winston": "0.5.9", + "winston": "2.3.1", "winston-mongodb": "2.0.10", "yamljs": "0.3.0", "webshot": "0.18.0", From b4a344b76f2d9883e62d8ff2f08c9062e96a7d09 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 9 Oct 2017 15:16:03 -0700 Subject: [PATCH 220/576] minor lot inventory fixes --- modules/CmpdReg/src/client/custom/LotView_Custom.inc | 6 +++++- modules/CmpdReg/src/client/custom/Lot_Custom.js | 7 +++++++ .../src/server/routes/InventoryServiceRoutes.coffee | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/LotView_Custom.inc b/modules/CmpdReg/src/client/custom/LotView_Custom.inc index 13c5154ae..0958d0522 100755 --- a/modules/CmpdReg/src/client/custom/LotView_Custom.inc +++ b/modules/CmpdReg/src/client/custom/LotView_Custom.inc @@ -77,11 +77,15 @@
    -
    +
    +
    + +
    +

    diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index 52ff3f248..b9b2ca6d0 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -249,6 +249,11 @@ $(function() { this.$('.synthesisDate').datepicker( "option", "dateFormat", "mm/dd/yy" ); this.$('.editAnalyticalFiles').hide(); this.$('.analyticalFiles').html('Add analytical files by editing lot after it is saved'); + } else { + if (window.configuration.metaLot.showLotInventory) { + this.$('.amountWrapper').hide(); + this.$('.barcodeWrapper').hide(); + } } return this; }, @@ -272,6 +277,7 @@ $(function() { comments: '', color: '', amount: null, + barcode: null, purity: null, physicalState: null, purityOperator: null, @@ -329,6 +335,7 @@ $(function() { amount: (jQuery.trim(this.$('.amount').val())=='') ? null : parseFloat(jQuery.trim(this.$('.amount').val())), + barcode: (jQuery.trim(this.$('.barcode').val())=='') ? null : jQuery.trim(this.$('.barcode').val()), retain: (jQuery.trim(this.$('.retain').val())=='') ? null : parseFloat(jQuery.trim(this.$('.retain').val())), diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 130c9b57d..e69583796 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3131,7 +3131,7 @@ decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> change = codeName: oldContainerWellContent.containerCodeName recordedBy: user - recordedDate: (new Date()).getMilliseconds() + recordedDate: new Date().getTime() entryType: 'UPDATE' entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" changes.push change From b880be7bbe418e736abdb2dfe0272647b0d2efb6 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 9 Oct 2017 15:22:41 -0700 Subject: [PATCH 221/576] Changed new salt sketcher to not show itself when marvin load is complete in MarvinJS mode. Fixes #258 --- modules/CmpdReg/src/client/src/Salt.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/CmpdReg/src/client/src/Salt.js b/modules/CmpdReg/src/client/src/Salt.js index 6bce90060..37d05395a 100755 --- a/modules/CmpdReg/src/client/src/Salt.js +++ b/modules/CmpdReg/src/client/src/Salt.js @@ -177,7 +177,6 @@ $(function() { sketcherInstance.addTemplate(window.marvinStructureTemplates[i]); } } - self.show(); self.sketcherLoaded = true; }, function (error) { alert("Cannot retrieve newSaltMarvinSketch sketcher instance from iframe:" + error); From d378322205c7f3578e9f711a1d1ae11e17455425 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 9 Oct 2017 16:03:36 -0700 Subject: [PATCH 222/576] Changed password change success function to set "returnTo" to /, then redirect to login, so user is sent to ACAS homepage instead of back to changePassword. Fixes #260 --- modules/Login/src/server/routes/loginRoutes.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/Login/src/server/routes/loginRoutes.coffee b/modules/Login/src/server/routes/loginRoutes.coffee index 8c5b914eb..7c4a87299 100644 --- a/modules/Login/src/server/routes/loginRoutes.coffee +++ b/modules/Login/src/server/routes/loginRoutes.coffee @@ -161,6 +161,7 @@ exports.changeAuthenticationService = (req, resp) -> console.log results if results.indexOf("Your password has successfully been changed")>=0 req.flash 'error','Your new password is set.' + req.session.returnTo = '/' resp.redirect '/login' else if results.indexOf("connection_error")>=0 req.flash 'error','Cannot connect to authentication service. Please contact an administrator.' From 4f79d4b105b747275e422f8328d1b7519fff2547 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 9 Oct 2017 16:28:48 -0700 Subject: [PATCH 223/576] Fixed LabelSequence modelSaveCallback. It no longer clears the LsRoles list, and it disables all inputs and selects and hides the update and cancel buttons. Fixes #262 --- .../ACASLabelSequences/src/client/ACASLabelSequences.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee index 77d5b0125..3824e1f6b 100644 --- a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee +++ b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee @@ -307,12 +307,14 @@ class window.ACASLabelSequenceController extends AbstractFormController @$('.bv_saving').hide() modelSaveCallback: (method, model) => - @$('.bv_save').show() + @$('.bv_save').hide() + @$('.bv_cancel').hide() @$('.bv_save').attr('disabled', 'disabled') unless @$('.bv_saveFailed').is(":visible") @$('.bv_saveComplete').show() @$('.bv_saving').hide() - @setupACASLabelSequenceRoleListController() + @$('input').prop 'disabled', true + @$('select').prop 'disabled', true @render() @trigger 'amClean' From dc4bb6681fd27e5e91aba45eb6d1ce4af847d014 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 10 Oct 2017 10:30:30 -0700 Subject: [PATCH 224/576] Added check to checkParentWellContent in daughter vial validation that if a batchCode was passed in, it needs to match the parent vial's batch code. Fixes #265 Refactored createDaughterVialsInternal to get parent well content earlier, fill in the parent vial's batch code if the daughter does not specify a batch code. Fixes #264 Also refactored decrementAmountsFromVials to accept the parentWellContent as a variable so we don't query that same data twice. Fixed another issue with the error/warning reporting regarding hasError / hasWarning. --- .../routes/InventoryServiceRoutes.coffee | 227 ++++++++++-------- 1 file changed, 123 insertions(+), 104 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index e69583796..26af77848 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -2655,7 +2655,7 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> dryRun: dryRun fileToParse: csvFileName htmlSummary: '' - hasError: true + hasError: false hasWarning: false errorMessages: [] transactionId: null @@ -2684,8 +2684,12 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> errorLevel: 'error' message: "The following barcodes already exist: " + existingBarcodes.join ', ' validationResponse.errorMessages.push error - if validationResponse.errorMessages.length < 1 - validationResponse.hasError = false + errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} + warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} + if errors.length > 0 + validationResponse.hasError = true + if warnings.length > 0 + validationResponse.hasWarning = true validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo callback validationResponse else @@ -2808,7 +2812,7 @@ exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) - dryRun: dryRun fileToParse: csvFileName htmlSummary: '' - hasError: true + hasError: false hasWarning: false errorMessages: [] transactionId: null @@ -2823,8 +2827,12 @@ exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) - callback err else validationResponse.errorMessages.push errorsAndWarnings... - if validationResponse.errorMessages.length < 1 - validationResponse.hasError = false + errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} + warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} + if errors.length > 0 + validationResponse.hasError = true + if warnings.length > 0 + validationResponse.hasWarning = true validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo callback null, validationResponse else @@ -3034,6 +3042,11 @@ exports.checkParentWellContent = (fileEntryArray, callback) -> errorLevel: 'warning' message: "Creating daughter vial #{fileEntry.destinationVialBarcode} will remove more than the total amount currently in parent vial #{fileEntry.sourceVialBarcode}, leaving a negative amount in the parent vial." errorMessages.push error + if fileEntry.batchCode? and fileEntry.batchCode != parentWellContent.batchCode + error = + errorLevel: 'error' + message: "Daughter vial #{fileEntry.destinationVialBarcode} must reference the same lot #{parentWellContent.batchCode} as the parent vial #{fileEntry.destinationVialBarcode}." + errorMessages.push error else error = errorLevel: 'error' @@ -3114,38 +3127,36 @@ exports.getContainerTubeDefinitionCode = (callback) -> exports.containersByTypeKindInternal 'definition container', 'tube', 'codetable', false, false, (definitionContainers) -> callback definitionContainers[0].code -decrementAmountsFromVials = (parentVialsToDecrement, user, callback) -> - vialBarcodes = _.pluck parentVialsToDecrement, 'barcode' - exports.getWellContentByContainerLabelsInternal vialBarcodes, 'container', 'tube', 'barcode', 'barcode', (wellContentList, statusCode) -> - wellsToUpdate = [] - changes = [] - _.each parentVialsToDecrement, (toDecrement) -> - oldContainerWellContent = _.findWhere wellContentList, {label: toDecrement.barcode} - oldWellContent = oldContainerWellContent.wellContent[0] - wellCode = oldWellContent.containerCodeName - newWellContent = - containerCodeName: wellCode - amount: oldWellContent.amount - toDecrement.amountToDecrement - recordedBy: user - wellsToUpdate.push newWellContent - change = - codeName: oldContainerWellContent.containerCodeName - recordedBy: user - recordedDate: new Date().getTime() - entryType: 'UPDATE' - entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" - changes.push change - console.log wellsToUpdate - exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> - console.log updateWellsStatusCode - if updateWellsStatusCode != 204 - callback "Error: #{updateWellsResponse}" - else - exports.containerLogsInternal changes, 0, (logs, statusCode) -> - if statusCode != 200 - callback logs - else - callback null, updateWellsResponse +decrementAmountsFromVials = (toDecrementList, parentWellContentList, user, callback) -> + wellsToUpdate = [] + changes = [] + _.each toDecrementList, (toDecrement) -> + oldContainerWellContent = _.findWhere parentWellContentList, {label: toDecrement.barcode} + oldWellContent = oldContainerWellContent.wellContent[0] + wellCode = oldWellContent.containerCodeName + newWellContent = + containerCodeName: wellCode + amount: oldWellContent.amount - toDecrement.amountToDecrement + recordedBy: user + wellsToUpdate.push newWellContent + change = + codeName: oldContainerWellContent.containerCodeName + recordedBy: user + recordedDate: new Date().getTime() + entryType: 'UPDATE' + entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" + changes.push change + console.log wellsToUpdate + exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> + console.log updateWellsStatusCode + if updateWellsStatusCode != 204 + callback "Error: #{updateWellsResponse}" + else + exports.containerLogsInternal changes, 0, (logs, statusCode) -> + if statusCode != 200 + callback logs + else + callback null, updateWellsResponse prepareSummaryInfo = (fileEntryArray) -> summaryInfo = @@ -3283,74 +3294,82 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> exports.getContainerTubeDefinitionCode (definitionCode) -> if !definitionCode? callback 'Could not find definition container for tube' - tubesToCreate = [] - _.each vialsToCreate, (entry) -> - tube = - barcode: entry.destinationVialBarcode - definition: definitionCode - recordedBy: user - createdUser: entry.preparedBy - createdDate: entry.createdDate - physicalState: entry.physicalState - wells: [ - wellName: "A001" - batchCode: entry.batchCode - amount: entry.amount - amountUnits: entry.amountUnits - physicalState: entry.physicalState + return + parentVialBarcodes = _.pluck vialsToCreate, 'sourceVialBarcode' + exports.getWellContentByContainerLabelsInternal parentVialBarcodes, 'container', 'tube', 'barcode', 'barcode', (parentWellContentList, statusCode) -> + tubesToCreate = [] + _.each vialsToCreate, (entry) -> + parentVialAndWellContent = _.findWhere parentWellContentList, {label: entry.sourceVialBarcode} + parentWellContent = parentVialAndWellContent.wellContent[0] + batchCode = parentWellContent.batchCode + if entry.batchCode? and entry.batchCode.length > 0 + batchCode = entry.batchCode + tube = + barcode: entry.destinationVialBarcode + definition: definitionCode recordedBy: user - recordedDate: (new Date()).getTime() - ] - if entry.physicalState == 'solution' - tube.wells[0].batchConcentration = entry.concentration - tube.wells[0].batchConcUnits = entry.concUnits - tube.wells[0].solventCode = entry.solvent - tubesToCreate.push tube - console.log JSON.stringify tubesToCreate - exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> - console.log statusCode - console.log json - if statusCode != 200 - callback json - else - interactionsToCreate = [] - _.each vialsToCreate, (entry) -> - interaction = - interactionType: 'added to' - interactionKind: 'well_well' - firstContainerBarcode: entry.sourceVialBarcode - secondContainerBarcode: entry.destinationVialBarcode - firstWellLabel: 'A001' - secondWellLabel: 'A001' - interactionStates: [ - lsType: 'metadata' - lsKind: 'information' - lsValues: [ - lsType: 'numericValue' - lsKind: 'amount added' - numericValue: entry.amount - unitKind: entry.amountUnits + createdUser: entry.preparedBy + createdDate: entry.createdDate + physicalState: entry.physicalState + wells: [ + wellName: "A001" + batchCode: batchCode + amount: entry.amount + amountUnits: entry.amountUnits + physicalState: entry.physicalState + recordedBy: user + recordedDate: (new Date()).getTime() + ] + if entry.physicalState == 'solution' + tube.wells[0].batchConcentration = entry.concentration + tube.wells[0].batchConcUnits = entry.concUnits + tube.wells[0].solventCode = entry.solvent + tubesToCreate.push tube + console.log JSON.stringify tubesToCreate + exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> + console.log statusCode + console.log json + if statusCode != 200 + callback json + else + interactionsToCreate = [] + _.each vialsToCreate, (entry) -> + interaction = + interactionType: 'added to' + interactionKind: 'well_well' + firstContainerBarcode: entry.sourceVialBarcode + secondContainerBarcode: entry.destinationVialBarcode + firstWellLabel: 'A001' + secondWellLabel: 'A001' + interactionStates: [ + lsType: 'metadata' + lsKind: 'information' + lsValues: [ + lsType: 'numericValue' + lsKind: 'amount added' + numericValue: entry.amount + unitKind: entry.amountUnits + ] ] - ] - interactionsToCreate.push interaction - exports.saveWellToWellInteractionsInternal interactionsToCreate, user, (err, itxResponse) -> - if err? - callback err - else - parentVialsToDecrement = [] - _.each vialsToCreate, (entry) -> - toDecrement = - barcode: entry.sourceVialBarcode - amountToDecrement: entry.amount - amountToDecrementUnits: entry.amountUnits - daughterVialBarcode: entry.destinationVialBarcode - parentVialsToDecrement.push toDecrement - decrementAmountsFromVials parentVialsToDecrement, user, (err, decrementVialsResponse) -> - if err? - callback err - else - #TODO see what this service should respond with - callback null, 'successfully created daughter vials' + interactionsToCreate.push interaction + exports.saveWellToWellInteractionsInternal interactionsToCreate, user, (err, itxResponse) -> + if err? + callback err + else + toDecrementList = [] + _.each vialsToCreate, (entry) -> + toDecrement = + barcode: entry.sourceVialBarcode + amountToDecrement: entry.amount + amountToDecrementUnits: entry.amountUnits + daughterVialBarcode: entry.destinationVialBarcode + toDecrementList.push toDecrement + decrementAmountsFromVials toDecrementList, parentWellContentList, user, (err, decrementVialsResponse) -> + if err? + callback err + else + #TODO see what this service should respond with + callback null, 'successfully created daughter vials' exports.advancedSearchContainers = (req, resp) -> exports.advancedSearchContainersInternal req.body, req.query.format, (err, response) -> From 0a035d00dd285f13130bba2519a0b725c3e78d6e Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 10 Oct 2017 10:42:48 -0700 Subject: [PATCH 225/576] Fixed calculation of unique batch codes to filter out undefined. Fixes #266 --- .../ServerAPI/src/server/routes/InventoryServiceRoutes.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 26af77848..f98f0c1dd 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3164,7 +3164,9 @@ prepareSummaryInfo = (fileEntryArray) -> numSolidVials: (_.where fileEntryArray, {physicalState: 'solid'}).length numLiquidVials: (_.where fileEntryArray, {physicalState: 'solution'}).length batchCodes = _.pluck fileEntryArray, 'batchCode' - if batchCodes? + batchCodes = _.filter batchCodes, (entry) -> + entry? + if batchCodes? and batchCodes.length > 0 summaryInfo.totalBatchCodes = (_.uniq batchCodes).length summaryInfo From 01fab093a1f51219c48985192b9d69246c11fbe4 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 10 Oct 2017 14:50:27 -0700 Subject: [PATCH 226/576] Made tweak suggested by developer on Indigo forums to force Ketcher to always set the Chiral flag --- public/lib/ketcher-2.0.0-alpha.3/ketcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.js b/public/lib/ketcher-2.0.0-alpha.3/ketcher.js index 2c9589f28..1e5ddd121 100644 --- a/public/lib/ketcher-2.0.0-alpha.3/ketcher.js +++ b/public/lib/ketcher-2.0.0-alpha.3/ketcher.js @@ -922,7 +922,7 @@ require("./struct"),require("./cis_trans"),require("./dfs"),require("./molfile") }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./cis_trans":8,"./dfs":9,"./molfile":12,"./sgroup":13,"./smiles":14,"./stereocenters":15,"./struct":16,"./struct_valence":17}],12:[function(require,module,exports){ (function (global){ -var Map=require("../util/map"),Set=require("../util/set"),Vec2=require("../util/vec2"),element=require("./element"),util=require("../util"),chem=global.chem=global.chem||{};chem.Molfile=function(){},chem.Molfile.loadRGroupFragments=!0,chem.Molfile.parseDecimalInt=function(e){var t=parseInt(e,10);return isNaN(t)?0:t},chem.Molfile.partitionLine=function(e,t,r){for(var i=[],a=0,n=0;a0&&(M.ifthen=g),M.resth=1==w,M.range=S,n[d]=M}else if("APO"==c)o.get("attpnt")||o.set("attpnt",new Map),o.get("attpnt").update(l.readKeyValuePairs(p));else if("ALS"==c){o.get("atomList")||o.set("atomList",new Map);var v=l.parsePropertyLineAtomList(l.partitionLine(p,[1,3,3,1,1,1]),l.partitionLineFixed(p.slice(10),4,!1));o.get("atomList").update(v),o.get("label")||o.set("label",new Map);for(var D in v)o.get("label").set(D,"L#")}else if("STY"==c)l.initSGroup(a,p);else if("SST"==c)l.applySGroupProp(a,"subtype",p);else if("SLB"==c)l.applySGroupProp(a,"label",p,!0);else if("SPL"==c)l.applySGroupProp(a,"parent",p,!0,!0);else if("SCN"==c)l.applySGroupProp(a,"connectivity",p);else if("SAL"==c)l.applySGroupArrayProp(a,"atoms",p,-1);else if("SBL"==c)l.applySGroupArrayProp(a,"bonds",p,-1);else if("SPA"==c)l.applySGroupArrayProp(a,"patoms",p,-1);else if("SMT"==c){var b=l.parseDecimalInt(p.slice(0,4))-1;a[b].data.subscript=p.slice(4).strip()}else"SDT"==c?l.applyDataSGroupDesc(a,p):"SDD"==c?l.applyDataSGroupInfoLine(a,p):"SCD"==c?l.applyDataSGroupDataLine(a,p,!1):"SED"==c&&l.applyDataSGroupDataLine(a,p,!0)}++r}return o},chem.Molfile.applyAtomProp=function(e,t,r,i){t.each(function(t,i){e.get(t)[r]=i})},chem.Molfile.parseCTabV2000=function(e,t){var r,i=new chem.Struct,a=chem.Molfile,n=a.parseDecimalInt(t[0]),l=a.parseDecimalInt(t[1]),o=a.parseDecimalInt(t[2]);i.isChiral=0!=a.parseDecimalInt(t[4]);var s=a.parseDecimalInt(t[5]),c=a.parseDecimalInt(t[10]),p=0,h=e.slice(p,p+n);p+=n;var u=e.slice(p,p+l);p+=l;var m=e.slice(p,p+o);p+=o+s;var f=h.map(a.parseAtomLine);for(r=0;r=0){var b=g[D-1];"GEN"===b.type&&(v.atoms=util.array(b.atoms))}}}for(S in g)chem.SGroup.addGroup(i,g[S],M);var N=[];for(S in g)chem.SGroup.filter(i,g[S],M),0!=g[S].atoms.length||g[S].allAtoms||N.push(S);for(r=0;rn+1&&i.push(e.slice(n+1,r)),n=r);return r>n+1&&i.push(e.slice(n+1,r)),n=r,i},chem.Molfile.splitonce=function(e,t){var r=e.indexOf(t);return[e.slice(0,r),e.slice(r+1)]},chem.Molfile.splitSGroupDef=function(e){for(var t=[],r=0,i=!1,a=0;a0&&t.push(e.strip()),t},chem.Molfile.parseBracedNumberList=function(e,t){if(!e)return null;var r=[];e=e.strip(),e=e.substr(1,e.length-2);var i=e.split(" ");t=t||0;for(var a=1;a0&&(d.ifthen=u),d.resth=1==m,d.range=f,i[l]=d,a++}}}for(var g in r)for(var w=0;wr[s];)++s;n[s]=n[s]||{},Set.mergeIn(n[s],t[a])}var c=[],p=[],h=[];for(a=0;a0,e.rxnArrows.count()>1)throw new Error("Reaction may not contain more than one arrow");if(this.molfile="",this.reaction){if(e.rgroups.count()>0)throw new Error("Unable to save the structure - reactions with r-groups are not supported at the moment");var a=chem.MolfileSaver.getComponents(e),n=a.reactants,l=a.products,o=n.concat(l);this.molfile="$RXN\n\n\n\n"+util.paddedInt(n.length,3)+util.paddedInt(l.length,3)+util.paddedInt(0,3)+"\n";for(var s=0;s0){if(!r){var u=new chem.MolfileSaver(!1).getCTab(e.getScaffold(),e.rgroups);return this.molfile="$MDL REV 1\n$MOL\n$HDR\n\n\n\n$END HDR\n",this.molfile+="$CTAB\n"+u+"$END CTAB\n",e.rgroups.each(function(t,r){this.molfile+="$RGP\n",this.writePaddedNumber(t,3),this.molfile+="\n",r.frags.each(function(t,r){var i=new chem.MolfileSaver(!1).getCTab(e.getFragment(r));this.molfile+="$CTAB\n"+i+"$END CTAB\n"},this),this.molfile+="$END RGP\n"},this),this.molfile+="$END MOL\n",this.molfile}e=e.getScaffold()}return this.molecule=e.clone(),this.prepareSGroups(t,i),this.writeHeader(),this.writeCTab2000(),this.molfile},chem.MolfileSaver.prototype.writeHeader=function(){var e=new Date;this.writeCR(),this.writeWhiteSpace(2),this.write("Ketcher"),this.writeWhiteSpace(),this.writeCR((e.getMonth()+1).toPaddedString(2)+e.getDate().toPaddedString(2)+(e.getFullYear()%100).toPaddedString(2)+e.getHours().toPaddedString(2)+e.getMinutes().toPaddedString(2)+"2D 1 1.00000 0.00000 0"),this.writeCR()},chem.MolfileSaver.prototype.write=function(e){this.molfile+=e},chem.MolfileSaver.prototype.writeCR=function(e){0==arguments.length&&(e=""),this.molfile+=e+"\n"},chem.MolfileSaver.prototype.writeWhiteSpace=function(e){0==arguments.length&&(e=1),e.times(function(){this.write(" ")},this)},chem.MolfileSaver.prototype.writePadded=function(e,t){this.write(e),this.writeWhiteSpace(t-e.length)},chem.MolfileSaver.prototype.writePaddedNumber=function(e,t){var r=(e-0).toString();this.writeWhiteSpace(t-r.length),this.write(r)},chem.MolfileSaver.prototype.writePaddedFloat=function(e,t,r){this.write(util.paddedFloat(e,t,r))},chem.MolfileSaver.prototype.writeCTab2000Header=function(){this.writePaddedNumber(this.molecule.atoms.count(),3),this.writePaddedNumber(this.molecule.bonds.count(),3),this.writePaddedNumber(0,3),this.writeWhiteSpace(3),this.writePaddedNumber(this.molecule.isChiral?1:0,3),this.writePaddedNumber(0,3),this.writeWhiteSpace(12),this.writePaddedNumber(999,3),this.writeCR(" V2000")},chem.MolfileSaver.prototype.writeCTab2000=function(e){this.writeCTab2000Header(),this.mapping={};var t=1,r=[],i=[];for(this.molecule.atoms.each(function(e,a){this.writePaddedFloat(a.pp.x,10,4),this.writePaddedFloat(-a.pp.y,10,4),this.writePaddedFloat(0,10,4),this.writeWhiteSpace();var n=a.label;null!=a.atomList?(n="L",r.push(e)):null==element.getElementByLabel(n)&&-1==["A","Q","X","*","R#"].indexOf(n)&&(n="C",i.push(e)),this.writePadded(n,3),this.writePaddedNumber(0,2),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),Object.isUndefined(a.hCount)&&(a.hCount=0),this.writePaddedNumber(a.hCount,3),Object.isUndefined(a.stereoCare)&&(a.stereoCare=0),this.writePaddedNumber(a.stereoCare,3),this.writePaddedNumber(a.explicitValence<0?0:0==a.explicitValence?15:a.explicitValence,3),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),Object.isUndefined(a.aam)&&(a.aam=0),this.writePaddedNumber(a.aam,3),Object.isUndefined(a.invRet)&&(a.invRet=0),this.writePaddedNumber(a.invRet,3),Object.isUndefined(a.exactChangeFlag)&&(a.exactChangeFlag=0),this.writePaddedNumber(a.exactChangeFlag,3),this.writeCR(),this.mapping[e]=t,t++},this),this.bondMapping={},t=1,this.molecule.bonds.each(function(e,r){this.bondMapping[e]=t++,this.writePaddedNumber(this.mapping[r.begin],3),this.writePaddedNumber(this.mapping[r.end],3),this.writePaddedNumber(r.type,3),Object.isUndefined(r.stereo)&&(r.stereo=0),this.writePaddedNumber(r.stereo,3),this.writeWhiteSpace(3),Object.isUndefined(r.topology)&&(r.topology=0),this.writePaddedNumber(r.topology,3),Object.isUndefined(r.reactingCenterStatus)&&(r.reactingCenterStatus=0),this.writePaddedNumber(r.reactingCenterStatus,3),this.writeCR()},this);i.length>0;)this.write("A "),this.writePaddedNumber(i[0]+1,3),this.writeCR(),this.writeCR(this.molecule.atoms.get(i[0]).label),i.splice(0,1);var a=new Array,n=new Array,l=new Array,o=new Array,s=new Array,c=new Array,p=new Array,h=new Array,u=new Array;this.molecule.atoms.each(function(e,t){if(0!=t.charge&&a.push([e,t.charge]),0!=t.isotope&&n.push([e,t.isotope]),0!=t.radical&&l.push([e,t.radical]),null!=t.rglabel&&"R#"==t.label)for(var r=0;r<32;r++)t.rglabel&1<0||t.range.length>0){var r=" 1 "+util.paddedInt(e,3)+" "+util.paddedInt(t.ifthen,3)+" "+util.paddedInt(t.resth?1:0,3)+" "+t.range;s.push(r)}});var m=function(e,t){for(;t.length>0;){for(var r=new Array;t.length>0&&r.length<8;)r.push(t[0]),t.splice(0,1);this.write(e),this.writePaddedNumber(r.length,3),r.each(function(e){this.writeWhiteSpace(),this.writePaddedNumber(this.mapping[e[0]],3),this.writeWhiteSpace(),this.writePaddedNumber(e[1],3)},this),this.writeCR()}};m.call(this,"M CHG",a),m.call(this,"M ISO",n),m.call(this,"M RAD",l),m.call(this,"M RGP",o);for(var f=0;f0)for(f=0;f=0&&(this.write("M SPL"),this.writePaddedNumber(1,3),this.writeWhiteSpace(1),this.writePaddedNumber(N,3),this.writeWhiteSpace(1),this.writePaddedNumber(M[T],3),this.writeCR()),"SRU"==A.type&&A.data.connectivity){var C="";C+=" ",C+=util.stringPadded(N.toString(),3),C+=" ",C+=util.stringPadded(A.data.connectivity,3,!0),this.write("M SCN"),this.writePaddedNumber(1,3),this.write(C.toUpperCase()),this.writeCR()}"SRU"==A.type&&(this.write("M SMT "),this.writePaddedNumber(N,3),this.writeWhiteSpace(),this.write(A.data.subscript||"n"),this.writeCR()),this.writeCR(A.saveToMolfile(this.molecule,M,this.mapping,this.bondMapping))}this.writeCR("M END")},chem.Molfile.parseRxn=function(e){var t=chem.Molfile,r=e[0].strip().split(" ");return r.length>1&&"V3000"==r[1]?t.parseRxn3000(e):t.parseRxn2000(e)},chem.Molfile.parseRxn2000=function(e){var t=chem.Molfile;e=e.slice(4);var r=t.partitionLine(e[0],t.fmtInfo.rxnItemsPartition),i=r[0]-0,a=r[1]-0,n=r[2]-0;e=e.slice(1);for(var l=[];e.length>0&&"$MOL"==e[0].substr(0,4);){e=e.slice(1);for(var o=0;o2?r[2]-0:0,l=function(e){util.assert(e,"CTab format invalid")},o=[],s=[],c=null,p=[],h=0;h0&&(M.ifthen=g),M.resth=1==w,M.range=S,n[d]=M}else if("APO"==c)o.get("attpnt")||o.set("attpnt",new Map),o.get("attpnt").update(l.readKeyValuePairs(p));else if("ALS"==c){o.get("atomList")||o.set("atomList",new Map);var v=l.parsePropertyLineAtomList(l.partitionLine(p,[1,3,3,1,1,1]),l.partitionLineFixed(p.slice(10),4,!1));o.get("atomList").update(v),o.get("label")||o.set("label",new Map);for(var D in v)o.get("label").set(D,"L#")}else if("STY"==c)l.initSGroup(a,p);else if("SST"==c)l.applySGroupProp(a,"subtype",p);else if("SLB"==c)l.applySGroupProp(a,"label",p,!0);else if("SPL"==c)l.applySGroupProp(a,"parent",p,!0,!0);else if("SCN"==c)l.applySGroupProp(a,"connectivity",p);else if("SAL"==c)l.applySGroupArrayProp(a,"atoms",p,-1);else if("SBL"==c)l.applySGroupArrayProp(a,"bonds",p,-1);else if("SPA"==c)l.applySGroupArrayProp(a,"patoms",p,-1);else if("SMT"==c){var b=l.parseDecimalInt(p.slice(0,4))-1;a[b].data.subscript=p.slice(4).strip()}else"SDT"==c?l.applyDataSGroupDesc(a,p):"SDD"==c?l.applyDataSGroupInfoLine(a,p):"SCD"==c?l.applyDataSGroupDataLine(a,p,!1):"SED"==c&&l.applyDataSGroupDataLine(a,p,!0)}++r}return o},chem.Molfile.applyAtomProp=function(e,t,r,i){t.each(function(t,i){e.get(t)[r]=i})},chem.Molfile.parseCTabV2000=function(e,t){var r,i=new chem.Struct,a=chem.Molfile,n=a.parseDecimalInt(t[0]),l=a.parseDecimalInt(t[1]),o=a.parseDecimalInt(t[2]);i.isChiral=0!=a.parseDecimalInt(t[4]);var s=a.parseDecimalInt(t[5]),c=a.parseDecimalInt(t[10]),p=0,h=e.slice(p,p+n);p+=n;var u=e.slice(p,p+l);p+=l;var m=e.slice(p,p+o);p+=o+s;var f=h.map(a.parseAtomLine);for(r=0;r=0){var b=g[D-1];"GEN"===b.type&&(v.atoms=util.array(b.atoms))}}}for(S in g)chem.SGroup.addGroup(i,g[S],M);var N=[];for(S in g)chem.SGroup.filter(i,g[S],M),0!=g[S].atoms.length||g[S].allAtoms||N.push(S);for(r=0;rn+1&&i.push(e.slice(n+1,r)),n=r);return r>n+1&&i.push(e.slice(n+1,r)),n=r,i},chem.Molfile.splitonce=function(e,t){var r=e.indexOf(t);return[e.slice(0,r),e.slice(r+1)]},chem.Molfile.splitSGroupDef=function(e){for(var t=[],r=0,i=!1,a=0;a0&&t.push(e.strip()),t},chem.Molfile.parseBracedNumberList=function(e,t){if(!e)return null;var r=[];e=e.strip(),e=e.substr(1,e.length-2);var i=e.split(" ");t=t||0;for(var a=1;a0&&(d.ifthen=u),d.resth=1==m,d.range=f,i[l]=d,a++}}}for(var g in r)for(var w=0;wr[s];)++s;n[s]=n[s]||{},Set.mergeIn(n[s],t[a])}var c=[],p=[],h=[];for(a=0;a0,e.rxnArrows.count()>1)throw new Error("Reaction may not contain more than one arrow");if(this.molfile="",this.reaction){if(e.rgroups.count()>0)throw new Error("Unable to save the structure - reactions with r-groups are not supported at the moment");var a=chem.MolfileSaver.getComponents(e),n=a.reactants,l=a.products,o=n.concat(l);this.molfile="$RXN\n\n\n\n"+util.paddedInt(n.length,3)+util.paddedInt(l.length,3)+util.paddedInt(0,3)+"\n";for(var s=0;s0){if(!r){var u=new chem.MolfileSaver(!1).getCTab(e.getScaffold(),e.rgroups);return this.molfile="$MDL REV 1\n$MOL\n$HDR\n\n\n\n$END HDR\n",this.molfile+="$CTAB\n"+u+"$END CTAB\n",e.rgroups.each(function(t,r){this.molfile+="$RGP\n",this.writePaddedNumber(t,3),this.molfile+="\n",r.frags.each(function(t,r){var i=new chem.MolfileSaver(!1).getCTab(e.getFragment(r));this.molfile+="$CTAB\n"+i+"$END CTAB\n"},this),this.molfile+="$END RGP\n"},this),this.molfile+="$END MOL\n",this.molfile}e=e.getScaffold()}return this.molecule=e.clone(),this.prepareSGroups(t,i),this.writeHeader(),this.writeCTab2000(),this.molfile},chem.MolfileSaver.prototype.writeHeader=function(){var e=new Date;this.writeCR(),this.writeWhiteSpace(2),this.write("Ketcher"),this.writeWhiteSpace(),this.writeCR((e.getMonth()+1).toPaddedString(2)+e.getDate().toPaddedString(2)+(e.getFullYear()%100).toPaddedString(2)+e.getHours().toPaddedString(2)+e.getMinutes().toPaddedString(2)+"2D 1 1.00000 0.00000 0"),this.writeCR()},chem.MolfileSaver.prototype.write=function(e){this.molfile+=e},chem.MolfileSaver.prototype.writeCR=function(e){0==arguments.length&&(e=""),this.molfile+=e+"\n"},chem.MolfileSaver.prototype.writeWhiteSpace=function(e){0==arguments.length&&(e=1),e.times(function(){this.write(" ")},this)},chem.MolfileSaver.prototype.writePadded=function(e,t){this.write(e),this.writeWhiteSpace(t-e.length)},chem.MolfileSaver.prototype.writePaddedNumber=function(e,t){var r=(e-0).toString();this.writeWhiteSpace(t-r.length),this.write(r)},chem.MolfileSaver.prototype.writePaddedFloat=function(e,t,r){this.write(util.paddedFloat(e,t,r))},chem.MolfileSaver.prototype.writeCTab2000Header=function(){this.writePaddedNumber(this.molecule.atoms.count(),3),this.writePaddedNumber(this.molecule.bonds.count(),3),this.writePaddedNumber(0,3),this.writeWhiteSpace(3),this.writePaddedNumber(1,3),this.writePaddedNumber(0,3),this.writeWhiteSpace(12),this.writePaddedNumber(999,3),this.writeCR(" V2000")},chem.MolfileSaver.prototype.writeCTab2000=function(e){this.writeCTab2000Header(),this.mapping={};var t=1,r=[],i=[];for(this.molecule.atoms.each(function(e,a){this.writePaddedFloat(a.pp.x,10,4),this.writePaddedFloat(-a.pp.y,10,4),this.writePaddedFloat(0,10,4),this.writeWhiteSpace();var n=a.label;null!=a.atomList?(n="L",r.push(e)):null==element.getElementByLabel(n)&&-1==["A","Q","X","*","R#"].indexOf(n)&&(n="C",i.push(e)),this.writePadded(n,3),this.writePaddedNumber(0,2),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),Object.isUndefined(a.hCount)&&(a.hCount=0),this.writePaddedNumber(a.hCount,3),Object.isUndefined(a.stereoCare)&&(a.stereoCare=0),this.writePaddedNumber(a.stereoCare,3),this.writePaddedNumber(a.explicitValence<0?0:0==a.explicitValence?15:a.explicitValence,3),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),this.writePaddedNumber(0,3),Object.isUndefined(a.aam)&&(a.aam=0),this.writePaddedNumber(a.aam,3),Object.isUndefined(a.invRet)&&(a.invRet=0),this.writePaddedNumber(a.invRet,3),Object.isUndefined(a.exactChangeFlag)&&(a.exactChangeFlag=0),this.writePaddedNumber(a.exactChangeFlag,3),this.writeCR(),this.mapping[e]=t,t++},this),this.bondMapping={},t=1,this.molecule.bonds.each(function(e,r){this.bondMapping[e]=t++,this.writePaddedNumber(this.mapping[r.begin],3),this.writePaddedNumber(this.mapping[r.end],3),this.writePaddedNumber(r.type,3),Object.isUndefined(r.stereo)&&(r.stereo=0),this.writePaddedNumber(r.stereo,3),this.writeWhiteSpace(3),Object.isUndefined(r.topology)&&(r.topology=0),this.writePaddedNumber(r.topology,3),Object.isUndefined(r.reactingCenterStatus)&&(r.reactingCenterStatus=0),this.writePaddedNumber(r.reactingCenterStatus,3),this.writeCR()},this);i.length>0;)this.write("A "),this.writePaddedNumber(i[0]+1,3),this.writeCR(),this.writeCR(this.molecule.atoms.get(i[0]).label),i.splice(0,1);var a=new Array,n=new Array,l=new Array,o=new Array,s=new Array,c=new Array,p=new Array,h=new Array,u=new Array;this.molecule.atoms.each(function(e,t){if(0!=t.charge&&a.push([e,t.charge]),0!=t.isotope&&n.push([e,t.isotope]),0!=t.radical&&l.push([e,t.radical]),null!=t.rglabel&&"R#"==t.label)for(var r=0;r<32;r++)t.rglabel&1<0||t.range.length>0){var r=" 1 "+util.paddedInt(e,3)+" "+util.paddedInt(t.ifthen,3)+" "+util.paddedInt(t.resth?1:0,3)+" "+t.range;s.push(r)}});var m=function(e,t){for(;t.length>0;){for(var r=new Array;t.length>0&&r.length<8;)r.push(t[0]),t.splice(0,1);this.write(e),this.writePaddedNumber(r.length,3),r.each(function(e){this.writeWhiteSpace(),this.writePaddedNumber(this.mapping[e[0]],3),this.writeWhiteSpace(),this.writePaddedNumber(e[1],3)},this),this.writeCR()}};m.call(this,"M CHG",a),m.call(this,"M ISO",n),m.call(this,"M RAD",l),m.call(this,"M RGP",o);for(var f=0;f0)for(f=0;f=0&&(this.write("M SPL"),this.writePaddedNumber(1,3),this.writeWhiteSpace(1),this.writePaddedNumber(N,3),this.writeWhiteSpace(1),this.writePaddedNumber(M[T],3),this.writeCR()),"SRU"==A.type&&A.data.connectivity){var C="";C+=" ",C+=util.stringPadded(N.toString(),3),C+=" ",C+=util.stringPadded(A.data.connectivity,3,!0),this.write("M SCN"),this.writePaddedNumber(1,3),this.write(C.toUpperCase()),this.writeCR()}"SRU"==A.type&&(this.write("M SMT "),this.writePaddedNumber(N,3),this.writeWhiteSpace(),this.write(A.data.subscript||"n"),this.writeCR()),this.writeCR(A.saveToMolfile(this.molecule,M,this.mapping,this.bondMapping))}this.writeCR("M END")},chem.Molfile.parseRxn=function(e){var t=chem.Molfile,r=e[0].strip().split(" ");return r.length>1&&"V3000"==r[1]?t.parseRxn3000(e):t.parseRxn2000(e)},chem.Molfile.parseRxn2000=function(e){var t=chem.Molfile;e=e.slice(4);var r=t.partitionLine(e[0],t.fmtInfo.rxnItemsPartition),i=r[0]-0,a=r[1]-0,n=r[2]-0;e=e.slice(1);for(var l=[];e.length>0&&"$MOL"==e[0].substr(0,4);){e=e.slice(1);for(var o=0;o2?r[2]-0:0,l=function(e){util.assert(e,"CTab format invalid")},o=[],s=[],c=null,p=[],h=0;h Date: Wed, 11 Oct 2017 15:39:27 -0700 Subject: [PATCH 227/576] Added line to hide Add Ls Role button after save comes back #270 --- modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee index 3824e1f6b..dfc6c4e0b 100644 --- a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee +++ b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee @@ -315,6 +315,7 @@ class window.ACASLabelSequenceController extends AbstractFormController @$('.bv_saving').hide() @$('input').prop 'disabled', true @$('select').prop 'disabled', true + @$('.bv_addLabelSequenceRoleButton').hide() @render() @trigger 'amClean' From 6c3aedc5e78bd50f8aaaa01050d7b90afcd0bd2e Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 11 Oct 2017 16:36:11 -0700 Subject: [PATCH 228/576] Completed updateAuthorAndRolesInternal route that was missing an else case for when permissions are all in order for an update but the ls roles have not been changed. Fixes #272 Also fixed an internal function that still said resp.json instead of callback. --- .../src/server/routes/AuthorRoutes.coffee | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 7db5ecf10..e69d78ddc 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -122,7 +122,7 @@ exports.updateAuthorModulePreferencesInternal = (userName, moduleName, settings, exports.updateAuthorInternal = (author, callback) -> if global.specRunnerTestmode authorServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/AuthorServiceTestJSON.js' - resp.json authorServiceTestJSON.updateAuthor + callback authorServiceTestJSON.updateAuthor else config = require '../conf/compiled/conf.js' # if author.has('transactionOptions') @@ -639,6 +639,18 @@ exports.updateAuthorAndRolesInternal = (author, user, callback) -> callback err else callback null, response + else + #roles have not changed, just update the author + exports.updateAuthorInternal author, (updatedAuthor, statusCode) -> + if statusCode != 200 + callback updatedAuthor + else + #save successful. Fetch the new author and return. + exports.getAuthorByUsernameInternal author.userName, (response, statusCode) -> + if statusCode != 200 + callback err + else + callback null, response validateAuthorAttributes = (author, callback) -> requiredAttrs = ['firstName', 'lastName', 'userName', 'emailAddress'] From 690a2a4180b9608524c20268e75b91fbb40226bd Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 12 Oct 2017 11:45:37 -0700 Subject: [PATCH 229/576] feature/sel-required-attributes: Issue #274 (Specify required SEL attributes in Protocol Editor), fix styling for curve display params --- .../ServerAPI/conf/ProtocolConfJSON.coffee | 3 ++ modules/ServerAPI/src/client/Protocol.coffee | 44 +++++++++++++++++++ modules/ServerAPI/src/client/Protocol.html | 3 +- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/conf/ProtocolConfJSON.coffee b/modules/ServerAPI/conf/ProtocolConfJSON.coffee index 3f65708ad..97bd4f053 100644 --- a/modules/ServerAPI/conf/ProtocolConfJSON.coffee +++ b/modules/ServerAPI/conf/ProtocolConfJSON.coffee @@ -179,6 +179,9 @@ , typeName: "protocol" kindName: "review status" + , + typeName: "protocol" + kindName: "sel required attribute" , typeName: "assay" kindName: "scientist" diff --git a/modules/ServerAPI/src/client/Protocol.coffee b/modules/ServerAPI/src/client/Protocol.coffee index 5340ba8d2..3b2354491 100644 --- a/modules/ServerAPI/src/client/Protocol.coffee +++ b/modules/ServerAPI/src/client/Protocol.coffee @@ -66,6 +66,16 @@ class window.Protocol extends BaseEntity projectCodeValue + getSelRequiredAttr: (attr) => + selRequiredAttrCodeValue = @.get('lsStates').getOrCreateValueByTypeAndKind "metadata", "protocol metadata", "codeValue", attr + if selRequiredAttrCodeValue.get('codeValue') is undefined or selRequiredAttrCodeValue.get('codeValue') is "" + selRequiredAttrCodeValue.set codeValue: "false" + selRequiredAttrCodeValue.set codeType: "protocol" + selRequiredAttrCodeValue.set codeKind: "sel required attribute" + selRequiredAttrCodeValue.set codeOrigin: "ACAS DDICT" + + selRequiredAttrCodeValue + getCurveDisplayMin: -> minY = @.get('lsStates').getOrCreateValueByTypeAndKind "metadata", "screening assay", "numericValue", "curve display min" if (minY.get('numericValue') is undefined or minY.get('numericValue') is "") and @isNew() @@ -235,6 +245,7 @@ class window.ProtocolBaseController extends BaseEntityController @$('.bv_group_projectCode').hide() else @setupProjectSelect() + @setupSelRequiredAttrs() @render() @listenTo @model, 'sync', @modelSyncCallback @listenTo @model, 'change', @modelChangeCallback @@ -324,6 +335,39 @@ class window.ProtocolBaseController extends BaseEntityController @trigger 'amDirty' #need to put this after the first time @attachFileListController is rendered or else the module will always start off dirty @model.trigger 'change' + setupSelRequiredAttrs: => + #get codetable values for required sel attrs + $.ajax + type: 'GET' + url: "/api/codetables/protocol/sel required attribute" + dataType: 'json' + error: (err) -> + alert 'Could not get list of sel required attributes' + success: (json) => + unless json.length == 0 + for attr in json + @setupSelRequiredAttrCheckboxes attr + + setupSelRequiredAttrCheckboxes: (attr) => + camelCaseAttrCode = attr.code.replace /\s(.)/g, (match, group1) -> group1.toUpperCase() + @$('.bv_selRequiredAttributesSection').append '
    +
    ' + + currentVal = @model.getSelRequiredAttr attr.code + if currentVal.get('codeValue') is "true" + @$(".bv_#{camelCaseAttrCode}").attr 'checked', 'checked' + else + @$(".bv_#{camelCaseAttrCode}").removeAttr 'checked' + @$(".bv_#{camelCaseAttrCode}").on "click", => + @handleSelRequiredAttrChkbxChanged attr.code, camelCaseAttrCode + + handleSelRequiredAttrChkbxChanged: (attrCode, camelCaseAttrCode) => + currentVal = @model.getSelRequiredAttr attrCode + unless currentVal.isNew() + currentVal.set 'ignored', true + currentVal = @model.getSelRequiredAttr attrCode + currentVal.set 'codeValue', JSON.stringify(@$(".bv_#{camelCaseAttrCode}").is(":checked")) + handleDeleteStatusChosen: => @$(".bv_deleteButtons").removeClass "hide" @$(".bv_okayButton").addClass "hide" diff --git a/modules/ServerAPI/src/client/Protocol.html b/modules/ServerAPI/src/client/Protocol.html index 4ccab610d..292ed55ca 100644 --- a/modules/ServerAPI/src/client/Protocol.html +++ b/modules/ServerAPI/src/client/Protocol.html @@ -88,10 +88,11 @@
    +
    - + From fdde72c77d954fd082dcedcd2b2e1fa14c172a8e Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 12 Oct 2017 14:09:16 -0700 Subject: [PATCH 230/576] Added new config: client.compoundInventory.daughterVials.strictMatchPhysicalState=false, which turns unit and physical state mismatches into warnings instead of errors. Changed createDaughterVialsInternal to skip decrementing from the parent vials if any of these properties are mismatched. Refactored decrementAmountsFromVials to pass in additional fields. Fixes #276 --- conf/config.properties.example | 1 + .../routes/InventoryServiceRoutes.coffee | 76 ++++++++++--------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 6719e298c..25f064771 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -553,3 +553,4 @@ client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER client.entity.saveInitialsCorpName=false client.compoundInventory.enforceUppercaseBarcodes=false +client.compoundInventory.daughterVials.strictMatchPhysicalState=false diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index f98f0c1dd..0ce42be86 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3017,6 +3017,10 @@ exports.checkParentWellContent = (fileEntryArray, callback) -> #concentration and concentration units match if solution errorMessages = [] vialBarcodes = _.pluck fileEntryArray, 'sourceVialBarcode' + strictMatch = config.all.client.compoundInventory.daughterVials.strictMatchPhysicalState + flexibleErrorLevel = 'warning' + if strictMatch? and strictMatch + flexibleErrorLevel = 'error' exports.getWellContentByContainerLabelsInternal vialBarcodes, 'container', 'tube', 'barcode', 'barcode', (wellContentList, statusCode) -> _.each fileEntryArray, (fileEntry) -> parentVialAndWellContent = _.findWhere wellContentList, {label: fileEntry.sourceVialBarcode} @@ -3024,17 +3028,17 @@ exports.checkParentWellContent = (fileEntryArray, callback) -> parentWellContent = parentVialAndWellContent.wellContent[0] if parentWellContent.physicalState != fileEntry.physicalState error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must be of the same physical state as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.physicalState}." errorMessages.push error if fileEntry.physicalState == 'solution' and (Math.abs(fileEntry.concentration - parentWellContent.batchConcentration) > 0.0001 or fileEntry.concUnits != parentWellContent.batchConcUnits) error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must have the same concentration as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.batchConcentration} #{parentWellContent.batchConcUnits}." errorMessages.push error if parentWellContent.amountUnits != fileEntry.amountUnits error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must use the same amount units as parent vial #{fileEntry.sourceVialBarcode}, which is in #{parentWellContent.amountUnits}." errorMessages.push error else if parentWellContent.amount < fileEntry.amount @@ -3131,32 +3135,40 @@ decrementAmountsFromVials = (toDecrementList, parentWellContentList, user, callb wellsToUpdate = [] changes = [] _.each toDecrementList, (toDecrement) -> - oldContainerWellContent = _.findWhere parentWellContentList, {label: toDecrement.barcode} + oldContainerWellContent = _.findWhere parentWellContentList, {label: toDecrement.sourceVialBarcode} oldWellContent = oldContainerWellContent.wellContent[0] - wellCode = oldWellContent.containerCodeName - newWellContent = - containerCodeName: wellCode - amount: oldWellContent.amount - toDecrement.amountToDecrement - recordedBy: user - wellsToUpdate.push newWellContent - change = - codeName: oldContainerWellContent.containerCodeName - recordedBy: user - recordedDate: new Date().getTime() - entryType: 'UPDATE' - entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" - changes.push change + #Check that the amount is valid to decrement. + differentState = (oldWellContent.physicalState != toDecrement.physicalState) + concentrationMismatch = (toDecrement.physicalState == 'solution' and (Math.abs(toDecrement.concentration - oldWellContent.batchConcentration) > 0.0001 or toDecrement.concUnits != oldWellContent.batchConcUnits)) + unitMismatch = (oldWellContent.amountUnits != toDecrement.amountUnits) + if !differentState and !concentrationMismatch and !unitMismatch + wellCode = oldWellContent.containerCodeName + newWellContent = + containerCodeName: wellCode + amount: oldWellContent.amount - toDecrement.amount + recordedBy: user + wellsToUpdate.push newWellContent + change = + codeName: oldContainerWellContent.containerCodeName + recordedBy: user + recordedDate: new Date().getTime() + entryType: 'UPDATE' + entry: "Amount #{toDecrement.amount} #{toDecrement.amountUnits} taken out to create daughter vial #{toDecrement.destinationVialBarcode}" + changes.push change console.log wellsToUpdate - exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> - console.log updateWellsStatusCode - if updateWellsStatusCode != 204 - callback "Error: #{updateWellsResponse}" - else - exports.containerLogsInternal changes, 0, (logs, statusCode) -> - if statusCode != 200 - callback logs - else - callback null, updateWellsResponse + if wellsToUpdate.length > 0 + exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> + console.log updateWellsStatusCode + if updateWellsStatusCode != 204 + callback "Error: #{updateWellsResponse}" + else + exports.containerLogsInternal changes, 0, (logs, statusCode) -> + if statusCode != 200 + callback logs + else + callback null, updateWellsResponse + else + callback null prepareSummaryInfo = (fileEntryArray) -> summaryInfo = @@ -3358,15 +3370,7 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> if err? callback err else - toDecrementList = [] - _.each vialsToCreate, (entry) -> - toDecrement = - barcode: entry.sourceVialBarcode - amountToDecrement: entry.amount - amountToDecrementUnits: entry.amountUnits - daughterVialBarcode: entry.destinationVialBarcode - toDecrementList.push toDecrement - decrementAmountsFromVials toDecrementList, parentWellContentList, user, (err, decrementVialsResponse) -> + decrementAmountsFromVials vialsToCreate, parentWellContentList, user, (err, decrementVialsResponse) -> if err? callback err else From 7baa6c5491b3cebca5d2f21acdaa169f2a08247a Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 12 Oct 2017 14:13:16 -0700 Subject: [PATCH 231/576] bugfix/creg-fix-add-alias-clear-type-picklist-bug: Issue #227 (CReg - Click add alias clears selected option in type picklists) - need to update typeKind after type is selected --- modules/CmpdReg/src/client/src/AddAlias.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/src/AddAlias.js b/modules/CmpdReg/src/client/src/AddAlias.js index 10760c99b..03654315d 100644 --- a/modules/CmpdReg/src/client/src/AddAlias.js +++ b/modules/CmpdReg/src/client/src/AddAlias.js @@ -313,7 +313,10 @@ newAlias = new AliasModel(formValues); return this.trigger("isDirty", newAlias); } else { - return this.model.set(formValues); + this.model.set(formValues); + var typeKind = this.model.get('lsType') + ':' + this.model.get('lsKind'); + this.model.set({'typeKind': typeKind}); + return this; } }; From 27ea3ecfdb317b5d451d9858c5e65b377f89fce8 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 12 Oct 2017 14:17:31 -0700 Subject: [PATCH 232/576] Renamed ketcher folder from ketcher-2.0.0-alpha.3 to ketcher-2.0.0-alpha.3_custom because we have made a custom tweak to ketcher.js to always set the Chiral flag on molecules sent from Ketcher. Fixed all references to the folder name. --- modules/CmpdReg/src/client/src/EditParent.js | 2 +- .../CmpdReg/src/client/src/RegistrationSearch.js | 2 +- modules/CmpdReg/src/client/src/Salt.js | 2 +- modules/CmpdReg/src/client/src/SaltForm.js | 2 +- modules/CmpdReg/src/client/src/SearchForm.js | 2 +- .../src/client/ACASFormChemicalStructure.coffee | 2 +- .../LICENSE | 0 .../demo.html | 0 .../favicon.ico | Bin .../ketcher.css | 0 .../ketcher.eot | Bin .../ketcher.html | 0 .../ketcher.js | 0 .../ketcher.py | 0 .../ketcher.svg | 0 .../ketcher.ttf | Bin .../ketcher.woff | Bin .../logo.jpg | Bin .../prototype-min.js | 0 .../raphael.min.js | 0 .../templates.sdf | 0 21 files changed, 6 insertions(+), 6 deletions(-) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/LICENSE (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/demo.html (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/favicon.ico (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.css (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.eot (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.html (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.js (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.py (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.svg (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.ttf (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/ketcher.woff (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/logo.jpg (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/prototype-min.js (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/raphael.min.js (100%) rename public/lib/{ketcher-2.0.0-alpha.3 => ketcher-2.0.0-alpha.3_custom}/templates.sdf (100%) diff --git a/modules/CmpdReg/src/client/src/EditParent.js b/modules/CmpdReg/src/client/src/EditParent.js index e172079f4..ad2b8f3e4 100644 --- a/modules/CmpdReg/src/client/src/EditParent.js +++ b/modules/CmpdReg/src/client/src/EditParent.js @@ -65,7 +65,7 @@ $(function () { }); } else if (this.useKetcher) { - this.$('#editParentMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/cmpdReg/ketcher/"); + this.$('#editParentMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/cmpdReg/ketcher/"); this.$('#editParentMarvinSketch').on('load', function () { self.ketcher = self.$('#editParentMarvinSketch')[0].contentWindow.ketcher; self.ketcher.setMolecule(self.options.parentModel.get('molStructure')); diff --git a/modules/CmpdReg/src/client/src/RegistrationSearch.js b/modules/CmpdReg/src/client/src/RegistrationSearch.js index 7c9b92783..bb4f9b879 100755 --- a/modules/CmpdReg/src/client/src/RegistrationSearch.js +++ b/modules/CmpdReg/src/client/src/RegistrationSearch.js @@ -67,7 +67,7 @@ $(function() { }); } else if (this.useKetcher) { - this.$('#registrationSearchMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/cmpdReg/ketcher/"); + this.$('#registrationSearchMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/cmpdReg/ketcher/"); this.$('#registrationSearchMarvinSketch').on('load', function () { self.ketcher = self.$('#registrationSearchMarvinSketch')[0].contentWindow.ketcher; }); diff --git a/modules/CmpdReg/src/client/src/Salt.js b/modules/CmpdReg/src/client/src/Salt.js index 37d05395a..32e1c6a8f 100755 --- a/modules/CmpdReg/src/client/src/Salt.js +++ b/modules/CmpdReg/src/client/src/Salt.js @@ -183,7 +183,7 @@ $(function() { }); } else if (this.useKetcher) { - this.$('#newSaltMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/cmpdReg/ketcher/"); + this.$('#newSaltMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/cmpdReg/ketcher/"); this.$('#newSaltMarvinSketch').on('load', function () { self.ketcher = self.$('#newSaltMarvinSketch')[0].contentWindow.ketcher; self.sketcherLoaded = true; diff --git a/modules/CmpdReg/src/client/src/SaltForm.js b/modules/CmpdReg/src/client/src/SaltForm.js index b6ec00564..05f30ea6c 100755 --- a/modules/CmpdReg/src/client/src/SaltForm.js +++ b/modules/CmpdReg/src/client/src/SaltForm.js @@ -309,7 +309,7 @@ $(function() { }); } else if (this.useKetcher) { - this.$('#saltFormMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/cmpdReg/ketcher/"); + this.$('#saltFormMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/cmpdReg/ketcher/"); this.$('#saltFormMarvinSketch').on('load', function () { self.ketcher = self.$('#saltFormMarvinSketch')[0].contentWindow.ketcher; if (!self.sketcherLoaded) { diff --git a/modules/CmpdReg/src/client/src/SearchForm.js b/modules/CmpdReg/src/client/src/SearchForm.js index 67049e173..1b666f83f 100755 --- a/modules/CmpdReg/src/client/src/SearchForm.js +++ b/modules/CmpdReg/src/client/src/SearchForm.js @@ -106,7 +106,7 @@ $(function() { }); } else if (this.useKetcher) { - this.$('#searchMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/cmpdReg/ketcher/"); + this.$('#searchMarvinSketch').attr('src',"/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/cmpdReg/ketcher/"); this.$('#searchMarvinSketch').on('load', function () { self.ketcher = self.$('#searchMarvinSketch')[0].contentWindow.ketcher; }); diff --git a/modules/Components/src/client/ACASFormChemicalStructure.coffee b/modules/Components/src/client/ACASFormChemicalStructure.coffee index 0cd3cf35f..70f857464 100644 --- a/modules/Components/src/client/ACASFormChemicalStructure.coffee +++ b/modules/Components/src/client/ACASFormChemicalStructure.coffee @@ -110,7 +110,7 @@ class window.KetcherChemicalStructureController extends Backbone.View $(@el).empty() $(@el).html @template() - searchFrameURL = "/lib/ketcher-2.0.0-alpha.3/ketcher.html?api_path=/api/chemStructure/ketcher/" + searchFrameURL = "/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html?api_path=/api/chemStructure/ketcher/" @$('.bv_sketcherIFrame').attr 'src', searchFrameURL diff --git a/public/lib/ketcher-2.0.0-alpha.3/LICENSE b/public/lib/ketcher-2.0.0-alpha.3_custom/LICENSE similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/LICENSE rename to public/lib/ketcher-2.0.0-alpha.3_custom/LICENSE diff --git a/public/lib/ketcher-2.0.0-alpha.3/demo.html b/public/lib/ketcher-2.0.0-alpha.3_custom/demo.html similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/demo.html rename to public/lib/ketcher-2.0.0-alpha.3_custom/demo.html diff --git a/public/lib/ketcher-2.0.0-alpha.3/favicon.ico b/public/lib/ketcher-2.0.0-alpha.3_custom/favicon.ico similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/favicon.ico rename to public/lib/ketcher-2.0.0-alpha.3_custom/favicon.ico diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.css b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.css similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.css rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.css diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.eot b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.eot similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.eot rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.eot diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.html b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.html rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.html diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.js b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.js similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.js rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.js diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.py b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.py similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.py rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.py diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.svg b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.svg similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.svg rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.svg diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.ttf b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.ttf similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.ttf rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.ttf diff --git a/public/lib/ketcher-2.0.0-alpha.3/ketcher.woff b/public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.woff similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/ketcher.woff rename to public/lib/ketcher-2.0.0-alpha.3_custom/ketcher.woff diff --git a/public/lib/ketcher-2.0.0-alpha.3/logo.jpg b/public/lib/ketcher-2.0.0-alpha.3_custom/logo.jpg similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/logo.jpg rename to public/lib/ketcher-2.0.0-alpha.3_custom/logo.jpg diff --git a/public/lib/ketcher-2.0.0-alpha.3/prototype-min.js b/public/lib/ketcher-2.0.0-alpha.3_custom/prototype-min.js similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/prototype-min.js rename to public/lib/ketcher-2.0.0-alpha.3_custom/prototype-min.js diff --git a/public/lib/ketcher-2.0.0-alpha.3/raphael.min.js b/public/lib/ketcher-2.0.0-alpha.3_custom/raphael.min.js similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/raphael.min.js rename to public/lib/ketcher-2.0.0-alpha.3_custom/raphael.min.js diff --git a/public/lib/ketcher-2.0.0-alpha.3/templates.sdf b/public/lib/ketcher-2.0.0-alpha.3_custom/templates.sdf similarity index 100% rename from public/lib/ketcher-2.0.0-alpha.3/templates.sdf rename to public/lib/ketcher-2.0.0-alpha.3_custom/templates.sdf From 8d8891b24b335fd4ae46101a4aef2e6e7cd3c940 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 12 Oct 2017 16:23:47 -0700 Subject: [PATCH 233/576] bugfix/creg-admin-browsers-clear-modal-backdrops: Fixes Issue #279 (Clear modal backdrops after deleting code entry from creg admin browser) --- .../client/AbstractCmpdRegAdminBrowser.coffee | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee index 68901f4f7..be4fb31df 100644 --- a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee +++ b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee @@ -43,26 +43,10 @@ class window.CmpdRegAdminSimpleSearchController extends AbstractFormController @$(".bv_doSearch").attr("disabled", false) if e.keyCode is ENTER_KEY $(':focus').blur() - @handleDoSearchClicked() + @trigger 'searchRequested' else @$(".bv_doSearch").attr("disabled", true) - handleDoSearchClicked: => - $(".bv_cmpdRegAdminTableController").addClass "hide" - $(".bv_errorOccurredPerformingSearch").addClass "hide" - cmpdRegAdminSearchTerm = $.trim(@$(".bv_cmpdRegAdminSearchTerm").val()) - if cmpdRegAdminSearchTerm isnt "" - $(".bv_noMatchingCmpdRegAdminsFoundMessage").addClass "hide" - $(".bv_cmpdRegAdminBrowserSearchInstructions").addClass "hide" - $(".bv_searchCmpdRegAdminsStatusIndicator").removeClass "hide" - if !window.conf.browser.enableSearchAll and cmpdRegAdminSearchTerm is "*" - $(".bv_moreSpecificCmpdRegAdminSearchNeeded").removeClass "hide" - else - $(".bv_searchingCmpdRegAdminsMessage").removeClass "hide" - $(".bv_cmpdRegAdminSearchTerm").html cmpdRegAdminSearchTerm - $(".bv_moreSpecificCmpdRegAdminSearchNeeded").addClass "hide" - @doSearch cmpdRegAdminSearchTerm - doSearch: (cmpdRegAdminSearchTerm) => # disable the search text field while performing a search @$(".bv_cmpdRegAdminSearchTerm").attr "disabled", true @@ -122,19 +106,15 @@ class window.CmpdRegAdminSummaryTableController extends Backbone.View render: => @template = _.template($('#CmpdRegAdminSummaryTableView').html()) $(@el).html @template(@options.toDisplay) - if @collection.models.length is 0 - $(".bv_noMatchingCmpdRegAdminsFoundMessage").removeClass "hide" - # display message indicating no results were found - else - $(".bv_noMatchingCmpdRegAdminsFoundMessage").addClass "hide" - @collection.each (admin) => - prsc = new CmpdRegAdminRowSummaryController - model: admin - prsc.on "gotClick", @selectedRowChanged - @$("tbody").append prsc.render().el + #will always be instantiated with at least one model in collection + @collection.each (admin) => + prsc = new CmpdRegAdminRowSummaryController + model: admin + prsc.on "gotClick", @selectedRowChanged + @$("tbody").append prsc.render().el - @$("table").dataTable oLanguage: - sSearch: "Filter results: " #rename summary table's search bar + @$("table").dataTable oLanguage: + sSearch: "Filter results: " #rename summary table's search bar @ @@ -170,6 +150,7 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View urlRoot: "/api/CmpdRegAdmin/#{@entityTypePlural}" toDisplay: @toDisplay @searchController.render() + @searchController.on "searchRequested", @handleSearchRequested @searchController.on "searchReturned", @setupCmpdRegAdminSummaryTable @searchController.on "createNewCmpdRegAdmin", @handleCreateNewCmpdRegAdminClicked #@searchController.on "resetSearch", @destroyCmpdRegAdminSummaryTable @@ -177,7 +158,7 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View setupCmpdRegAdminSummaryTable: (cmpdRegAdmins) => @destroyCmpdRegAdminSummaryTable() - $(".bv_searchingCmpdRegAdminsMessage").addClass "hide" + @$(".bv_searchingCmpdRegAdminsMessage").addClass "hide" if cmpdRegAdmins is null @$(".bv_errorOccurredPerformingSearch").removeClass "hide" @@ -185,14 +166,14 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View @$(".bv_noMatchingCmpdRegAdminsFoundMessage").removeClass "hide" @$(".bv_cmpdRegAdminTableController").html "" else - $(".bv_searchCmpdRegAdminsStatusIndicator").addClass "hide" + @$(".bv_searchCmpdRegAdminsStatusIndicator").addClass "hide" @$(".bv_cmpdRegAdminTableController").removeClass "hide" @cmpdRegAdminSummaryTable = new CmpdRegAdminSummaryTableController collection: new CmpdRegAdminList cmpdRegAdmins toDisplay: @toDisplay @cmpdRegAdminSummaryTable.on "selectedRowUpdated", @selectedCmpdRegAdminUpdated - $(".bv_cmpdRegAdminTableController").html @cmpdRegAdminSummaryTable.render().el + @$(".bv_cmpdRegAdminTableController").html @cmpdRegAdminSummaryTable.render().el selectedCmpdRegAdminUpdated: (cmpdRegAdmin) => @trigger "selectedCmpdRegAdminUpdated" @@ -202,12 +183,28 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View model: new window[@entityClass] cmpdRegAdmin.attributes readOnly: true - $('.bv_cmpdRegAdminController').html @cmpdRegAdminController.render().el - $(".bv_cmpdRegAdminController").removeClass("hide") - $(".bv_cmpdRegAdminControllerContainer").removeClass("hide") + @$('.bv_cmpdRegAdminController').html @cmpdRegAdminController.render().el + @$(".bv_cmpdRegAdminController").removeClass("hide") + @$(".bv_cmpdRegAdminControllerContainer").removeClass("hide") @$('.bv_editCmpdRegAdmin').show() @$('.bv_deleteCmpdRegAdmin').show() + handleSearchRequested: => + @$(".bv_cmpdRegAdminTableController").addClass "hide" + @$(".bv_errorOccurredPerformingSearch").addClass "hide" + cmpdRegAdminSearchTerm = $.trim(@$(".bv_cmpdRegAdminSearchTerm").val()) + if cmpdRegAdminSearchTerm isnt "" + @$(".bv_noMatchingCmpdRegAdminsFoundMessage").addClass "hide" + @$(".bv_cmpdRegAdminBrowserSearchInstructions").addClass "hide" + @$(".bv_searchCmpdRegAdminsStatusIndicator").removeClass "hide" + if !window.conf.browser.enableSearchAll and cmpdRegAdminSearchTerm is "*" + @$(".bv_moreSpecificCmpdRegAdminSearchNeeded").removeClass "hide" + else + @$(".bv_searchingCmpdRegAdminsMessage").removeClass "hide" + @$(".bv_cmpdRegAdminSearchTerm").html cmpdRegAdminSearchTerm + @$(".bv_moreSpecificCmpdRegAdminSearchNeeded").addClass "hide" + @searchController.doSearch cmpdRegAdminSearchTerm + handleDeleteCmpdRegAdminClicked: => @$(".bv_cmpdRegAdminCodeName").html @cmpdRegAdminController.model.get("code") @$(".bv_deleteButtons").removeClass "hide" @@ -216,8 +213,8 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View @$(".bv_deleteWarningMessage").removeClass "hide" @$(".bv_deletingStatusIndicator").addClass "hide" @$(".bv_cmpdRegAdminDeletedSuccessfullyMessage").addClass "hide" - $(".bv_confirmDeleteCmpdRegAdmin").removeClass "hide" - $('.bv_confirmDeleteCmpdRegAdmin').modal({ + @$(".bv_confirmDeleteCmpdRegAdmin").removeClass "hide" + @$('.bv_confirmDeleteCmpdRegAdmin').modal({ keyboard: false, backdrop: true }) @@ -233,7 +230,7 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View @$(".bv_okayButton").removeClass "hide" @$(".bv_deletingStatusIndicator").addClass "hide" @$(".bv_cmpdRegAdminDeletedSuccessfullyMessage").removeClass "hide" - @searchController.handleDoSearchClicked() + @handleSearchRequested() error: (result) => @$(".bv_okayButton").removeClass "hide" @$(".bv_deletingStatusIndicator").addClass "hide" @@ -251,9 +248,10 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View @cmpdRegAdminSummaryTable.remove() if @cmpdRegAdminController? @cmpdRegAdminController.remove() - $(".bv_cmpdRegAdminController").addClass("hide") - $(".bv_cmpdRegAdminControllerContainer").addClass("hide") - $(".bv_noMatchingCmpdRegAdminsFoundMessage").addClass("hide") + + @$(".bv_cmpdRegAdminController").addClass("hide") + @$(".bv_cmpdRegAdminControllerContainer").addClass("hide") + @$(".bv_noMatchingCmpdRegAdminsFoundMessage").addClass("hide") render: => @ @@ -280,5 +278,5 @@ class window.AbstractCmpdRegAdminBrowserController extends Backbone.View handleBackToCmpdRegAdminBrowserClicked: => @$('.bv_cmpdRegAdminBrowserWrapper').show() @$('.bv_cmpdRegAdminControllerWrapper').hide() - @searchController.handleDoSearchClicked() + @handleSearchRequested() @trigger 'amClean' From b7fe95f1b4c70c5be0241728d7a70fc496c6ba16 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 12 Oct 2017 17:28:45 -0700 Subject: [PATCH 234/576] Tweaked Label Sequence save routes to pass back the error response, and changed the GUI to alert the error message on save failed. Fixes #281 --- .../src/client/ACASLabelSequences.coffee | 6 +++++- .../server/routes/ACASLabelSequencesRoutes.coffee | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee index dfc6c4e0b..cf68cf9ec 100644 --- a/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee +++ b/modules/ACASLabelSequences/src/client/ACASLabelSequences.coffee @@ -427,6 +427,9 @@ class window.ACASLabelSequenceController extends AbstractFormController @model.trigger 'saveFailed' else @modelSaveCallback + error: (err, response) => + alert response.responseText + @model.trigger 'saveFailed' else @model.save null, @@ -435,7 +438,8 @@ class window.ACASLabelSequenceController extends AbstractFormController @model.trigger 'saveFailed' else @modelSaveCallback - error: (err) => + error: (err, response) => + alert response.responseText @model.trigger 'saveFailed' validationError: => diff --git a/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee b/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee index 8cd26903c..012f26791 100644 --- a/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee +++ b/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee @@ -80,9 +80,9 @@ exports.saveLabelSequence = (req, resp) -> if !error && response.statusCode == 201 resp.json json else - console.error 'got ajax error trying to save label sequence' + console.error 'got error trying to save label sequence' resp.statusCode = 500 - resp.end "saveFailed" + resp.end json ) exports.saveLabelSequenceArray = (req, resp) -> @@ -100,8 +100,8 @@ exports.saveLabelSequenceArray = (req, resp) -> if !error && response.statusCode == 201 resp.json json else - console.error 'got ajax error trying to save label sequence array' - resp.end "saveFailed" + console.error 'got error trying to save label sequence array' + resp.end json ) exports.updateLabelSequence = (req, resp) -> @@ -119,8 +119,8 @@ exports.updateLabelSequence = (req, resp) -> if !error && response.statusCode == 200 resp.json json else - console.error 'got ajax error trying to update label sequence' - resp.end "updateFailed" + console.error 'got error trying to update label sequence' + resp.end json ) exports.updateLabelSequenceArray = (req, resp) -> From 3aac8ca455e7cba9e3d72862fd6b5f84d2ff3817 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sat, 14 Oct 2017 16:33:21 -0700 Subject: [PATCH 235/576] Fixes #284 adding routes to notify registered forms that a save of a new entity is in progress --- .../AbstractFormControllerRoutes.coffee | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/modules/Components/src/server/routes/AbstractFormControllerRoutes.coffee b/modules/Components/src/server/routes/AbstractFormControllerRoutes.coffee index 2c4b5f729..861e6c803 100644 --- a/modules/Components/src/server/routes/AbstractFormControllerRoutes.coffee +++ b/modules/Components/src/server/routes/AbstractFormControllerRoutes.coffee @@ -4,6 +4,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.get '/api/formController/clearAllLocks', loginRoutes.ensureAuthenticated, exports.clearAllLocks global.editLockedEntities = {} +global.newFormEntities = {} exports.setupChannels = (io, sessionStore, loginRoutes) -> nsp = io.of('/formController:connected') @@ -20,6 +21,18 @@ exports.setupChannels = (io, sessionStore, loginRoutes) -> for quid in lock.rejectedRequestSocketIDs socket.broadcast.to(quid).emit('editLockAvailable') delete global.editLockedEntities[lockKey] + + for lockKey, lock of global.newFormEntities + if lock.savingLockSocketID==socket.id + console.log "clearing save lock on #{lock}" + lock.savingLockSocketID = null + for quid in lock.savingNotificationRequestSocketIDs + socket.broadcast.to(quid).emit('newEntitySavingComplete') + + index = lock.savingNotificationRequestSocketIDs.indexOf socket.id + if index > -1 + lock.savingNotificationRequestSocketIDs.splice(index, 1) + ) socket.on('editLockEntity', (entityType, codeName) => @@ -69,6 +82,42 @@ exports.setupChannels = (io, sessionStore, loginRoutes) -> now = new Date().getTime() lockedEntity.lastActivityDate = now ) + socket.on('clearEditLock', (entityType, codeName) => + console.log "got clearEditLock #{entityType}, #{codeName}" + lockKey = entityType+"_"+codeName + lockedEntity = global.editLockedEntities[lockKey] + if lockedEntity? and lockedEntity.socketID==socket.id + for quid in lockedEntity.rejectedRequestSocketIDs + socket.broadcast.to(quid).emit('editLockAvailable') + delete global.editLockedEntities[lockKey] + ) + + socket.on('registerForSavingNewLockNotification', (entityType) => + lockKey = entityType+"_savingNewLock" + nfEntity = global.newFormEntities[lockKey] + if nfEntity? + nfEntity.savingNotificationRequestSocketIDs.push socket.id + else + global.newFormEntities[lockKey] = + savingLockSocketID: null + savingNotificationRequestSocketIDs: [socket.id] + ) + socket.on('savingNewLock', (entityType) => + lockKey = entityType+"_savingNewLock" + nfEntity = global.newFormEntities[lockKey] + if nfEntity? + nfEntity.savingLockSocketID = socket.id + for quid in nfEntity.savingNotificationRequestSocketIDs + socket.broadcast.to(quid).emit('newEntitySaveActive') + ) + socket.on('savingNewConplete', (entityType) => + lockKey = entityType+"_savingNewLock" + nfEntity = global.newFormEntities[lockKey] + if nfEntity? and nfEntity.savingLockSocketID==socket.id + nfEntity.savingLockSocketID = null + for quid in nfEntity.savingNotificationRequestSocketIDs + socket.broadcast.to(quid).emit('newEntitySavingComplete') + ) ) exports.clearAllLocks = (req, resp) -> @@ -77,3 +126,4 @@ exports.clearAllLocks = (req, resp) -> global.editLockedEntities = {} console.dir global.editLockedEntities, depth: 3 resp.end() + From 597c3223832fb346a70f73c0ece8653d99059b3a Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 16 Oct 2017 21:29:13 -0700 Subject: [PATCH 236/576] added validation checks on lot_custom to require units for amounts --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index b9b2ca6d0..2a74406df 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -111,6 +111,11 @@ $(function() { errors.push({'attribute': 'amount', 'message': "Amount must be a number if provided"}); } } + if (attr.amount!=null && attr.amount!='') { + if(attr.amountUnits==null || attr.amountUnits=='unassigned') { + errors.push({'attribute': 'amountUnits', 'message': "Amount units must be set if amount set"}); + } + } if (attr.retain!=null) { if(isNaN(attr.retain) && attr.retain!='') { errors.push({'attribute': 'retain', 'message': "Retain must be a number if provided"}); @@ -121,6 +126,11 @@ $(function() { errors.push({'attribute': 'solutionAmount', 'message': "Solution Amount must be a number if provided"}); } } + if (attr.solutionAmount!=null && attr.solutionAmount!='') { + if(attr.solutionAmountUnits==null || attr.solutionAmountUnits=='unassigned') { + errors.push({'attribute': 'solutionAmountUnits', 'message': "Solution amount units must be set if amount set"}); + } + } if (attr.tareWeight!=null) { if(isNaN(attr.tareWeight) && attr.tareWeight!='') { errors.push({'attribute': 'tareWeight', 'message': "Tare weight must be a number if provided"}); From 680b3dddf7bd382b91adf92e9339932d2b9b371f Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 17 Oct 2017 08:13:19 -0700 Subject: [PATCH 237/576] Added getAPICmpdReg proxy route, and switched scientists to use API route, which takes a new optional ?withLots boolean parameter. Switched the search form to use that parameter, which limits the list of scientists returned to only those who have registered lots in the system. Fixed some buggy behavior around the search form picklist showing up empty when there are no lots registered. Added an error message for when there are no projects in the system and a user tries to save a lot. Fixes https://github.com/mcneilco/acas-cmpdreg-roo-server/issues/25 added in defaults of several new CmpdReg configs. Removed "Parent LiveDesign Corp Name" from default bulk loader mappings because it is more confusing than useful. --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 6 ++++++ modules/CmpdReg/src/client/custom/configuration.json | 6 ++++-- modules/CmpdReg/src/client/src/PickList.js | 4 ++-- modules/CmpdReg/src/client/src/SearchForm.js | 2 +- modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee | 10 +++++++++- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index b9b2ca6d0..5de973261 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -146,6 +146,9 @@ $(function() { errors.push({'attribute': 'boilingPoint', 'message': "BP must be a number if provided"}); } } + if (attr.project != null && typeof(attr.project) == 'undefined'){ + errors.push({'attribute': 'project', 'message': "Project must be provided"}); + } if (errors.length > 0) {return errors;} } }); @@ -259,6 +262,9 @@ $(function() { }, updateModel: function() { + if (this.projectCodeController.collection.length == 0){ + alert('System Configuration Error: There must be at least one project to proceed') + } this.clearValidationErrors(); if (this.model.isNew() ) { diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index f2b99776a..cb3e1638f 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -62,8 +62,11 @@ "maxSearchResults": 5000, "unitTestDB": true, "initalDBLoad": true, + "compoundInventory": true, + "checkACASDependenciesByBarcode": false, "projectRestrictions": false, - "jchemVersion": "16.4.25.0" + "jchemVersion": "16.4.25.0", + "orderSelectLists": true }, "bulkLoadSettings": { "useProjectRoles":false, @@ -100,7 +103,6 @@ {"name": "Project", "dataType": "string", "required": false}, {"name": "CAS Number", "dataType": "string", "required": false}, {"name": "Parent Corp Name", "dataType": "string", "required": false}, - {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false}, {"name": "Parent Common Name", "dataType": "string", "required": false}, {"name": "Parent Stereo Comment", "dataType": "string", "required": false} ] diff --git a/modules/CmpdReg/src/client/src/PickList.js b/modules/CmpdReg/src/client/src/PickList.js index 90066a8d6..fada98e6a 100755 --- a/modules/CmpdReg/src/client/src/PickList.js +++ b/modules/CmpdReg/src/client/src/PickList.js @@ -52,8 +52,8 @@ $(function() { this.collection = new PickListList(); this.collection.setType(this.options.type); this.collection.bind('add', this.addOne); - this.collection.bind('reset', this.handleListReset); - this.collection.fetch(); + //this.collection.bind('reset', this.handleListReset); + this.collection.fetch({success: this.handleListReset}); if (this.options.selectedCode !='') { this.selectedCode = this.options.selectedCode; } else { diff --git a/modules/CmpdReg/src/client/src/SearchForm.js b/modules/CmpdReg/src/client/src/SearchForm.js index 67049e173..3e397deeb 100755 --- a/modules/CmpdReg/src/client/src/SearchForm.js +++ b/modules/CmpdReg/src/client/src/SearchForm.js @@ -86,7 +86,7 @@ $(function() { this.updatePercentSimilarityDisabled(); this.chemistCodeController = - this.setupCodeController('chemist', 'scientists', 'chemist', true); + this.setupCodeController('chemist', 'scientists?withLots=true', 'chemist', true); this.chemistCodeController.collection.bind('reset', this.chemistsLoaded); var self = this; diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index c9a9bcfdf..f5c7bd60f 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -6,7 +6,7 @@ exports.setupAPIRoutes = (app) -> exports.setupRoutes = (app, loginRoutes) -> app.get '/cmpdReg', loginRoutes.ensureAuthenticated, exports.cmpdRegIndex app.get '/marvin4js-license.cxl', loginRoutes.ensureAuthenticated, exports.getMarvinJSLicense - app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg + app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getAPICmpdReg app.get '/cmpdReg/parentAliasKinds', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/units', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/solutionUnits', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg @@ -129,6 +129,14 @@ exports.getBasicCmpdReg = (req, resp) -> console.log cmpdRegCall req.pipe(request(cmpdRegCall)).pipe(resp) +exports.getAPICmpdReg = (req, resp) -> + console.log 'in getAPICmpdReg' + console.log req.originalUrl + endOfUrl = (req.originalUrl).replace /\/cmpdreg\//, "" + cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + "/api/v1/" +endOfUrl + console.log cmpdRegCall + req.pipe(request(cmpdRegCall)).pipe(resp) + exports.getAuthorizedCmpdRegProjects = (req, resp) -> exports.getAuthorizedCmpdRegProjectsInternal req, (response) => resp.status "200" From 4da95add85d4a798268fadd8abedb1b3bef941c6 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 12 Oct 2017 14:09:16 -0700 Subject: [PATCH 238/576] Added new config: client.compoundInventory.daughterVials.strictMatchPhysicalState=false, which turns unit and physical state mismatches into warnings instead of errors. Changed createDaughterVialsInternal to skip decrementing from the parent vials if any of these properties are mismatched. Refactored decrementAmountsFromVials to pass in additional fields. Fixes #276 --- conf/config.properties.example | 1 + .../routes/InventoryServiceRoutes.coffee | 76 ++++++++++--------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 6719e298c..25f064771 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -553,3 +553,4 @@ client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER client.entity.saveInitialsCorpName=false client.compoundInventory.enforceUppercaseBarcodes=false +client.compoundInventory.daughterVials.strictMatchPhysicalState=false diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index f98f0c1dd..0ce42be86 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3017,6 +3017,10 @@ exports.checkParentWellContent = (fileEntryArray, callback) -> #concentration and concentration units match if solution errorMessages = [] vialBarcodes = _.pluck fileEntryArray, 'sourceVialBarcode' + strictMatch = config.all.client.compoundInventory.daughterVials.strictMatchPhysicalState + flexibleErrorLevel = 'warning' + if strictMatch? and strictMatch + flexibleErrorLevel = 'error' exports.getWellContentByContainerLabelsInternal vialBarcodes, 'container', 'tube', 'barcode', 'barcode', (wellContentList, statusCode) -> _.each fileEntryArray, (fileEntry) -> parentVialAndWellContent = _.findWhere wellContentList, {label: fileEntry.sourceVialBarcode} @@ -3024,17 +3028,17 @@ exports.checkParentWellContent = (fileEntryArray, callback) -> parentWellContent = parentVialAndWellContent.wellContent[0] if parentWellContent.physicalState != fileEntry.physicalState error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must be of the same physical state as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.physicalState}." errorMessages.push error if fileEntry.physicalState == 'solution' and (Math.abs(fileEntry.concentration - parentWellContent.batchConcentration) > 0.0001 or fileEntry.concUnits != parentWellContent.batchConcUnits) error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must have the same concentration as parent vial #{fileEntry.sourceVialBarcode}, which is #{parentWellContent.batchConcentration} #{parentWellContent.batchConcUnits}." errorMessages.push error if parentWellContent.amountUnits != fileEntry.amountUnits error = - errorLevel: 'error' + errorLevel: flexibleErrorLevel message: "Daughter vial #{fileEntry.destinationVialBarcode} must use the same amount units as parent vial #{fileEntry.sourceVialBarcode}, which is in #{parentWellContent.amountUnits}." errorMessages.push error else if parentWellContent.amount < fileEntry.amount @@ -3131,32 +3135,40 @@ decrementAmountsFromVials = (toDecrementList, parentWellContentList, user, callb wellsToUpdate = [] changes = [] _.each toDecrementList, (toDecrement) -> - oldContainerWellContent = _.findWhere parentWellContentList, {label: toDecrement.barcode} + oldContainerWellContent = _.findWhere parentWellContentList, {label: toDecrement.sourceVialBarcode} oldWellContent = oldContainerWellContent.wellContent[0] - wellCode = oldWellContent.containerCodeName - newWellContent = - containerCodeName: wellCode - amount: oldWellContent.amount - toDecrement.amountToDecrement - recordedBy: user - wellsToUpdate.push newWellContent - change = - codeName: oldContainerWellContent.containerCodeName - recordedBy: user - recordedDate: new Date().getTime() - entryType: 'UPDATE' - entry: "Amount #{toDecrement.amountToDecrement} #{toDecrement.amountToDecrementUnits} taken out to create daughter vial #{toDecrement.daughterVialBarcode}" - changes.push change + #Check that the amount is valid to decrement. + differentState = (oldWellContent.physicalState != toDecrement.physicalState) + concentrationMismatch = (toDecrement.physicalState == 'solution' and (Math.abs(toDecrement.concentration - oldWellContent.batchConcentration) > 0.0001 or toDecrement.concUnits != oldWellContent.batchConcUnits)) + unitMismatch = (oldWellContent.amountUnits != toDecrement.amountUnits) + if !differentState and !concentrationMismatch and !unitMismatch + wellCode = oldWellContent.containerCodeName + newWellContent = + containerCodeName: wellCode + amount: oldWellContent.amount - toDecrement.amount + recordedBy: user + wellsToUpdate.push newWellContent + change = + codeName: oldContainerWellContent.containerCodeName + recordedBy: user + recordedDate: new Date().getTime() + entryType: 'UPDATE' + entry: "Amount #{toDecrement.amount} #{toDecrement.amountUnits} taken out to create daughter vial #{toDecrement.destinationVialBarcode}" + changes.push change console.log wellsToUpdate - exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> - console.log updateWellsStatusCode - if updateWellsStatusCode != 204 - callback "Error: #{updateWellsResponse}" - else - exports.containerLogsInternal changes, 0, (logs, statusCode) -> - if statusCode != 200 - callback logs - else - callback null, updateWellsResponse + if wellsToUpdate.length > 0 + exports.updateWellContentInternal wellsToUpdate, true, false, (updateWellsResponse, updateWellsStatusCode) -> + console.log updateWellsStatusCode + if updateWellsStatusCode != 204 + callback "Error: #{updateWellsResponse}" + else + exports.containerLogsInternal changes, 0, (logs, statusCode) -> + if statusCode != 200 + callback logs + else + callback null, updateWellsResponse + else + callback null prepareSummaryInfo = (fileEntryArray) -> summaryInfo = @@ -3358,15 +3370,7 @@ exports.createDaughterVialsInternal = (vialsToCreate, user, callback) -> if err? callback err else - toDecrementList = [] - _.each vialsToCreate, (entry) -> - toDecrement = - barcode: entry.sourceVialBarcode - amountToDecrement: entry.amount - amountToDecrementUnits: entry.amountUnits - daughterVialBarcode: entry.destinationVialBarcode - toDecrementList.push toDecrement - decrementAmountsFromVials toDecrementList, parentWellContentList, user, (err, decrementVialsResponse) -> + decrementAmountsFromVials vialsToCreate, parentWellContentList, user, (err, decrementVialsResponse) -> if err? callback err else From 87241a99ef7d233dbad21cae5d452211e7c75dd3 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 17 Oct 2017 08:13:19 -0700 Subject: [PATCH 239/576] Added getAPICmpdReg proxy route, and switched scientists to use API route, which takes a new optional ?withLots boolean parameter. Switched the search form to use that parameter, which limits the list of scientists returned to only those who have registered lots in the system. Fixed some buggy behavior around the search form picklist showing up empty when there are no lots registered. Added an error message for when there are no projects in the system and a user tries to save a lot. Fixes https://github.com/mcneilco/acas-cmpdreg-roo-server/issues/25 added in defaults of several new CmpdReg configs. Removed "Parent LiveDesign Corp Name" from default bulk loader mappings because it is more confusing than useful. --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 6 ++++++ modules/CmpdReg/src/client/custom/configuration.json | 6 ++++-- modules/CmpdReg/src/client/src/PickList.js | 4 ++-- modules/CmpdReg/src/client/src/SearchForm.js | 2 +- modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee | 10 +++++++++- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index b9b2ca6d0..5de973261 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -146,6 +146,9 @@ $(function() { errors.push({'attribute': 'boilingPoint', 'message': "BP must be a number if provided"}); } } + if (attr.project != null && typeof(attr.project) == 'undefined'){ + errors.push({'attribute': 'project', 'message': "Project must be provided"}); + } if (errors.length > 0) {return errors;} } }); @@ -259,6 +262,9 @@ $(function() { }, updateModel: function() { + if (this.projectCodeController.collection.length == 0){ + alert('System Configuration Error: There must be at least one project to proceed') + } this.clearValidationErrors(); if (this.model.isNew() ) { diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index f2b99776a..cb3e1638f 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -62,8 +62,11 @@ "maxSearchResults": 5000, "unitTestDB": true, "initalDBLoad": true, + "compoundInventory": true, + "checkACASDependenciesByBarcode": false, "projectRestrictions": false, - "jchemVersion": "16.4.25.0" + "jchemVersion": "16.4.25.0", + "orderSelectLists": true }, "bulkLoadSettings": { "useProjectRoles":false, @@ -100,7 +103,6 @@ {"name": "Project", "dataType": "string", "required": false}, {"name": "CAS Number", "dataType": "string", "required": false}, {"name": "Parent Corp Name", "dataType": "string", "required": false}, - {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false}, {"name": "Parent Common Name", "dataType": "string", "required": false}, {"name": "Parent Stereo Comment", "dataType": "string", "required": false} ] diff --git a/modules/CmpdReg/src/client/src/PickList.js b/modules/CmpdReg/src/client/src/PickList.js index 90066a8d6..fada98e6a 100755 --- a/modules/CmpdReg/src/client/src/PickList.js +++ b/modules/CmpdReg/src/client/src/PickList.js @@ -52,8 +52,8 @@ $(function() { this.collection = new PickListList(); this.collection.setType(this.options.type); this.collection.bind('add', this.addOne); - this.collection.bind('reset', this.handleListReset); - this.collection.fetch(); + //this.collection.bind('reset', this.handleListReset); + this.collection.fetch({success: this.handleListReset}); if (this.options.selectedCode !='') { this.selectedCode = this.options.selectedCode; } else { diff --git a/modules/CmpdReg/src/client/src/SearchForm.js b/modules/CmpdReg/src/client/src/SearchForm.js index 1b666f83f..1b0513d94 100755 --- a/modules/CmpdReg/src/client/src/SearchForm.js +++ b/modules/CmpdReg/src/client/src/SearchForm.js @@ -86,7 +86,7 @@ $(function() { this.updatePercentSimilarityDisabled(); this.chemistCodeController = - this.setupCodeController('chemist', 'scientists', 'chemist', true); + this.setupCodeController('chemist', 'scientists?withLots=true', 'chemist', true); this.chemistCodeController.collection.bind('reset', this.chemistsLoaded); var self = this; diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index c9a9bcfdf..f5c7bd60f 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -6,7 +6,7 @@ exports.setupAPIRoutes = (app) -> exports.setupRoutes = (app, loginRoutes) -> app.get '/cmpdReg', loginRoutes.ensureAuthenticated, exports.cmpdRegIndex app.get '/marvin4js-license.cxl', loginRoutes.ensureAuthenticated, exports.getMarvinJSLicense - app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg + app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getAPICmpdReg app.get '/cmpdReg/parentAliasKinds', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/units', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/solutionUnits', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg @@ -129,6 +129,14 @@ exports.getBasicCmpdReg = (req, resp) -> console.log cmpdRegCall req.pipe(request(cmpdRegCall)).pipe(resp) +exports.getAPICmpdReg = (req, resp) -> + console.log 'in getAPICmpdReg' + console.log req.originalUrl + endOfUrl = (req.originalUrl).replace /\/cmpdreg\//, "" + cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + "/api/v1/" +endOfUrl + console.log cmpdRegCall + req.pipe(request(cmpdRegCall)).pipe(resp) + exports.getAuthorizedCmpdRegProjects = (req, resp) -> exports.getAuthorizedCmpdRegProjectsInternal req, (response) => resp.status "200" From 4083bcfc272e35ec35a5ab6e2ef46b03f0211702 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 17 Oct 2017 11:19:57 -0700 Subject: [PATCH 240/576] #286: Uncommenting moveToLocation callCustom code --- .../routes/InventoryServiceRoutes.coffee | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 644e9a0f8..eb814afa4 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -1371,15 +1371,15 @@ exports.moveToLocationInternal = (input, callCustom, updateLocationHistory, call if updateLocationHistory exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> callback json, statusCode - # if callCustom && csUtilities.moveToLocation? - # console.log "running customer specific server function moveToLocation" - # csUtilities.moveToLocation input, (customerResponse, statusCode) -> - # json = _.extend json, customerResponse - # callback json, statusCode - # else - # console.warn "could not find customer specific server function moveToLocation so not running it" - # callback json, response.statusCode - # ) + if callCustom && csUtilities.moveToLocation? + console.log "running customer specific server function moveToLocation" + csUtilities.moveToLocation input, (customerResponse, statusCode) -> + json = _.extend json, customerResponse + callback json, statusCode + else + if callCustom + console.warn "could not find customer specific server function moveToLocation so not running it" + callback json, statusCode ) else callback json, response.statusCode @@ -1392,6 +1392,7 @@ exports.moveToLocationInternal = (input, callCustom, updateLocationHistory, call ) #) + exports.getWellContentByContainerLabel = (req, resp) -> req.setTimeout 86400000 exports.getWellContentByContainerLabelsInternal [req.params.label], req.query.containerType, req.query.containerKind, req.query.labelType, req.query.labelKind, (json, statusCode) -> From 89c57b5ef9a06a92d94ebc0c0d343a454f61db5e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 17 Oct 2017 14:09:46 -0700 Subject: [PATCH 241/576] #286: Uncommenting was not enough, to provide the right callback heirarchy I created a no op function that returns null if callCustom and csUtilities.moveToLocation are not set. --- .../routes/InventoryServiceRoutes.coffee | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index eb814afa4..96b72d5a2 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -1367,22 +1367,14 @@ exports.moveToLocationInternal = (input, callCustom, updateLocationHistory, call #add the call to updateContainerHistoryLogs here... console.debug "response statusCode: #{response.statusCode}" if !error - console.log input[0].containerCodeName - if updateLocationHistory - exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> - callback json, statusCode - if callCustom && csUtilities.moveToLocation? - console.log "running customer specific server function moveToLocation" - csUtilities.moveToLocation input, (customerResponse, statusCode) -> - json = _.extend json, customerResponse - callback json, statusCode - else - if callCustom - console.warn "could not find customer specific server function moveToLocation so not running it" + shouldCallCustom = (callCustom && csUtilities.moveToLocation?) + callFunctionOrReturnNull shouldCallCustom, csUtilities.moveToLocation, input, (customerResponse, statusCode) -> + if updateLocationHistory + exports.updateContainerHistoryLogsInternal(input, (json, statusCode) -> callback json, statusCode - ) - else - callback json, response.statusCode + ) + else + callback json, response.statusCode else console.error 'got ajax error trying to get moveToLocation' console.error error @@ -1392,6 +1384,14 @@ exports.moveToLocationInternal = (input, callCustom, updateLocationHistory, call ) #) +callFunctionOrReturnNull = (callFunctionBoolean, funct, input, callback) -> + if callFunctionBoolean + console.log "running customer specific server function" + funct input, (customerResponse, statusCode) -> + callback customerResponse, statusCode + else + console.log "not running customer specific server function" + callback null exports.getWellContentByContainerLabel = (req, resp) -> req.setTimeout 86400000 From da7ef95b24c99264e09502c0e0560ca899e0c87b Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 17 Oct 2017 17:19:16 -0700 Subject: [PATCH 242/576] Exposed getCodeTableValues as an internal function, then used it in Vial CSV loaders to get the configured list of physical states, validate that the physical states in the CSV are recognized and dealias physical state Names into Codes. Tweaked validation logic to be more permissive. Also reworked validation to report errors when an amount or concentration is supplied but the units are not. Checked that units are uL for liquid and solution, or mg for any other physical state. Also fixed HTML reporting to count up vials by physical state generically based on the configured physical states. Fixes #288 --- .../routes/CodeTableServiceRoutes.coffee | 12 +- .../routes/InventoryServiceRoutes.coffee | 328 ++++++++++-------- 2 files changed, 198 insertions(+), 142 deletions(-) diff --git a/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee b/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee index 422d307f0..7e87f439e 100644 --- a/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee +++ b/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee @@ -37,13 +37,17 @@ exports.getAllCodeTableValues = (req, resp) -> ) exports.getCodeTableValues = (req, resp) -> + exports.getCodeTableValuesInternal req.params.type, req.params.kind, (result) -> + resp.json result + +exports.getCodeTableValuesInternal = (type, kind, cb) -> if global.specRunnerTestmode fullCodeTableJSON = require '../public/javascripts/spec/CodeTableJSON.js' - correctCodeTable = _.findWhere(fullCodeTableJSON.codes, {type:req.params.type, kind:req.params.kind}) - resp.end JSON.stringify correctCodeTable['codes'] + correctCodeTable = _.findWhere(fullCodeTableJSON.codes, {type:type, kind:kind}) + cb correctCodeTable['codes'] else config = require '../conf/compiled/conf.js' - baseurl = "#{config.all.client.service.persistence.fullpath}ddictvalues/all/#{req.params.type}/#{req.params.kind}/codetable" + baseurl = "#{config.all.client.service.persistence.fullpath}ddictvalues/all/#{type}/#{kind}/codetable" request = require 'request' request( method: 'GET' @@ -58,7 +62,7 @@ exports.getCodeTableValues = (req, resp) -> if a.name.toUpperCase() > b.name.toUpperCase() return 1 return 0 - resp.json json + cb json else console.log 'got ajax error trying to get code table entries' console.log error diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 0ce42be86..bfc527b02 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -2,6 +2,7 @@ serverUtilityFunctions = require './ServerUtilityFunctions.js' csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' _ = require 'underscore' preferredEntityCodeService = require '../routes/PreferredEntityCodeService.js' +codeTableRoutes = require './CodeTableServiceRoutes.js' config = require '../conf/compiled/conf.js' RUN_CUSTOM_FLAG = "0" fs = require('fs') @@ -2664,34 +2665,34 @@ exports.validateParentVialsFromCSVInternal = (csvFileName, dryRun, callback) -> createParentVialFileEntryArray csvFileName, (err, fileEntryArray) -> if err? callback err - summaryInfo = prepareSummaryInfo fileEntryArray - checkRequiredAttributes fileEntryArray, (requiredAttributeErrors) -> - if requiredAttributeErrors? - validationResponse.errorMessages.push requiredAttributeErrors... - checkDataTypeErrors fileEntryArray, (dataTypeErrors) -> - if dataTypeErrors? - validationResponse.errorMessages.push dataTypeErrors... - checkBatchCodesExist fileEntryArray, (missingBatchCodeErrors) -> - if missingBatchCodeErrors? and missingBatchCodeErrors.length > 0 - error = - errorLevel: 'error' - message: "The following batches do not exist: "+ missingBatchCodeErrors.join ', ' - validationResponse.errorMessages.push error - barcodes = _.pluck fileEntryArray, 'destinationVialBarcode' - checkBarcodesExist barcodes, (existingBarcodes, newBarcodes) -> - if existingBarcodes? and existingBarcodes.length > 0 + prepareSummaryInfo fileEntryArray, (summaryInfo) -> + checkRequiredAttributes fileEntryArray, (requiredAttributeErrors) -> + if requiredAttributeErrors? + validationResponse.errorMessages.push requiredAttributeErrors... + checkDataTypeErrors fileEntryArray, (dataTypeErrors) -> + if dataTypeErrors? + validationResponse.errorMessages.push dataTypeErrors... + checkBatchCodesExist fileEntryArray, (missingBatchCodeErrors) -> + if missingBatchCodeErrors? and missingBatchCodeErrors.length > 0 error = errorLevel: 'error' - message: "The following barcodes already exist: " + existingBarcodes.join ', ' + message: "The following batches do not exist: "+ missingBatchCodeErrors.join ', ' validationResponse.errorMessages.push error - errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} - warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} - if errors.length > 0 - validationResponse.hasError = true - if warnings.length > 0 - validationResponse.hasWarning = true - validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo - callback validationResponse + barcodes = _.pluck fileEntryArray, 'destinationVialBarcode' + checkBarcodesExist barcodes, (existingBarcodes, newBarcodes) -> + if existingBarcodes? and existingBarcodes.length > 0 + error = + errorLevel: 'error' + message: "The following barcodes already exist: " + existingBarcodes.join ', ' + validationResponse.errorMessages.push error + errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} + warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} + if errors.length > 0 + validationResponse.hasError = true + if warnings.length > 0 + validationResponse.hasWarning = true + validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo + callback validationResponse else error = errorLevel: 'error' @@ -2716,52 +2717,53 @@ exports.createParentVialsFromCSVInternal = (csvFileName, dryRun, user, callback) createParentVialFileEntryArray csvFileName, (err, fileEntryArray) -> if err? callback err - summaryInfo = prepareSummaryInfo fileEntryArray - exports.getContainerTubeDefinitionCode (definitionCode) -> - if !definitionCode? - error = - errorLevel: 'error' - message: 'Could not find definition container for tube' - createResponse.errorMessages.push error - tubesToCreate = [] - _.each fileEntryArray, (entry) -> - tube = - barcode: entry.destinationVialBarcode - definition: definitionCode - recordedBy: user - createdUser: entry.preparedBy - createdDate: entry.createdDate - physicalState: entry.physicalState - wells: [ - wellName: "A001" - batchCode: entry.batchCode - amount: entry.amount - amountUnits: entry.amountUnits - physicalState: entry.physicalState - recordedBy: user - recordedDate: (new Date()).getTime() - ] - if entry.physicalState == 'solution' - tube.wells[0].batchConcentration = entry.concentration - tube.wells[0].batchConcUnits = entry.concUnits - tube.wells[0].solventCode = entry.solvent - tubesToCreate.push tube - console.log JSON.stringify tubesToCreate - exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> - console.log statusCode - console.log json - if statusCode != 200 - createResponse.hasError = true - console.error json - error = - errorLevel: 'error' - message: json - createResponse.errorMessages.push error - else - createResponse.hasError = false - createResponse.commit = true - createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo - callback createResponse + dealiasPhysicalStates fileEntryArray, (fileEntryArray) -> + prepareSummaryInfo fileEntryArray, (summaryInfo) -> + exports.getContainerTubeDefinitionCode (definitionCode) -> + if !definitionCode? + error = + errorLevel: 'error' + message: 'Could not find definition container for tube' + createResponse.errorMessages.push error + tubesToCreate = [] + _.each fileEntryArray, (entry) -> + tube = + barcode: entry.destinationVialBarcode + definition: definitionCode + recordedBy: user + createdUser: entry.preparedBy + createdDate: entry.createdDate + physicalState: entry.physicalState + wells: [ + wellName: "A001" + batchCode: entry.batchCode + amount: entry.amount + amountUnits: entry.amountUnits + physicalState: entry.physicalState + recordedBy: user + recordedDate: (new Date()).getTime() + ] + if entry.physicalState == 'solution' + tube.wells[0].batchConcentration = entry.concentration + tube.wells[0].batchConcUnits = entry.concUnits + tube.wells[0].solventCode = entry.solvent + tubesToCreate.push tube + console.log JSON.stringify tubesToCreate + exports.createTubesInternal tubesToCreate, 0, (json, statusCode) -> + console.log statusCode + console.log json + if statusCode != 200 + createResponse.hasError = true + console.error json + error = + errorLevel: 'error' + message: json + createResponse.errorMessages.push error + else + createResponse.hasError = false + createResponse.commit = true + createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo + callback createResponse else error = errorLevel: 'error' @@ -2821,20 +2823,20 @@ exports.validateDaughterVialsFromCSVInternal = (csvFileName, dryRun, callback) - createDaughterVialFileEntryArray csvFileName, (err, fileEntryArray) -> if err? callback err - summaryInfo = prepareSummaryInfo fileEntryArray - exports.validateDaughterVialsInternal fileEntryArray, (err, errorsAndWarnings) -> - if err? - callback err - else - validationResponse.errorMessages.push errorsAndWarnings... - errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} - warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} - if errors.length > 0 - validationResponse.hasError = true - if warnings.length > 0 - validationResponse.hasWarning = true - validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo - callback null, validationResponse + prepareSummaryInfo fileEntryArray, (summaryInfo) -> + exports.validateDaughterVialsInternal fileEntryArray, (err, errorsAndWarnings) -> + if err? + callback err + else + validationResponse.errorMessages.push errorsAndWarnings... + errors = _.where validationResponse.errorMessages, {errorLevel: 'error'} + warnings = _.where validationResponse.errorMessages, {errorLevel: 'warning'} + if errors.length > 0 + validationResponse.hasError = true + if warnings.length > 0 + validationResponse.hasWarning = true + validationResponse.results.htmlSummary = prepareValidationHTMLSummary validationResponse.hasError, validationResponse.hasWarning, validationResponse.errorMessages, summaryInfo + callback null, validationResponse else error = errorLevel: 'error' @@ -2865,18 +2867,19 @@ exports.createDaughterVialsFromCSVInternal = (csvFileName, dryRun, user, callbac createDaughterVialFileEntryArray csvFileName, (err, fileEntryArray) -> if err? callback err - summaryInfo = prepareSummaryInfo fileEntryArray - exports.createDaughterVialsInternal fileEntryArray, user, (err, response) -> - if err? - error = - errorLevel: 'error' - message: err - createResponse.errorMessages.push error - else - createResponse.hasError = false - createResponse.commit = true - createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo - callback null, createResponse + dealiasPhysicalStates fileEntryArray, (fileEntryArray) -> + prepareSummaryInfo fileEntryArray, (summaryInfo) -> + exports.createDaughterVialsInternal fileEntryArray, user, (err, response) -> + if err? + error = + errorLevel: 'error' + message: err + createResponse.errorMessages.push error + else + createResponse.hasError = false + createResponse.commit = true + createResponse.results.htmlSummary = prepareCreateVialsHTMLSummary createResponse.hasError, createResponse.hasWarning, createResponse.errorMessages, summaryInfo + callback null, createResponse @@ -2902,6 +2905,7 @@ createParentVialFileEntryArray = (csvFileName, callback) => fs.createReadStream(path + fileName) .pipe(parse({delimiter: ','})) .on('data', (csvrow) -> + rowCount++ fileEntry = batchCode: csvrow[PARENT_COMPOUND_LOT_INDEX].trim() destinationVialBarcode: csvrow[PARENT_DESTINATION_VIAL_INDEX].trim() @@ -2914,9 +2918,8 @@ createParentVialFileEntryArray = (csvFileName, callback) => concUnits:csvrow[PARENT_CONC_UNITS_INDEX].trim() solvent: csvrow[PARENT_SOLVENT_INDEX].trim() rowNumber: rowCount - if rowCount? and rowCount > 0 + if rowCount? and rowCount > 1 csvFileEntries.push(fileEntry) - rowCount++ ) .on('end', () -> return callback null, csvFileEntries @@ -2930,6 +2933,7 @@ createDaughterVialFileEntryArray = (csvFileName, callback) => fs.createReadStream(path + fileName) .pipe(parse({delimiter: ','})) .on('data', (csvrow) -> + rowCount++ fileEntry = sourceVialBarcode: csvrow[DAUGHTER_SOURCE_VIAL_INDEX].trim() destinationVialBarcode: csvrow[DAUGHTER_DESTINATION_VIAL_INDEX].trim() @@ -2942,27 +2946,33 @@ createDaughterVialFileEntryArray = (csvFileName, callback) => concUnits: csvrow[DAUGHTER_CONC_UNITS_INDEX].trim() solvent: csvrow[DAUGHTER_SOLVENT_INDEX].trim() rowNumber: rowCount - if rowCount? and rowCount > 0 + if rowCount? and rowCount > 1 csvFileEntries.push(fileEntry) - rowCount++ ) .on('end', () -> return callback null, csvFileEntries ) +dealiasPhysicalStates = (fileEntryArray, callback) -> + codeTableRoutes.getCodeTableValuesInternal 'container status', 'physical state', (configuredPhysicalStates) -> + cleanedFileEntryArray = _.map fileEntryArray, (entry) -> + foundPhysicalState = _.findWhere configuredPhysicalStates, {code: entry.physicalState} + if !foundPhysicalState? + foundPhysicalState = _.findWhere configuredPhysicalStates, {name: entry.physicalState} + if foundPhysicalState? + entry.physicalState = foundPhysicalState.code + entry + callback cleanedFileEntryArray + checkRequiredAttributes = (fileEntryArray, callback) -> requiredAttributeErrors = [] _.each fileEntryArray, (entry) -> missingAttributes = [] - for attr in ['batchCode', 'destinationVialBarcode', 'sourceVialBarcode', 'amount', 'amountUnits', 'preparedBy', 'preparedDate', 'physicalState', 'concentration', 'concUnits', 'solvent'] + for attr in ['batchCode', 'destinationVialBarcode', 'sourceVialBarcode','preparedBy', 'preparedDate', 'physicalState'] + #not strictly required: 'amount', 'amountUnits', 'concentration', 'concUnits', 'solvent', if entry[attr]? - if attr in ['concentration', 'concUnits', 'solvent'] - if entry.physicalState == 'solution' - if entry[attr] == "" - missingAttributes.push attr - else - if entry[attr] == "" - missingAttributes.push attr + if entry[attr] == "" + missingAttributes.push attr if missingAttributes.length > 0 error = errorLevel: 'error' @@ -2971,19 +2981,50 @@ checkRequiredAttributes = (fileEntryArray, callback) -> callback requiredAttributeErrors checkDataTypeErrors = (fileEntryArray, callback) -> - dataTypeErrors = [] - _.each fileEntryArray, (entry) -> - if entry.amount? and entry.amount.length > 0 and isNaN(entry.amount) - error = - errorLevel: 'error' - message: "Row #{entry.rowNumber} contains the invalid amount: #{entry.amount}" - dataTypeErrors.push error - if entry.concentration? and entry.concentration.length > 0 and isNaN(entry.concentration) - error = - errorLevel: 'error' - message: "Row #{entry.rowNumber} contains the invalid concentration: #{entry.concentration}" - dataTypeErrors.push error - callback dataTypeErrors + codeTableRoutes.getCodeTableValuesInternal 'container status', 'physical state', (configuredPhysicalStates) -> + dataTypeErrors = [] + _.each fileEntryArray, (entry) -> + foundPhysicalState = _.findWhere configuredPhysicalStates, {code: entry.physicalState} + if !foundPhysicalState? + foundPhysicalState = _.findWhere configuredPhysicalStates, {name: entry.physicalState} + if !foundPhysicalState? + configuredPhysicalStateCodes = _.pluck configuredPhysicalStates, 'code' + configuredPhysicalStateCodeString = configuredPhysicalStateCodes.join(', ') + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} has a physical state that was not recognized: #{entry.physicalState}. The available options are: #{configuredPhysicalStateCodeString}" + dataTypeErrors.push error + else + entry.physicalState = foundPhysicalState.code + if entry.amount? and !isNaN(entry.amount) + if !entry.amountUnits? or entry.amountUnits.length < 1 + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} has an amount but no units" + dataTypeErrors.push error + if entry.concentration? and !isNaN(entry.concentration) + if entry.concUnits.length < 1 + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} has a concentration but no units. Concentration units must be mM" + dataTypeErrors.push error + else if entry.concUnits != 'mM' + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} must use concentration units of mM" + dataTypeErrors.push error + if entry.physicalState == 'liquid' or entry.physicalState == 'solution' + if entry.amountUnits != 'uL' + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} uses physical state \"#{entry.physicalState}\" and so must use units uL" + dataTypeErrors.push error + else if entry.amountUnits.length > 0 and entry.amountUnits != 'mg' and foundPhysicalState? + error = + errorLevel: 'error' + message: "Row #{entry.rowNumber} uses physical state \"#{entry.physicalState}\" and so must use amount units mg" + dataTypeErrors.push error + callback dataTypeErrors checkBatchCodesExist = (fileEntryArray, callback) -> requests = [] @@ -2994,7 +3035,7 @@ checkBatchCodesExist = (fileEntryArray, callback) -> csUtilities.getPreferredBatchIds requests, (batchIdResponse) -> missingBatchCodes = [] _.each batchIdResponse, (batchCodeRequest) -> - if batchCodeRequest.preferredName.length < 1 + if batchCodeRequest.preferredName? and batchCodeRequest.preferredName.length < 1 missingBatchCodes.push batchCodeRequest.requestName callback missingBatchCodes @@ -3080,8 +3121,12 @@ prepareValidationHTMLSummary = (hasError, hasWarning, errorMessages, summaryInfo warningsBlock += "" htmlSummaryInfo = "

    Summary

    Information:

    \n
      \n " htmlSummaryInfo += "
    • Total Vials: #{summaryInfo.totalNumberOfVials}
    • " - htmlSummaryInfo += "
    • Solid Vials: #{summaryInfo.numSolidVials}
    • " - htmlSummaryInfo += "
    • Liquid Vials: #{summaryInfo.numLiquidVials}
    • " + console.log summaryInfo + stateNames = Object.keys(summaryInfo.totalsByStates) + stateNames.sort() + console.log stateNames + for stateName in stateNames + htmlSummaryInfo += "
    • #{stateName} Vials: #{summaryInfo.totalsByStates[stateName]}
    • " if summaryInfo.totalBatchCodes? htmlSummaryInfo += "
    • Unique Corporate Batch ID's: #{summaryInfo.totalBatchCodes}
    • " htmlSummaryInfo += "\n
    " @@ -3102,8 +3147,11 @@ prepareCreateVialsHTMLSummary = (hasError, hasWarning, errorMessages, summaryInf errorHeader = "

    An error occurred during uploading. If the messages below are unhelpful, you will need to contact your system administrator.

    " htmlSummaryInfo = "

    Summary

    Information:

    \n
      \n " htmlSummaryInfo += "
    • Total Vials: #{summaryInfo.totalNumberOfVials}
    • " - htmlSummaryInfo += "
    • Solid Vials: #{summaryInfo.numSolidVials}
    • " - htmlSummaryInfo += "
    • Liquid Vials: #{summaryInfo.numLiquidVials}
    • " + stateNames = Object.keys(summaryInfo.totalsByStates) + stateNames.sort() + console.log stateNames + for stateName in stateNames + htmlSummaryInfo += "
    • #{stateName} Vials: #{summaryInfo.totalsByStates[stateName]}
    • " if summaryInfo.totalBatchCodes? htmlSummaryInfo += "
    • Unique Corporate Batch ID's: #{summaryInfo.totalBatchCodes}
    • " htmlSummaryInfo += "\n
    " @@ -3170,17 +3218,21 @@ decrementAmountsFromVials = (toDecrementList, parentWellContentList, user, callb else callback null -prepareSummaryInfo = (fileEntryArray) -> - summaryInfo = - totalNumberOfVials: fileEntryArray.length - numSolidVials: (_.where fileEntryArray, {physicalState: 'solid'}).length - numLiquidVials: (_.where fileEntryArray, {physicalState: 'solution'}).length - batchCodes = _.pluck fileEntryArray, 'batchCode' - batchCodes = _.filter batchCodes, (entry) -> - entry? - if batchCodes? and batchCodes.length > 0 - summaryInfo.totalBatchCodes = (_.uniq batchCodes).length - summaryInfo +prepareSummaryInfo = (fileEntryArray, cb) -> + codeTableRoutes.getCodeTableValuesInternal 'container status', 'physical state', (configuredPhysicalStates) -> + summaryInfo = + totalNumberOfVials: fileEntryArray.length + totalsByStates: {} + _.each configuredPhysicalStates, (configuredPhysicalState) -> + count = (_.where fileEntryArray, {physicalState: configuredPhysicalState.code}).length + (_.where fileEntryArray, {physicalState: configuredPhysicalState.name}).length + if count > 0 + summaryInfo.totalsByStates[configuredPhysicalState.name] = count + batchCodes = _.pluck fileEntryArray, 'batchCode' + batchCodes = _.filter batchCodes, (entry) -> + entry? + if batchCodes? and batchCodes.length > 0 + summaryInfo.totalBatchCodes = (_.uniq batchCodes).length + cb summaryInfo exports.saveWellToWellInteractions = (req, resp) -> From 29c894d6c5a2af2f61526c5db68f865638ed777e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 17 Oct 2017 18:24:07 -0700 Subject: [PATCH 243/576] #290: R service to bulk load locations --- .../src/server/r/bulkLoadLocations.R | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 modules/ServerAPI/src/server/r/bulkLoadLocations.R diff --git a/modules/ServerAPI/src/server/r/bulkLoadLocations.R b/modules/ServerAPI/src/server/r/bulkLoadLocations.R new file mode 100644 index 000000000..3721f02f3 --- /dev/null +++ b/modules/ServerAPI/src/server/r/bulkLoadLocations.R @@ -0,0 +1,35 @@ +# The next line is used by PrepareConfigFiles to include this file as a route in rapache, do not modify unless you intend to modify rapache routes (it can be anywhere in the files though) +# ROUTE: /bulkLoadLocations + +library(data.table) + +bulkLoadLocations <- function(postData, GET) { + createLocation <- function(labelText, labelType, labelKind, recordedBy) { + label <- createContainerLabel(labelText = labelText, lsType = labelType, lsKind = labelKind, recordedBy = recordedBy, preferred = TRUE) + container <- createContainer(lsType = "location", lsKind = "default", recordedBy = recordedBy, containerLabels = list(label)) + return(container) + } + createInteraction <- function(data, ptempid, firstID, recordedBy) { + interaction <- createContainerContainerInteraction(lsType = "moved to", lsKind = "location_location", recordedBy = recordedBy, firstContainer = firstID, secondContainer = data[tempid == ptempid,]$id) + interaction$firstContainer <- data[tempid == firstID,]$savedContainer[[1]] + interaction$secondContainer <- data[tempid == ptempid,]$savedContainer[[1]] + return(interaction) + } + + data <- as.data.table(jsonlite::fromJSON(postData)) + recordedBy <- GET$recordedBy + data[ , container := list(list(createLocation(label, labeltype, labelkind, recordedBy = recordedBy))), by = tempid] + containers <- saveContainers(data$container) + containerDT <- rbindlist(lapply(containers, function(x) {list(id = x$id, label = x$lsLabels[[1]]$labelText, savedContainer = list(x))})) + setkey(data, label) + setkey(containerDT, label) + data <- data[containerDT] + data[ !is.na(parenttempid), interaction := list(list(createInteraction(data, parenttempid, tempid, recordedBy = recordedBy))), by = tempid] + interactions <- saveContainerContainerInteractions(data[!is.na(parenttempid)]$interaction) + cat(jsonlite::toJSON(data, auto_unbox = TRUE)) +} + +postData <- rawToChar(receiveBin()) +out <- bulkLoadLocations(postData, GET) +cat(out) +DONE From b09b39b6915d4d12d9dd2d0cf656f1e48bd914aa Mon Sep 17 00:00:00 2001 From: Fiona McNeil Date: Tue, 17 Oct 2017 22:38:45 -0700 Subject: [PATCH 244/576] Fixed floating panels positioning. --- .../MultipleFilePicker/css/style_nocompile.css | 6 +++--- .../src/client/css/NewCmpdReg_nocompile.css | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/CmpdReg/src/client/MultipleFilePicker/css/style_nocompile.css b/modules/CmpdReg/src/client/MultipleFilePicker/css/style_nocompile.css index 28ea02148..7ee2f79e7 100644 --- a/modules/CmpdReg/src/client/MultipleFilePicker/css/style_nocompile.css +++ b/modules/CmpdReg/src/client/MultipleFilePicker/css/style_nocompile.css @@ -11,9 +11,9 @@ div#upload-app { /* ORIGINAL WIDTH width: 510px;*/ width: 625px; - position: absolute; - bottom: 5px; important! -left: 20px; + position: fixed; + bottom: 60px; important! + left: 20px; } diff --git a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css index c5e25f056..db64370f3 100755 --- a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css +++ b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css @@ -168,9 +168,9 @@ div { /****** FLOATING PANELS ******/ /****** div#upload-app LOCATED IN MULTIPICKER CSS ******/ .MetaLotView .floatingPanel, .SearchResultsView .floatingPanel { - position: absolute; - left: 20px; - bottom: 140px; + position: fixed; + left: 220px; + bottom: 60px; padding: 5px; border: 12px solid #D5D8DD; background-color: #EEE; @@ -182,8 +182,9 @@ div { box-shadow: 3px 3px 4px #a7a9ad;*/ } .MetaLotView div.floatingPanel.NewLotSuccessView { - position: absolute; - left: 20px; + position: fixed; + bottom: 85px; + left: 220px; padding: 5px; cursor: hand; } @@ -445,9 +446,9 @@ div { /****** ERROR NOTIFICATION ******/ .ErrrorNotificationListView { - position: absolute; - bottom: -120px; - left: 15px; + position: fixed; + bottom: 60px; + left: 140px; z-index: 100; width: 400px; margin-left: 150px; From dcac1120dbd30eda4f042e6411f3cbbcf61f8bce Mon Sep 17 00:00:00 2001 From: Fiona McNeil Date: Wed, 18 Oct 2017 09:12:26 -0700 Subject: [PATCH 245/576] Fixes issues #292 and #293. Also made a couple spacing adjustments to various buttons. --- modules/ServerAPI/src/client/AuthorEditor.coffee | 14 ++++++++------ modules/ServerAPI/src/client/AuthorEditor.html | 12 ++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/ServerAPI/src/client/AuthorEditor.coffee b/modules/ServerAPI/src/client/AuthorEditor.coffee index 926163f66..cfc5486a3 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.coffee +++ b/modules/ServerAPI/src/client/AuthorEditor.coffee @@ -1,5 +1,5 @@ class window.AuthorRole extends Backbone.Model - + class window.AuthorRoleList extends Backbone.Collection model: AuthorRole @@ -65,7 +65,7 @@ class window.Author extends Backbone.Model return errors else return null - + getSystemRoles: => systemRoles = _.filter @get('authorRoles'), (role) => if role.roleEntry?.lsType? @@ -92,7 +92,7 @@ class window.Author extends Backbone.Model class window.AuthorList extends Backbone.Collection model: Author - + class window.AuthorRoleController extends AbstractFormController template: _.template($("#AuthorRoleView").html()) @@ -112,7 +112,7 @@ class window.AuthorRoleController extends AbstractFormController @roleKind = @options.roleKind else @roleKind = null - + render: => $(@el).empty() $(@el).html @template() @@ -280,7 +280,7 @@ class window.AuthorRoleListController extends Backbone.View class window.AuthorEditorController extends AbstractFormController template: _.template($("#AuthorEditorView").html()) moduleLaunchName: "author" - + events: -> "keyup .bv_firstName": "attributeChanged" "keyup .bv_lastName": "attributeChanged" @@ -381,6 +381,7 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_userName').attr 'disabled', 'disabled' @$('.bv_save').html("Update") @$('.bv_newEntity').show() + @$('.bv_cancel').hide() @$('.bv_save').attr('disabled', 'disabled') @$('.bv_cancel').attr('disabled','disabled') @@ -467,7 +468,7 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_saveComplete').hide() @$('.bv_saveFailed').hide() @checkFormValid() - + updateModel: => @model.set 'firstName', UtilityFunctions::getTrimmedInput @$('.bv_firstName') @@ -500,6 +501,7 @@ class window.AuthorEditorController extends AbstractFormController @$('.bv_saveComplete').html "Update Complete" @$('.bv_save').attr('disabled', 'disabled') @$('.bv_saving').show() + @$('.bv_cancel').hide() console.log "handleSaveClicked" console.log JSON.stringify @model @model.save null, diff --git a/modules/ServerAPI/src/client/AuthorEditor.html b/modules/ServerAPI/src/client/AuthorEditor.html index c375f2c26..5f43c4441 100644 --- a/modules/ServerAPI/src/client/AuthorEditor.html +++ b/modules/ServerAPI/src/client/AuthorEditor.html @@ -43,19 +43,19 @@

    ACAS Author

    - +
    - +
    - +
    @@ -63,7 +63,7 @@

    ACAS Author

    -
    +
    @@ -110,6 +110,6 @@ From 584b3f15aff4fe4499f5fe457f1ba0f124c943f0 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 18 Oct 2017 12:48:46 -0700 Subject: [PATCH 246/576] Added new config for the root location label, and added new Node route api/getContainerLocationTree, that gets the container location hierarchy in jsTree-compatible format based on the configured root location. Fixes #294 --- conf/config.properties.example | 3 +- .../routes/InventoryServiceRoutes.coffee | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 25f064771..8c24e649e 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -267,7 +267,7 @@ client.include.project=FALSE # config for whether project attribute is saved as part of an experiment client.save.project=TRUE client.protocol.save.project=true -client.path= +client.path=http://localhost:3000 client.fullpath=http://${client.host}:${client.port} # Query tool settings @@ -340,6 +340,7 @@ server.service.external.inventory.database.username= server.service.external.inventory.database.password= client.compoundInventory.enforceUppercaseBarcodes=false +client.compoundInventory.rootLocationLabel=COMPANY ## options for auth. strategy are: properties, database, ldap server.security.authstrategy=database diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index bfc527b02..569caff8f 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -66,6 +66,7 @@ exports.setupAPIRoutes = (app) -> app.post '/api/createDaughterVials', exports.createDaughterVials app.post '/api/advancedSearchContainers', exports.advancedSearchContainers app.get '/api/getParentVialByDaughterVialBarcode', exports.getParentVialByDaughterVialBarcode + app.get '/api/getContainerLocationTree', exports.getContainerLocationTree exports.setupRoutes = (app, loginRoutes) -> @@ -122,6 +123,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/createDaughterVials', loginRoutes.ensureAuthenticated, exports.createDaughterVials app.post '/api/advancedSearchContainers', loginRoutes.ensureAuthenticated, exports.advancedSearchContainers app.get '/api/getParentVialByDaughterVialBarcode', loginRoutes.ensureAuthenticated, exports.getParentVialByDaughterVialBarcode + app.get '/api/getContainerLocationTree', loginRoutes.ensureAuthenticated, exports.getContainerLocationTree exports.getContainersInLocation = (req, resp) -> @@ -3506,3 +3508,39 @@ exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callb parentVialBarcode = _.findWhere parentVial.lsLabels, {lsType: 'barcode', lsKind: 'barcode', ignored: false} responseStub.parentVialBarcode = parentVialBarcode.labelText callback null, responseStub + +exports.getContainerLocationTree = (req, resp) -> + exports.getContainerLocationTreeInternal (err, response) -> + if err? + resp.statusCode = 500 + resp.json err + else + resp.json response + +exports.getContainerLocationTreeInternal = (callback) -> + rootLabel = config.all.client.compoundInventory.rootLocationLabel + baseurl = config.all.client.service.persistence.fullpath+"containers/getLocationTreeByRootLabel?rootLabel=#{rootLabel}" + request( + method: 'GET' + url: baseurl + json: true + timeout: 86400000 + headers: 'content-type': 'application/json' + , (error, response, json) => + if !error && response.statusCode == 200 + console.debug "returned successfully from #{baseurl}" + formattedTree = [] + _.each json, (rawLocation) -> + location = + id: rawLocation.codeName + parent: rawLocation.parentCodeName + text: rawLocation.labelText + formattedTree.push location + callback null, formattedTree + else + console.error 'got ajax error trying to get getWellCodesByContainerCodes' + console.error error + console.error json + console.error response + callback JSON.stringify "getWellCodesByContainerCodes failed" + ) \ No newline at end of file From 833d35a97331567d2aa1bca77786a29ed1bf4ef8 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 18 Oct 2017 14:23:55 -0700 Subject: [PATCH 247/576] Tweaked getContainerLocationTreeInternal to set the root node's parent to # instead of null so it is compatible with jsTree --- .../src/server/routes/InventoryServiceRoutes.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 569caff8f..31b8fbdfe 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3531,9 +3531,12 @@ exports.getContainerLocationTreeInternal = (callback) -> console.debug "returned successfully from #{baseurl}" formattedTree = [] _.each json, (rawLocation) -> + parent = rawLocation.parentCodeName + if !parent? + parent = '#' location = id: rawLocation.codeName - parent: rawLocation.parentCodeName + parent: parent text: rawLocation.labelText formattedTree.push location callback null, formattedTree From 540ecf3304ae939ee9c4dcf2cfd890118b7d7e39 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 18 Oct 2017 14:52:04 -0700 Subject: [PATCH 248/576] Added breadcrumb to the returned JSON --- .../ServerAPI/src/server/routes/InventoryServiceRoutes.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 31b8fbdfe..8e94333a6 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3538,6 +3538,7 @@ exports.getContainerLocationTreeInternal = (callback) -> id: rawLocation.codeName parent: parent text: rawLocation.labelText + breadcrumb: rawLocation.labelTextBreadcrumb formattedTree.push location callback null, formattedTree else From 8a5111c90a7273b29f476a476e85b21058c72891 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Wed, 18 Oct 2017 17:10:43 -0700 Subject: [PATCH 249/576] feature/lot-inventory-location-select: added form field for location tree --- .../src/client/ACASFormLocationTree.coffee | 129 ++++++++++++++++++ .../src/client/ACASFormLocationTreeView.html | 32 +++++ .../src/client/AbstractFormController.coffee | 3 + 3 files changed, 164 insertions(+) create mode 100644 modules/Components/src/client/ACASFormLocationTree.coffee create mode 100644 modules/Components/src/client/ACASFormLocationTreeView.html diff --git a/modules/Components/src/client/ACASFormLocationTree.coffee b/modules/Components/src/client/ACASFormLocationTree.coffee new file mode 100644 index 000000000..252ee8fcf --- /dev/null +++ b/modules/Components/src/client/ACASFormLocationTree.coffee @@ -0,0 +1,129 @@ +class window.ACASFormLocationTreeController extends ACASFormAbstractFieldController + ### + Launching controller must: + - Initialize the model with an LSValue + Do whatever else is required or optional in ACASFormAbstractFieldController + ### + events: -> + "click .bv_editIcon": "handleEditIconClicked" + "click .bv_searchClear": "handleSearchClear" + "click .bv_updateLocation": "handleUpdateLocationClicked" + + template: _.template($("#ACASFormLocationTreeView").html()) + + initialize: -> + super() + @tubeCode = @options.tubeCode + if @tubeCode? + $.ajax + type: 'POST' + url: "/api/getBreadCrumbByContainerCode" + data: JSON.stringify [@tubeCode] + contentType: 'application/json' + success: (response) => + if response.length is 1 + @getModel().set + value: response[0].currentLocationCode + ignored: false + breadcrumb: response[0].labelBreadCrumb + else + @getModel().set + value: null + ignored: false + breadcrumb: "" + @renderModelContent() + + error: (err) => + alert 'error getting breadcrumb for container' + + + setEmptyValue: -> + @getModel().set + value: null + ignored: true + breadcrumb: null + + setInputValue: (inputValue) -> + @$('input').val inputValue + + + renderModelContent: => + @$('input').val @getModel().get('breadcrumb') + super() + + handleEditIconClicked: => + @$('.bv_locationTreeModal').modal + backdrop: 'static' + @getContainerLocationTree() + + getContainerLocationTree: => + $.ajax + type: 'GET' + url: "/api/getContainerLocationTree" + dataType: 'json' + error: (err) => + alert 'Could not get container location tree. Please contact administrator' + @$('.bv_locationTreeModal').modal 'hide' + success: (json) => + @setupTree (json) + + setupTree: (locationTreeObj) -> + _.map locationTreeObj, (loc) => + loc.icon = false + + @$('.bv_locationTree').jstree + core: + data: locationTreeObj + search: + fuzzy: false + show_only_matches: true + plugins: ["search"] + + @$('.bv_locationTree').bind "hover_node.jstree", (e, data) -> + $(e.target).attr("title", data.node.original.breadcrumb) + @$('.bv_locationTree').bind "select_node.jstree", (e, data) => + if data.selected.length is 1 + @$('.bv_updateLocation').removeAttr 'disabled' + else + @$('.bv_updateLocation').attr 'disabled', 'disabled' + + to = false + @$(".bv_searchVal").keyup => + clearTimeout to if to + to = setTimeout(-> + v = @$(".bv_searchVal").val() + @$(".bv_locationTree").jstree(true).search v + return + , 250) + return + + selectedLocation = @getModel().get('value') + if selectedLocation? and $.trim(selectedLocation) != "" + @$(".bv_locationTree").jstree('show_node', selectedLocation) + @$(".bv_locationTree").jstree('select_node', selectedLocation) + + handleSearchClear: => + @$('.bv_searchVal').val("") + + + handleUpdateLocationClicked: => + selectedLocation = @$('.bv_locationTree').jstree('get_selected', true) + @$('.bv_locationTreeModal').modal 'hide' + value = selectedLocation[0].original.id + breadcrumb = selectedLocation[0].original.breadcrumb + + @$('input').val breadcrumb + @handleInputChanged(value, breadcrumb) + + handleInputChanged: (value, breadcrumb) => + @clearError() + @userInputEvent = true + if value == "" + @setEmptyValue() + else + @getModel().set + value: value + ignored: false + breadcrumb: breadcrumb + @checkEmptyAndRequired() + @trigger 'formFieldChanged' diff --git a/modules/Components/src/client/ACASFormLocationTreeView.html b/modules/Components/src/client/ACASFormLocationTreeView.html new file mode 100644 index 000000000..da1e2f11f --- /dev/null +++ b/modules/Components/src/client/ACASFormLocationTreeView.html @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index d2425404a..a0554c653 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -166,6 +166,9 @@ class window.AbstractThingFormController extends AbstractFormController when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts + when 'locationTree' + opts.tubeCode = @model.get('tubeCode') + newField = new ACASFormLocationTreeController opts @$("."+field.fieldSettings.fieldWrapper).append newField.render().el newField.afterRender() From 23334e7ceed26164c918b0eeeea8dc6a336affb9 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Wed, 18 Oct 2017 17:42:27 -0700 Subject: [PATCH 250/576] Fixed compound reg issues where open lot details and new lot after a new lot is saved fail to open a tab. Also fixed issues where Ketcher inserts a single C in empty mol --- modules/CmpdReg/src/client/src/EditParent.js | 2 +- modules/CmpdReg/src/client/src/NewLotSuccess.js | 8 +++++--- modules/CmpdReg/src/client/src/RegistrationSearch.js | 10 ++++++---- modules/CmpdReg/src/client/src/Salt.js | 2 +- modules/CmpdReg/src/client/src/SaltForm.js | 2 +- modules/CmpdReg/src/client/src/SearchForm.js | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/modules/CmpdReg/src/client/src/EditParent.js b/modules/CmpdReg/src/client/src/EditParent.js index ad2b8f3e4..b245f08ed 100644 --- a/modules/CmpdReg/src/client/src/EditParent.js +++ b/modules/CmpdReg/src/client/src/EditParent.js @@ -118,7 +118,7 @@ $(function () { } else if (this.useKetcher) { mol = this.ketcher.getMolfile(); - if (mol.indexOf(" 0 0 0 0 0 999") > -1) mol = null; + if (mol.indexOf(" 0 0 0 1 0 999") > -1) mol = null; editParentSearch.set({ molStructure: mol, corpName: jQuery.trim(self.$('.corpName').val()) diff --git a/modules/CmpdReg/src/client/src/NewLotSuccess.js b/modules/CmpdReg/src/client/src/NewLotSuccess.js index 5532ac718..9c49aab16 100755 --- a/modules/CmpdReg/src/client/src/NewLotSuccess.js +++ b/modules/CmpdReg/src/client/src/NewLotSuccess.js @@ -27,20 +27,22 @@ $(function() { }, openLot: function() { - window.open("#lot/"+this.options.corpName); + console.log("about to open new lot window"); + window.open("#lot/"+this.options.corpName, '_blank'); this.closeLot(); // appController.reset(); // if(appController) {appController.router.navigate('lot/'+this.options.corpName,true);} }, newLot: function() { - window.open("#register/"+this.options.corpName); + window.open("#register/"+this.options.corpName, '_blank'); this.closeLot(); - if(appController) {appController.router.navigate('register/'+this.options.corpName,true);} + // if(appController) {appController.router.navigate('register/'+this.options.corpName,true);} }, closeLot: function() { // $(this.el).hide(); + console.log("about to reset the controller"); appController.reset(); } }); diff --git a/modules/CmpdReg/src/client/src/RegistrationSearch.js b/modules/CmpdReg/src/client/src/RegistrationSearch.js index bb4f9b879..8bd6e3b99 100755 --- a/modules/CmpdReg/src/client/src/RegistrationSearch.js +++ b/modules/CmpdReg/src/client/src/RegistrationSearch.js @@ -12,8 +12,8 @@ $(function() { if (errors.length > 0) {return errors;} } }); - - + + window.RegistrationSearchController = Backbone.View.extend({ template: _.template($('#RegistrationSearch_template').html()), @@ -127,12 +127,14 @@ $(function() { }); } else if (this.useKetcher) { mol = this.ketcher.getMolfile(); - if (mol.indexOf(" 0 0 0 0 0 999") > -1) mol = null; + if (mol.indexOf(" 0 0 0 1 0 999") > -1) mol = null; regSearch.set({ molStructure: mol, corpName: jQuery.trim(self.$('.corpName').val()) }); - + console.log(self.$('.corpName').val()); + console.log(mol); + console.log(regSearch); if ( this.isValid() ) { this.trigger('registrationSearchNext', regSearch); this.hide(); diff --git a/modules/CmpdReg/src/client/src/Salt.js b/modules/CmpdReg/src/client/src/Salt.js index 32e1c6a8f..943c17daf 100755 --- a/modules/CmpdReg/src/client/src/Salt.js +++ b/modules/CmpdReg/src/client/src/Salt.js @@ -280,7 +280,7 @@ $(function() { } else if (this.useKetcher) { mol = this.ketcher.getMolfile(); - if (mol.indexOf(" 0 0 0 0 0 999") > -1) mol = ''; + if (mol.indexOf(" 0 0 0 1 0 999") > -1) mol = ''; gotMol(mol); } else { alert("No new salt sketcher configured"); diff --git a/modules/CmpdReg/src/client/src/SaltForm.js b/modules/CmpdReg/src/client/src/SaltForm.js index 05f30ea6c..1d9ebd484 100755 --- a/modules/CmpdReg/src/client/src/SaltForm.js +++ b/modules/CmpdReg/src/client/src/SaltForm.js @@ -176,7 +176,7 @@ $(function() { }); }else if (this.useKetcher) { mol = this.ketcher.getMolfile(); - if (mol.indexOf(" 0 0 0 0 0 999") > -1) mol = ''; + if (mol.indexOf(" 0 0 0 1 0 999") > -1) mol = ''; this.model.set({molStructure: mol}); callback(); } diff --git a/modules/CmpdReg/src/client/src/SearchForm.js b/modules/CmpdReg/src/client/src/SearchForm.js index 1b666f83f..360a6b54d 100755 --- a/modules/CmpdReg/src/client/src/SearchForm.js +++ b/modules/CmpdReg/src/client/src/SearchForm.js @@ -159,7 +159,7 @@ $(function() { }); } else if (this.useKetcher) { mol = this.ketcher.getMolfile(); - if (mol.indexOf(" 0 0 0 0 0 999") > -1) mol = ''; + if (mol.indexOf(" 0 0 0 1 0 999") > -1) mol = ''; var sf = this.makeSearchFormModel(mol); if (this.isValid()) { this.trigger('searchNext', sf); From d8772145b68619a6c181269169ea884b464de43d Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 19 Oct 2017 09:44:42 -0700 Subject: [PATCH 251/576] Added in optional ?withContainers parameter for getContainerLocationTree that will include non-location containers in the tree. Without the paremeter, or with the parameter set to false, it the tree will only have locations, and not any containers in those locations. --- .../src/server/routes/InventoryServiceRoutes.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 8e94333a6..7e009f623 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3510,16 +3510,18 @@ exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callb callback null, responseStub exports.getContainerLocationTree = (req, resp) -> - exports.getContainerLocationTreeInternal (err, response) -> + exports.getContainerLocationTreeInternal req.query.withContainers, (err, response) -> if err? resp.statusCode = 500 resp.json err else resp.json response -exports.getContainerLocationTreeInternal = (callback) -> +exports.getContainerLocationTreeInternal = (withContainers, callback) -> rootLabel = config.all.client.compoundInventory.rootLocationLabel baseurl = config.all.client.service.persistence.fullpath+"containers/getLocationTreeByRootLabel?rootLabel=#{rootLabel}" + if withContainers? + baseurl+= "&withContainers=#{withContainers}" request( method: 'GET' url: baseurl From 7adf730e4147395dfb7144936ea882f0651c9b9a Mon Sep 17 00:00:00 2001 From: Samantha Munoz Date: Thu, 19 Oct 2017 15:19:05 -0400 Subject: [PATCH 252/576] Issue 66 --- .../routes/InventoryServiceRoutes.coffee | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 7e009f623..0eabf4406 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -67,6 +67,7 @@ exports.setupAPIRoutes = (app) -> app.post '/api/advancedSearchContainers', exports.advancedSearchContainers app.get '/api/getParentVialByDaughterVialBarcode', exports.getParentVialByDaughterVialBarcode app.get '/api/getContainerLocationTree', exports.getContainerLocationTree + app.post '/api/checkBatchDependencies', exports.checkBatchDependencies exports.setupRoutes = (app, loginRoutes) -> @@ -124,7 +125,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/advancedSearchContainers', loginRoutes.ensureAuthenticated, exports.advancedSearchContainers app.get '/api/getParentVialByDaughterVialBarcode', loginRoutes.ensureAuthenticated, exports.getParentVialByDaughterVialBarcode app.get '/api/getContainerLocationTree', loginRoutes.ensureAuthenticated, exports.getContainerLocationTree - + app.post '/api/checkBatchDependencies', loginRoutes.ensureAuthenticated, exports.checkBatchDependencies exports.getContainersInLocation = (req, resp) -> req.setTimeout 86400000 @@ -3549,4 +3550,36 @@ exports.getContainerLocationTreeInternal = (withContainers, callback) -> console.error json console.error response callback JSON.stringify "getWellCodesByContainerCodes failed" - ) \ No newline at end of file + ) + +exports.checkBatchDependencies = (req, resp) => + + exports.checkBatchDependenciesInternal(req.body, (json, statusCode) => + resp.statusCode = statusCode + resp.json json + ) + +exports.checkBatchDependenciesInternal = (input, callback) => + batchCodes = input + + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"compounds/checkBatchDependencies" + console.log 'baseurl', baseurl + request = require 'request' + request( + method: 'POST' + url: baseurl + body: batchCodes + json: true + timeout: 86400000 + , (error, response, json) => + if !error && response.statusCode == 200 + callback json, response.statusCode + else + console.error 'got ajax error trying to checkBatchDependencies' + console.error error + console.error json + console.error response + callback null, 500 + #resp.end JSON.stringify "getContainerStatesByContainerValue failed" + ) From bfe4fc688e7f269f80c3f308ff9a0b102f399e64 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Thu, 19 Oct 2017 13:28:19 -0700 Subject: [PATCH 253/576] fixed numericValueFormField so that it works with additional controls appended into its div --- modules/Components/src/client/ACASFormFields.coffee | 6 ++++-- modules/Components/src/client/ACASFormFieldsView.html | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 249cdc8a9..1aac21699 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -187,6 +187,8 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField - Initialize the model with an LSValue Do whatever else is required or optional in ACASFormAbstractFieldController ### + events: -> + "keyup .bv_number": "handleInputChanged" template: _.template($("#ACASFormLSNumericValueFieldView").html()) @@ -213,11 +215,11 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField ignored: true setInputValue: (inputValue) -> - @$('input').val inputValue + @$('.bv_number').val inputValue renderModelContent: => - @$('input').val @getModel().get('value') + @$('.bv_number').val @getModel().get('value') if @getModel().has 'unitKind' @$('.bv_units').html @getModel().get('unitKind') super() diff --git a/modules/Components/src/client/ACASFormFieldsView.html b/modules/Components/src/client/ACASFormFieldsView.html index 5bc7befc5..cc68f457c 100644 --- a/modules/Components/src/client/ACASFormFieldsView.html +++ b/modules/Components/src/client/ACASFormFieldsView.html @@ -9,7 +9,7 @@ \ No newline at end of file + diff --git a/public/lib/jstree/themes/default/40px.png b/public/lib/jstree/themes/default/40px.png old mode 100755 new mode 100644 index 9e76db4f7653a672e9231560ad74697b9fd78b76..7551c048e54ea71c4758193d3a0afd92920781ad GIT binary patch literal 21628 zcmeI4c{tQ-`@qLiQc5Hx+niCVlQF}L*^H%Q&^fjiLWvnOgDK2p29vUsS6M2FHo|Gs zX^|F7vUUh3rA4KZ$dVEvA!YeJGkkGW=bdw%>;1jgdtKwI?=#=;_rCAvc|Om5-_QNb z9}~6O)^hgDMKfVA*zBKIQPzUC3kH)oFk?FSbl^$H4$#PQRylKFuvzm2|0Q6F#}~q2 zKh0*@JMo-stVwjXk1mbDc4zAHeKNhkHz7!*uHRqU79=FpJ#+X2s#@78=IF8XS^d{?if2j5yhu* zPCN{3-_+#VZ{sjgYnfcOzaO1x9ss7I^{v@+d25*yt0RIbApT85 zJeJ3I>-!Zu}r2r#>2w^SpQ1>E7KG<-Cq!R!1QR2VzBAJ{KT;IXc#?) z2O5X;z<~s$r-!E@-3>hSkW385fXQTF(O5bb>S&U&uR5~xV}TS(^PVIlmmPVY1`U{J2a(C>tT5SRT)gFLC<#x=*(SYI!v zXb48_Pg4JWAAw#>-@gj4QBy*x?;GK=J$QjMKjunL;31;L{;1tQD^Bfzj1p`t-mL$` zQm}mkn0|j}F~E}3z^2Uf;~IPTv3=mo2bO36&F}B8-8c49@!E~2`+uBc{!LmDWS;Rf1TEr!C&w_DeyqQfWhRZ14q<`| z^XmW3od0oN6|FWK4JdJJLeIjP=KQoAP*)mf&1GdSqJ%z6)X+;|vTiU}v5Bc&f%~ z7N6LjhBt~W>gI|Au(^svvfx&{7z+hqFFxbEdXy3wM{%9DijS7R2j>2FI*azKi zS-@ZlZ-1t&wC6u>uM71~@J}i3zK7}Ir6sHUa_(vzKhi#gi``RAwRI}lWwL=rtfSu1 z&Dn0{7p0m`Ek2!~Kh0zN_A_Azix0xx6{baOjI_LYYss?~+x6J&MU}3wLsySh)*7uo(+s5>jxg@D?UHU2t2XkF|;CZHk&Mxx1>{;>#jw zxmmZYN~Psu%Kzf3))RKW=huqf8e(<#wRu+t&IM^&C#Eq6wY+*=Q&To<+!+6P#5UC! ziNG7buX)9(h?=+buwAP0(3>|8P8H3G_$gZH5c)22=P@cCUvQ9xJ2&CotRxqg6XU=zPGaIuE?%+_H=f3J`?FZKdugO6?>>W(MD!7qdBYdMJ~5>duKro zc0<0|vyOn=fR5*NMn*;vrIkvg$Nc#U+YA*mD3UH)RWEE($jzV}?hRl(BN`-UKl5(n z5E9f41KLTvH}}`()T(1B=4(#p_X z5iRzX?=g=KR$P62{Mk-i5&*p-qoD73(jIxu;j;=_7Nx`K=axOJt3yUaM1-cNrz2C* zwBJMfK7O2wTK7g(##B)&5rOz(-fXE?IC7za{+YR}`woZtNTwqQ|`R)b^w=T^WnpXgpS&rqQ{ROWtMgXln*(ZojCBqzh=`Buy~HH zYuEbeFO*Z%l{l+bM*ms2Y=O$YQ~T~0EPP0#Kw$I2FdU*;`M`!Q$ z`uFdBm+R^l+Hc(Wr5OxugoR$%T(PvFHu}>K3b}qst=G>T*K&)xxMD?9R&7UB`o^61 zLk;2bDJnbSEp$$-US$!hQCA4o$h^NU(5 zcNCESaBx$Mi-_}QI^@TN#y-8_|1PDeJl5pUo8vhd8FpoRRk&Bu(mWm%^uMfqURP(6 zzqdG$tQh(uA~vR?W$}V*^6(G~SGEG-*IW_z*v7_2wrReC?4FXV-p{VOB_t$d z>icLn@GJ%+B0(tBpWfYtNL}ndPqb_8(gl6BCzOSy`EMw6#TVAIZ-r4-EF)8+qB; zKVrO4ZofZ4&E>RRF>Bway^({a6x|m$PogM?w`!|ezIR*ihEMA*->y@|vtS|SS9Z`xGw z=BA7$c|Mzz&Pu%QqDu^X|AJRkKay8fRn={KHvTPE;;h4+r6*2Sl*UsJ$6N=oVee3| z7U=zY@nU;yR8>@+Z_bXP-kNoTAtQ0`YBEM~_LeaWX*>W&DJ6jf{h;o!$;b2bq*6^_EmgpCJzqXPea)KA;cFfE}29-vi zbV)ydKIp@%J(|P8C%Ntp4rf4yvPLh|SrlYAgRtpR<-QzBh|&O?){HB?Utf>HC8fV> zIpP+w86?x97+kVO{;%~--Mr63!_nPM<*8~Kc%6^=t8N51kzBbpy;qtWzXZh(Py-zt z%qip~JlmM}q4|&U=_vlf{9i+Xo7LXK02m=UE`MUpA*z zBOYFJzZLEI`fgI->4Jhy(b3V*1<|XwwXt;GA1?RpN?&{4zPCTRznff|^n8_fPOa3T zLx*B4_bti_29shRN=P{VOG_ek7^m1;%rad1+Vu7vuIwE?L8j^xFOk1bc7I0|u}fXk zRE<*hukwJUR41%^;}Q|BS-3+srlnNr>B3DFPK8a`W5;gC_E@mx0)e?`a0}!GJMgHy z@K(4d3+0_qRGVr_HMUQhniaNZw^gZWn?0t~@FQ;6Z-=*QMt^olWmab9n?N6^Xr4zZY^WSj@ee!a0FI=7!7Z=}U%8zWQ#;O>ZUgM%6aI}8Qy$cn% z=j-b0JH5QT_>PnmT8_kYX|)xNz#Z>`EJaqGA?NYQ5OVUN3s2D6rek|FKF zg%z)ZwG?XcKF>d9N9xeR;W~OKt)&R}dLZhnFrcCk9(K2BWb@|D$~MWQECZ#Px74G|OZ(bD zowXccvebR<?A6bK#Bw4C0|#K2`e3n!Obv z17)i0N3TKcw>NNjaA0Kkp%n_L?TVIEotshFvM8jd2#3R6`f=H^xk<>r&LgI9c&zc~ zH`^-@BfngU{8!`x>5JTa-<*wQv6`V)`C$8SK6EE@pu1et#NOF?ApTXir1c5^1F5Md zLL548_I+Z*D=jv9VFK6&PT-s5YGa;L;`CbHDw@$LoLS zyH*KHmfGJN(rgMxlN-~(o_q@wo<^@YPLeyrbX=};I%lEsyVg~0laP|M<>{?V*b-9j zmbLkD_4)I=1!FSlf5A1;a)px~SMZw%>m3~(x85`A-|+)Ns$IJ{EUf969^K+m#-2|l zU8?*&^M7qpA|>BcKv9c{9MbPvuEoEGbz)5$r^(Iv6kxX4wRo|_*+q-HCADV}o}r@_ zpjpLHl1p6S+jhI&HyPZgk*myZ?ri%}c|_&6Wi=KJUN`kWNGdPdDZAkErx}9d%9PY7 z%$-wCFW*X~P-dMt?FcjL8ykbow_GzEty({9*svkN<@r``lQ$Zi8}c_^RaW(dqpNFU zkIFjrOQ9ZzS4E$AcV*~gdh7wB3~p1tg*5F9|L#@aqi`ywY|EMOYPs0BNN}{kly8X$ zueOfe7s=J3t|;B|Iy}ByxinsrbqB$39FiGdsyw?Y18&t<0TE*o{+h+De z)eP369*|~`WvX^Y)!eAPx4AYV?AogAvRMo=_0-w*>tm1LkH9@GsTQttFGsmsFIm+% zO(Kc2*hbr0@PWTb4Yj&)nzbV5{0T)A^Vx-P64ftFv3eV>@Kbq;Q{M6}!R5J$!p3 zdFg_bm3OnHgfI37P)~oM-iZj`FA#ejM*DKpy>;y9I%SugILS`<0<_D{Soj@ZnCwdM znsXNEPv7-!e(U0DWfk-LdufS{%bq(`YI^`!zwgU2{%Lw^v2sj}&b#S4nwM215g_&5 zD)~Wh4HsU`esE`~*15@4eZ-VdW?D4J-8xlXNs4;1vH?z=w?E$^eOAS}<-9bDpH5EG zx@^1B!+p_~kDqs5O!?{iB>~m=$0T^(v4P-vm$09T;fiVZ|9ouc${fF4IsX`+Rr01+ z$^zZ)Pen-TU?ZONsIT17ws;0X>eP7++Oby|W-w>N2oIjLH8}#;Go$>Cz$caoeF81K z5`13A3yzT0tT{!FFz3iAIKmu(MK9d{l(Vp% z01|Bp)pS>Pi49dMznG6f`Zx|j-EuHAhaGnKmj%Av4e;adJbZt_+dO>5`#gLD?$huC z_&W<<04kcmvFR$wprQ$!a^2OEsb~VD)Lkw49A2cR0_$kxG^id!drW6LyF$=NpoPQ!iP*NDk40B*eiB zku2R9hv*UnS%`yEFIk!l4$*x~l!kWEYaf#(z)f`NL$cJni6&85(%(eWn5f)@5W=$3 z*i8zDLPJ;Su(@`TP8i!Pw!&s;OjgQd;!O6@W?W5{IXHjpu)_{J?6AXM2mdj^mp21^ zrIu---hTk!s9B3(EI124s7*{@%s&TT7=tQ?uf!Us=Xt#Ug-j)P4)ubn-9LsO{V_%X zU&t{=3%}1UlF=z$1dmSXB6xI47r{id=1~#RD!3P|f_u>_xEHO0d(k?$6Rm?g(K@&j zt%Ez!p9p`OhHaWK)1$I164UZDZ4A?vv?K#l8X^f$1C!LNtyRr6rgHOHh0auBZN;6~ z3gsesn}&&%Qf`d08_*kOkqcKFA`k@?%4i-qAw3M&(%@6$*i z9p5PAK1Sba5%{>tQu-MECqA>IMhJ23V|3h^n=XH>AtZD$X3FwNa&@0HcoD~by#wU1 zRQCY}qAC0wYVKoa@bkN{^t!T&@gewdDAMiwLeHE8)^e*1^l# zNY=p-jbt61kZZ|07%0s$R>JKDQVzS_y2=S__mA#TZ9Dh(+#=Z|mpM4K2~Y<|AcQyx zFIay=NRx0tslQWI!YRKpfEtd?h3aJR`7oF4)Y)?qrU_o0^x4uPEjMgY6BjzJoQg{< zSIEW1owE>PTfv$asi9$Qlh{gehD~hZO!iUJ9Cp}YhaGm})1CC|!XhkpXh zedp>E_FZ990yL&xL((^u8(X0>l~_}8H`xl4p|M#hlZi9gM@@6sVTT?5E^v0(VTT=d y*kOkqcKAzR!TT^>bJ$^r9d_7ZhaGl!YySY9OUqfHs?!_*0000Sd From 1ea5313fb3dd8d606a5b59b956a98c2af4128d13 Mon Sep 17 00:00:00 2001 From: Fiona McNeil Date: Mon, 23 Oct 2017 16:34:52 -0700 Subject: [PATCH 264/576] Fixes #318. Moved the floating panels in compound reg over some more and got rid of a white background that was obscuring other features. --- modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css index db64370f3..44d331769 100755 --- a/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css +++ b/modules/CmpdReg/src/client/css/NewCmpdReg_nocompile.css @@ -448,11 +448,10 @@ div { .ErrrorNotificationListView { position: fixed; bottom: 60px; - left: 140px; + left: 100px; z-index: 100; width: 400px; margin-left: 150px; - background-color: white; } .ErrrorNotificationListView .showHideButton { margin-left: 7px; From 2dd900457cab27728a0a857cedd585da50175ccb Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 23 Oct 2017 19:29:06 -0700 Subject: [PATCH 265/576] initial progress on sel preprocessor example --- .../client/ACASBarcodeSELPreprocessor.coffee | 12 ++ .../ACASBarcodeSELPreprocessorRoutes.coffee | 116 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 modules/ACASBarcodeSELPreprocessor/src/client/ACASBarcodeSELPreprocessor.coffee create mode 100644 modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee diff --git a/modules/ACASBarcodeSELPreprocessor/src/client/ACASBarcodeSELPreprocessor.coffee b/modules/ACASBarcodeSELPreprocessor/src/client/ACASBarcodeSELPreprocessor.coffee new file mode 100644 index 000000000..1f793baee --- /dev/null +++ b/modules/ACASBarcodeSELPreprocessor/src/client/ACASBarcodeSELPreprocessor.coffee @@ -0,0 +1,12 @@ +class window.ACASBarcodeSELPreprocessor extends BasicFileValidateAndSaveController + + initialize: -> + @fileProcessorURL = "/aCASBarcodeSELPreprocessor/parseSEL" + @errorOwnerName = 'ACASBarcodeSELPreprocessor' + @loadReportFile = true + @loadImagesFile = true + @allowedFileTypes = ['csv', 'CSV'] + super() + @$('.bv_moduleTitle').html('Convert Barcodes to Lot/Batch Names and Load Experiment') + + diff --git a/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee new file mode 100644 index 000000000..8d2c4dcd5 --- /dev/null +++ b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee @@ -0,0 +1,116 @@ +exports.setupAPIRoutes = (app, loginRoutes) -> + app.post '/aCASBarcodeSELPreprocessor/parseSEL', exports.parseSEL + +exports.setupRoutes = (app, loginRoutes) -> + app.post '/aCASBarcodeSELPreprocessor/parseSEL', loginRoutes.ensureAuthenticated, exports.parseSEL + + +exports.parseSEL = (req, resp) -> + req.connection.setTimeout 6000000 + serverUtilityFunctions = require './ServerUtilityFunctions.js' + fs = require 'fs' + path = require('path') + inventoryServices = require "./InventoryServiceRoutes.js" + serverUtilityFunctions = require './ServerUtilityFunctions.js' + config = require '../conf/compiled/conf.js' + ACAS_HOME = '.' + + selData = req.body + + replyData = + transactionId: null + results: + path: req.body.path + fileToParse: selData.fileToParse + reportFile: selData.reportFile + htmlSummary: "" + dryRunMode: true + hasError: false + hasWarning: false + errorMessages: [] + + addError = (msg) -> + replyData.hasError = true + replyData.errorMessages.push + errorLevel: "error", message: msg + + infilePath = path.join ACAS_HOME, config.all.server.datafiles.relative_path, selData.fileToParse + fs.readFile infilePath, 'utf8', (err, selFile) -> + if err? + addError "Error reading file "+err + resp.json replyData + return + + inLines = selFile.split '\r' + outLines = [] + barcodesToSub = [] + linesToSub = [] + rawLines = [] + foundBarcodeHeader = false + foundRawResultsHeader = false + for line in inLines + console.log line + cells = line.split ',' + if foundBarcodeHeader && !foundRawResultsHeader + if cells[0].trim() != "" + barcodesToSub.push cells[0].trim() + linesToSub.push = cells + else + outLines.push cells.join ',' + else if foundRawResultsHeader + rawLines.push = line + else if !foundBarcodeHeader + outLines.push line + else + if cells[0].trim() == "Barcode" + foundBarcodeHeader = true + cells[0] == "Corporate Batch ID" + outLines.push cells.join ',' + else if cells[0].trim() == "Raw Results" + foundRawResultsHeader = true + rawLines.push line + + if !foundBarcodeHeader + addError "Expected to find left-hand cell containing the word \"Barcode\"" + resp.json replyData + return + + console.log barcodesToSub + inventoryServices.getWellContentByContainerLabelsInternal barcodesToSub, 'container', 'tube', 'barcode', 'barcode', (wells, wellStatusCode) -> + if wellStatusCode != 200 + addError "There was a problem looking up the barcodes" + resp.json replyData + return + + batches = {} + for well in wells + wellInfo = wells[0]?.wellContent[0] + if wellInfo?.batchCode? and wellInfo.batchCode != "" + batches[wellInfo.barcode] = wellInfo.batchCode + else + addError "Could not find lot/bath name for barcode #{wellInfo.barcode}" + if replyData.hasError + resp.json replyData + return + + console.log batches + for line in linesToSub + line[0] = batches[line[0]] + outLines.push line.join ',' + + outLines.push rawLines + console.log "number of lines: "+outLines.length + + outFile = outLines.join '\r\n' + outFileName = selData.fileToParse.slice(0, -4) + "_converted.csv" + outFilePath = path.join ACAS_HOME, config.all.server.datafiles.relative_path, outFileName + fs.writeFile outFilePath, outFile, 'utf8', (err) -> + if err? + addError "Error writing intermediate file "+err + resp.json replyData + return + + selData.fileToParse = outFileName + console.log selData + serverUtilityFunctions.runRFunctionOutsideRequest selData.user, selData, "src/r/GenericDataParser/generic_data_parser.R", "parseGenericData", (rReturn) -> + resp.end rReturn From 5d06701a846704cb0c48fd77b39d67126113a884 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 23 Oct 2017 20:34:04 -0700 Subject: [PATCH 266/576] Fixes #321 getParentVialByDaughterVialBarcodeInternal throws error if no parent found. This was not breaking anything, just putting errors on the log --- .../ServerAPI/src/server/routes/InventoryServiceRoutes.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 63685ff77..e2a67e50f 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -3500,6 +3500,7 @@ exports.getParentVialByDaughterVialBarcodeInternal = (daughterVialBarcode, callb return if advSearchReturn.results.length < 1 callback null, responseStub + return parentWell = advSearchReturn.results[0] responseStub.parentWellCodeName = parentWell.codeName parentWellLabel = _.findWhere parentWell.lsLabels, {lsType: 'name', lsKind: 'well name', ignored: false} From 7fa37f337c2008b31b180a8116d667a53c0ff27c Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 24 Oct 2017 20:56:13 -0700 Subject: [PATCH 267/576] #328: add 'Aliquot' to configured entity types and preferred entity services --- conf/ConfiguredEntityTypes.coffee | 9 +++ .../routes/PreferredEntityCodeService.coffee | 68 +++++++++++++++---- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/conf/ConfiguredEntityTypes.coffee b/conf/ConfiguredEntityTypes.coffee index f79625447..8b49f5bef 100644 --- a/conf/ConfiguredEntityTypes.coffee +++ b/conf/ConfiguredEntityTypes.coffee @@ -74,3 +74,12 @@ exports.entityTypes = sourceExternal: false parent: false model: require("../routes/ServerUtilityFunctions.js").DefinitionContainerTube + + 'Aliquot': + type: 'container' + kind: 'tube' + codeOrigin: 'ACAS LsContainer' + displayName: 'Aliquot' + sourceExternal: false + parent: false + diff --git a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee index de5e97782..96b526e25 100644 --- a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee +++ b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee @@ -48,10 +48,11 @@ exports.getSpecificEntityTypeRoute = (req, resp) -> resp.json json exports.getSpecificEntityType = (displayName, callback) -> - if configuredEntityTypes.entityTypes[displayName]? - callback configuredEntityTypes.entityTypes[displayName] + entityType = _.findWhere configuredEntityTypes.entityTypes, {displayName:displayName} + if callback? + callback entityType else - callback {} + return entityType exports.getSpecificEntityTypeByTypeKindAndCodeOrigin = (type, kind, codeOrigin) -> entityType = _.findWhere configuredEntityTypes.entityTypes, {type: type, kind: kind, codeOrigin: codeOrigin} @@ -108,7 +109,7 @@ exports.referenceCodes = (requestData, csv, callback) -> return else # internal source - entityType = configuredEntityTypes.entityTypes[requestData.displayName] + entityType = exports.getSpecificEntityType requestData.displayName if entityType.codeOrigin is "ACAS LsThing" preferredThingService = require "./ThingServiceRoutes.js" reqHashes = @@ -130,19 +131,56 @@ exports.referenceCodes = (requestData, csv, callback) -> else if entityType.codeOrigin is "ACAS LsContainer" console.log "entityType.codeOrigin is ACAS LsContainer" console.log reqList - console.log reqList + # reqList = [reqList[0].requestName] preferredContainerService = require "./InventoryServiceRoutes.js" - reqHashes = - containerType: entityType.type - containerKind: entityType.kind - requests: reqList - preferredContainerService.getContainerCodesFromNamesOrCodes reqHashes, (codeResponse) -> - console.log "codeResponse" - console.log codeResponse - callback - displayName: requestData.displayName - results: formatJSONReferenceCode(codeResponse.results, "referenceName") + if requestData.displayName == "Aliquot" + reqHashes = + containerType: entityType.type + containerKind: entityType.kind + requests: reqList + labels = _.pluck reqList, 'requestName' + preferredContainerService.getWellContentByContainerLabelsInternal labels, null, null, null, null, (response, statusCode) -> + out = [] + for res in response + if res.containerCodeName? && res.wellContent? && res.wellContent.length == 1 && res.wellContent[0].physicalState == "solution" + codeName = res.containerCodeName + else + codeName = "" + out.push + requestName: res.label + referenceName: codeName + if csv + out = for res in out + res.requestName + "," + res.referenceName + outStr = "Requested Name,Reference Code\n"+out.join('\n') + callback + displayName: requestData.displayName + resultCSV: outStr + else + callback + displayName: requestData.displayName + results: out + else + reqHashes = + containerType: entityType.type + containerKind: entityType.kind + requests: reqList + preferredContainerService.getContainerCodesFromNamesOrCodes reqHashes, (codeResponse) -> + console.log "codeResponse" + console.log codeResponse + console.log "sould be :#{csv}" + if csv + out = for res in codeResponse.results + res.requestName + "," + res.referenceName + outStr = "Requested Name,Reference Code\n"+out.join('\n') + callback + displayName: requestData.displayName + resultCSV: outStr + else + callback + displayName: requestData.displayName + results: formatJSONReferenceCode(codeResponse.results, "referenceName") return #this is the fall-through for internal. External fall-through is in csUtilities.getExternalReferenceCodes message = "problem with internal preferred Code request: code type and kind are unknown to system" From 334d5bc6785e7d6bcdbf8babc03f7b63f6d8c3c9 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 24 Oct 2017 20:57:45 -0700 Subject: [PATCH 268/576] #329: SEL should check protocol for required entity type --- .../src/server/generic_data_parser.R | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 1d921f0a3..e6452fede 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -470,7 +470,6 @@ validateCalculatedResults <- function(calculatedResults, dryRun, curveNames, tes # a "data.frame" of the validated calculated results require(data.table) - entityTypeAndKindList <- fromJSON(getURLcheckStatus(paste0(racas::applicationSettings$server.nodeapi.path, "/api/entitymeta/configuredEntityTypes/"), requireJSON = TRUE)) @@ -1178,7 +1177,7 @@ organizeCalculatedResults <- function(calculatedResults, inputFormat, formatPara if (lockCorpBatchId) { if(calculatedResultsValueKindRow[1] != mainCode && !precise) { stopUser(paste0("Could not find '", mainCode, "' column. The ", mainCode, - " column should be the first column of the Calculated Results")) + " column should be the first column of the Calculated Results")) } } else { if (!(mainCode %in% unlist(calculatedResultsValueKindRow)) && !precise) { @@ -2815,18 +2814,40 @@ runMain <- function(pathToGenericDataFormatExcelFile, reportFilePath=NULL, # Grab the Calculated Results Section calculatedResults <- getSection(genericDataFileDataFrame, lookFor = formatParameters$lookFor, transpose = FALSE) + # Get the protocol and experiment + newProtocol <- FALSE + if (!useExisting) { + protocol <- getProtocolByNameAndFormat(protocolName = validatedMetaData$'Protocol Name'[1], configList, inputFormat) + newProtocol <- is.na(protocol[[1]]) + if (!newProtocol) { + metaData$'Protocol Name'[1] <- getPreferredProtocolName(protocol, validatedMetaData$'Protocol Name'[1]) + } + } + # Organize the Calculated Results if (inputFormat %in% c("Gene ID Data", "Generic", "Dose Response")) { mainCode <- calculatedResults[2, 1] #Getting this from its standard position } else { mainCode <- "Corporate Batch ID" } + + if(!newProtocol) { + requiredMainCode <- getProtocolRequiredEntityCode(protocol) + if(length(requiredMainCode) > 0) { + requiredMainCode <- requiredMainCode[[1]] + if(mainCode != requiredMainCode) { + warnUser(paste0("'",metaData$'Protocol Name'[1],"' requires an entity type of '",requiredMainCode,"'. Please update your file with this entity type instead of the entity type '",mainCode,"'")) + } + mainCode <- requiredMainCode + calculatedResults[2, 1] <- mainCode + } + } + calculateGroupingID <- if (rawOnlyFormat) {calculateTreatmemtGroupID} else {NA} organizedResultsList <- organizeCalculatedResults( calculatedResults, inputFormat, formatParameters, mainCode, lockCorpBatchId = formatParameters$lockCorpBatchId, rawOnlyFormat = rawOnlyFormat, errorEnv = errorEnv, precise = precise, calculateGroupingID = calculateGroupingID) - calculatedResults <- organizedResultsList[[1]] selColumnOrderInfo <- organizedResultsList[[2]] @@ -2878,21 +2899,11 @@ runMain <- function(pathToGenericDataFormatExcelFile, reportFilePath=NULL, customExperimentMetaDataValues <- NULL } - - # Get the protocol and experiment and, when not on a dry run, create them if they do not exist - newProtocol <- FALSE - if (!useExisting) { - protocol <- getProtocolByNameAndFormat(protocolName = validatedMetaData$'Protocol Name'[1], configList, inputFormat) - newProtocol <- is.na(protocol[[1]]) - if (!newProtocol) { - metaData$'Protocol Name'[1] <- getPreferredProtocolName(protocol, validatedMetaData$'Protocol Name'[1]) - } - } - + # when not on a dry run, create protocol and experiment if they do not exist if (!dryRun && newProtocol && errorFree) { protocol <- createNewProtocol(metaData = validatedMetaData, lsTransaction, recordedBy) } - + useExistingExperiment <- inputFormat %in% c("Use Existing Experiment", "Precise For Existing Experiment") if (useExistingExperiment) { experiment <- getExperimentByCodeName(validatedMetaData$'Experiment Code Name'[1]) @@ -3046,7 +3057,12 @@ getPreviousExperimentCodes <- function(experiment) { previousExperimentCodes <- lapply(previousCodeValues, getElement, "codeValue") return(previousExperimentCodes) } - +getProtocolRequiredEntityCode <- function(protocol) { + metadataState <- getStatesByTypeAndKind(protocol, "metadata_protocol metadata")[[1]] + requiredEntities <- getValuesByTypeAndKind(metadataState, "codeValue_required entity type") + requiredEntityCodes <- lapply(requiredEntities, getElement, "codeValue") + return(requiredEntityCodes) +} translateClassToValueType <- function(x, reverse = F) { # translates Excel style Number formats to ACAS valueTypes (or reverse) valueTypeVector <- c("numericValue", "stringValue", "fileValue", "inlineFileValue", "urlValue", "dateValue", "clobValue", "blobValue", "codeValue") From a9ee604b6b257a8fd611a4a0d318202940d40029 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 25 Oct 2017 11:08:07 -0700 Subject: [PATCH 269/576] #328: Add better seperations between the entity type code identifier and the display name. Fix Aliquot to use the new entityType.code field to distinguish different LsContainers --- conf/ConfiguredEntityTypes.coffee | 44 ++++++++--------- .../routes/PreferredEntityCodeService.coffee | 49 ++++++++++--------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/conf/ConfiguredEntityTypes.coffee b/conf/ConfiguredEntityTypes.coffee index 8b49f5bef..2883a203f 100644 --- a/conf/ConfiguredEntityTypes.coffee +++ b/conf/ConfiguredEntityTypes.coffee @@ -1,45 +1,45 @@ -exports.entityTypes = - 'Corporate Parent ID': +exports.entityTypes = [ + code: 'Corporate Parent ID' type: 'compound' kind: 'parent name' codeOrigin: 'ACAS CmpdReg' displayName: 'Corporate Parent ID' sourceExternal: true parent: true - - 'Corporate Batch ID': + , + code: 'Corporate Batch ID' type: 'compound' kind: 'batch name' codeOrigin: 'ACAS CmpdReg' displayName: 'Corporate Batch ID' sourceExternal: true parent: false - - 'Protein Parent': + , + code: 'Protein Parent' type: 'parent' kind: 'protein' codeOrigin: 'ACAS LsThing' displayName: 'Protein Parent' sourceExternal: false parent: true - - 'Protein Batch': + , + code: 'Protein Batch' type: 'batch' kind: 'protein' codeOrigin: 'ACAS LsThing' displayName: 'Protein Batch' sourceExternal: false parent: false - - 'Gene ID': + , + code: 'Gene ID' type: 'gene' kind: 'entrez gene' codeOrigin: 'ACAS LsThing' displayName: 'Gene ID' sourceExternal: false parent: false - - 'Container Plate': + , + code: 'Container Plate' type: 'container' kind: 'plate' codeOrigin: 'ACAS LsContainer' @@ -47,8 +47,8 @@ exports.entityTypes = sourceExternal: false parent: false model: require("../routes/ServerUtilityFunctions.js").ContainerPlate - - 'Container Tube': + , + code: 'Container Tube' type: 'container' kind: 'tube' codeOrigin: 'ACAS LsContainer' @@ -56,8 +56,8 @@ exports.entityTypes = sourceExternal: false parent: false model: require("../routes/ServerUtilityFunctions.js").ContainerTube - - 'Definition Container Plate': + , + code: 'Definition Container Plate' type: 'definition container' kind: 'plate' codeOrigin: 'ACAS LsContainer' @@ -65,8 +65,8 @@ exports.entityTypes = sourceExternal: false parent: false model: require("../routes/ServerUtilityFunctions.js").DefinitionContainerPlate - - 'Definition Container Tube': + , + code: 'Definition Container Tube' type: 'definition container' kind: 'tube' codeOrigin: 'ACAS LsContainer' @@ -74,12 +74,12 @@ exports.entityTypes = sourceExternal: false parent: false model: require("../routes/ServerUtilityFunctions.js").DefinitionContainerTube - - 'Aliquot': + , + code: 'Aliquot' type: 'container' kind: 'tube' codeOrigin: 'ACAS LsContainer' - displayName: 'Aliquot' + displayName: 'Solution Aliquot' sourceExternal: false parent: false - +] diff --git a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee index 96b526e25..94fe08c4d 100644 --- a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee +++ b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee @@ -31,11 +31,11 @@ exports.getConfiguredEntityTypesRoute = (req, resp) -> resp.json json exports.getConfiguredEntityTypes = (asCodes, callback) -> - console.log "asCodes: "+asCodes + console.debug "asCodes: "+asCodes if asCodes - codes = for own name, et of configuredEntityTypes.entityTypes - code: name - name: name + codes = for type in configuredEntityTypes.entityTypes + code: type.code + name: type.displayName ignored: false callback codes else @@ -49,6 +49,7 @@ exports.getSpecificEntityTypeRoute = (req, resp) -> exports.getSpecificEntityType = (displayName, callback) -> entityType = _.findWhere configuredEntityTypes.entityTypes, {displayName:displayName} + entityType ?= {} if callback? callback entityType else @@ -80,14 +81,22 @@ exports.referenceCodesRoute = (req, resp) -> resp.json json exports.referenceCodes = (requestData, csv, callback) -> - console.log "stubs mode is: "+global.specRunnerTestmode #Note specRunnerTestMode is handled within functions called from here - console.log("csv is " + csv) + console.debug "stubs mode is: "+global.specRunnerTestmode #Note specRunnerTestMode is handled within functions called from here + console.debug("csv is " + csv) # convert displayName to type and kind - exports.getSpecificEntityType requestData.displayName, (json) -> - requestData.type = json.type - requestData.kind = json.kind - requestData.sourceExternal = json.sourceExternal + entityType = exports.getSpecificEntityType requestData.displayName + if _.isEmpty entityType + #this is the fall-through for internal. External fall-through is in csUtilities.getExternalReferenceCodes + message = "problem with internal preferred Code request: code type and kind are unknown to system" + callback message + console.error message + return + else + console.debug 'not empty' + requestData.type = entityType.type + requestData.kind = entityType.kind + requestData.sourceExternal = entityType.sourceExternal if csv reqList = formatCSVRequestAsReqArray(requestData.entityIdStringLines) @@ -95,7 +104,7 @@ exports.referenceCodes = (requestData, csv, callback) -> reqList = requestData.requests if requestData.sourceExternal - console.log("looking up external entity") + console.debug("looking up external entity") csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' csUtilities.getExternalReferenceCodes requestData.displayName, reqList, (prefResp) -> if csv @@ -109,7 +118,6 @@ exports.referenceCodes = (requestData, csv, callback) -> return else # internal source - entityType = exports.getSpecificEntityType requestData.displayName if entityType.codeOrigin is "ACAS LsThing" preferredThingService = require "./ThingServiceRoutes.js" reqHashes = @@ -129,12 +137,11 @@ exports.referenceCodes = (requestData, csv, callback) -> displayName: requestData.displayName results: formatJSONReferenceCode(codeResponse.results, "referenceName") else if entityType.codeOrigin is "ACAS LsContainer" - console.log "entityType.codeOrigin is ACAS LsContainer" - console.log reqList - -# reqList = [reqList[0].requestName] + console.debug "entityType.codeOrigin is ACAS LsContainer" + console.debug reqList preferredContainerService = require "./InventoryServiceRoutes.js" - if requestData.displayName == "Aliquot" +# reqList = [reqList[0].requestName] + if entityType.code == "Aliquot" reqHashes = containerType: entityType.type containerKind: entityType.kind @@ -167,9 +174,6 @@ exports.referenceCodes = (requestData, csv, callback) -> containerKind: entityType.kind requests: reqList preferredContainerService.getContainerCodesFromNamesOrCodes reqHashes, (codeResponse) -> - console.log "codeResponse" - console.log codeResponse - console.log "sould be :#{csv}" if csv out = for res in codeResponse.results res.requestName + "," + res.referenceName @@ -182,10 +186,7 @@ exports.referenceCodes = (requestData, csv, callback) -> displayName: requestData.displayName results: formatJSONReferenceCode(codeResponse.results, "referenceName") return - #this is the fall-through for internal. External fall-through is in csUtilities.getExternalReferenceCodes - message = "problem with internal preferred Code request: code type and kind are unknown to system" - callback message - console.error message + #################################################################### # BEST LABELS From 627bb11f5b7d2b4d35ec93ae5358fd17adb33e2a Mon Sep 17 00:00:00 2001 From: John McNeil Date: Wed, 25 Oct 2017 13:39:26 -0700 Subject: [PATCH 270/576] Barcode SEL preprocessor is feature complete --- .../ACASBarcodeSELPreprocessorRoutes.coffee | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee index 8d2c4dcd5..fac6545d0 100644 --- a/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee +++ b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee @@ -33,6 +33,9 @@ exports.parseSEL = (req, resp) -> replyData.hasError = true replyData.errorMessages.push errorLevel: "error", message: msg + if replyData.results.htmlSummary == "" + replyData.results.htmlSummary = "Proproccesor Errors:
    " + replyData.results.htmlSummary += "#{msg}
    " infilePath = path.join ACAS_HOME, config.all.server.datafiles.relative_path, selData.fileToParse fs.readFile infilePath, 'utf8', (err, selFile) -> @@ -41,6 +44,7 @@ exports.parseSEL = (req, resp) -> resp.json replyData return + selFile = selFile.replace '\n', '' inLines = selFile.split '\r' outLines = [] barcodesToSub = [] @@ -51,24 +55,23 @@ exports.parseSEL = (req, resp) -> for line in inLines console.log line cells = line.split ',' - if foundBarcodeHeader && !foundRawResultsHeader - if cells[0].trim() != "" - barcodesToSub.push cells[0].trim() - linesToSub.push = cells - else - outLines.push cells.join ',' - else if foundRawResultsHeader - rawLines.push = line - else if !foundBarcodeHeader - outLines.push line - else + if !foundBarcodeHeader && !foundRawResultsHeader if cells[0].trim() == "Barcode" foundBarcodeHeader = true - cells[0] == "Corporate Batch ID" + cells[0] = "Corporate Batch ID" outLines.push cells.join ',' - else if cells[0].trim() == "Raw Results" + else + outLines.push line + else if !foundRawResultsHeader + if cells[0].trim() == "Raw Results" foundRawResultsHeader = true rawLines.push line + else + if cells[0].trim() != "" + barcodesToSub.push cells[0].trim() + linesToSub.push cells + else + rawLines.push line if !foundBarcodeHeader addError "Expected to find left-hand cell containing the word \"Barcode\"" @@ -84,11 +87,13 @@ exports.parseSEL = (req, resp) -> batches = {} for well in wells - wellInfo = wells[0]?.wellContent[0] + wellInfo = well.wellContent[0] + console.log wellInfo if wellInfo?.batchCode? and wellInfo.batchCode != "" - batches[wellInfo.barcode] = wellInfo.batchCode + console.log "batchCode "+wellInfo.batchCode + batches[well.label] = wellInfo.batchCode else - addError "Could not find lot/bath name for barcode #{wellInfo.barcode}" + addError "Could not find lot/batch name for barcode #{well.label}" if replyData.hasError resp.json replyData return @@ -96,12 +101,10 @@ exports.parseSEL = (req, resp) -> console.log batches for line in linesToSub line[0] = batches[line[0]] + console.log line outLines.push line.join ',' - outLines.push rawLines - console.log "number of lines: "+outLines.length - - outFile = outLines.join '\r\n' + outFile = outLines.join '\n' outFileName = selData.fileToParse.slice(0, -4) + "_converted.csv" outFilePath = path.join ACAS_HOME, config.all.server.datafiles.relative_path, outFileName fs.writeFile outFilePath, outFile, 'utf8', (err) -> From ae2532f3b74aba787742c48b720ab494644ee909 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Wed, 25 Oct 2017 14:33:42 -0700 Subject: [PATCH 271/576] Fixes #333 cleaned up log statements --- .../server/routes/ACASBarcodeSELPreprocessorRoutes.coffee | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee index fac6545d0..f174de04f 100644 --- a/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee +++ b/modules/ACASBarcodeSELPreprocessor/src/server/routes/ACASBarcodeSELPreprocessorRoutes.coffee @@ -53,7 +53,6 @@ exports.parseSEL = (req, resp) -> foundBarcodeHeader = false foundRawResultsHeader = false for line in inLines - console.log line cells = line.split ',' if !foundBarcodeHeader && !foundRawResultsHeader if cells[0].trim() == "Barcode" @@ -78,7 +77,6 @@ exports.parseSEL = (req, resp) -> resp.json replyData return - console.log barcodesToSub inventoryServices.getWellContentByContainerLabelsInternal barcodesToSub, 'container', 'tube', 'barcode', 'barcode', (wells, wellStatusCode) -> if wellStatusCode != 200 addError "There was a problem looking up the barcodes" @@ -88,9 +86,7 @@ exports.parseSEL = (req, resp) -> batches = {} for well in wells wellInfo = well.wellContent[0] - console.log wellInfo if wellInfo?.batchCode? and wellInfo.batchCode != "" - console.log "batchCode "+wellInfo.batchCode batches[well.label] = wellInfo.batchCode else addError "Could not find lot/batch name for barcode #{well.label}" @@ -98,10 +94,8 @@ exports.parseSEL = (req, resp) -> resp.json replyData return - console.log batches for line in linesToSub line[0] = batches[line[0]] - console.log line outLines.push line.join ',' outFile = outLines.join '\n' @@ -114,6 +108,5 @@ exports.parseSEL = (req, resp) -> return selData.fileToParse = outFileName - console.log selData serverUtilityFunctions.runRFunctionOutsideRequest selData.user, selData, "src/r/GenericDataParser/generic_data_parser.R", "parseGenericData", (rReturn) -> resp.end rReturn From 7262c946cb5ca0e0ea365ba145b2011fde74aee5 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 25 Oct 2017 16:38:49 -0700 Subject: [PATCH 272/576] Fixed iframe url to be docroot relative instead of absolute path to localhost:3000. Fixes #336 --- modules/CmpdReg/src/client/src/MetaLot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/src/MetaLot.js b/modules/CmpdReg/src/client/src/MetaLot.js index 434ebc523..43dd72a21 100755 --- a/modules/CmpdReg/src/client/src/MetaLot.js +++ b/modules/CmpdReg/src/client/src/MetaLot.js @@ -232,7 +232,7 @@ $(function() { console.log("about to load inventory"); console.log(window.configuration.metaLot.showLotInventory); if (window.configuration.metaLot.showLotInventory) { - this.$('.bv_lotInventory').append("") + this.$('.bv_lotInventory').append("") } } From 2cfc2cd5d8057d49475af6963db99409b972d9f0 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 25 Oct 2017 16:40:33 -0700 Subject: [PATCH 273/576] Switched empty mols to set mol = null instead of to empty string in RegistrationSearch. Only switched it here because SearchForm is already working as expected. Fixes #335 --- modules/CmpdReg/src/client/src/RegistrationSearch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/src/RegistrationSearch.js b/modules/CmpdReg/src/client/src/RegistrationSearch.js index 8bd6e3b99..39bde9fef 100755 --- a/modules/CmpdReg/src/client/src/RegistrationSearch.js +++ b/modules/CmpdReg/src/client/src/RegistrationSearch.js @@ -108,9 +108,9 @@ $(function() { if (this.useMarvin) { this.marvinSketcherInstance.exportStructure(this.exportFormat).then(function (molecule) { if (molecule.indexOf("0 0 0 0 0 0 0 0 0 0999") > -1) - mol = ''; + mol = null; else if (molecule.indexOf("M V30 COUNTS 0 0 0 0 0") > -1) - mol = ''; + mol = null; else mol = molecule; regSearch.set({ From e9b4f572accca11bfb066a6401cc9548abc4fc99 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 25 Oct 2017 16:50:13 -0700 Subject: [PATCH 274/576] Fixed three instances of sketcher not checking for mol V3000 empty mol. Fixes #11 --- modules/CmpdReg/src/client/src/EditParent.js | 2 ++ modules/CmpdReg/src/client/src/Salt.js | 2 ++ modules/CmpdReg/src/client/src/SaltForm.js | 2 ++ 3 files changed, 6 insertions(+) diff --git a/modules/CmpdReg/src/client/src/EditParent.js b/modules/CmpdReg/src/client/src/EditParent.js index b245f08ed..218998dab 100644 --- a/modules/CmpdReg/src/client/src/EditParent.js +++ b/modules/CmpdReg/src/client/src/EditParent.js @@ -101,6 +101,8 @@ $(function () { this.marvinSketcherInstance.exportStructure(this.exportFormat).then(function (molecule) { if (molecule.indexOf("0 0 0 0 0 0 0 0 0 0999") > -1) mol = null; + else if (molecule.indexOf("M V30 COUNTS 0 0 0 0 0") > -1) + mol = null; else mol = molecule; editParentSearch.set({ diff --git a/modules/CmpdReg/src/client/src/Salt.js b/modules/CmpdReg/src/client/src/Salt.js index 943c17daf..40a8b7a74 100755 --- a/modules/CmpdReg/src/client/src/Salt.js +++ b/modules/CmpdReg/src/client/src/Salt.js @@ -230,6 +230,8 @@ $(function() { if (molecule.indexOf("0 0 0 0 0 0 0 0 0 0999") > -1) mol = ''; + else if (molecule.indexOf("M V30 COUNTS 0 0 0 0 0") > -1) + mol = ''; else mol = molecule; self.exportStructComplete = true; // for spec support diff --git a/modules/CmpdReg/src/client/src/SaltForm.js b/modules/CmpdReg/src/client/src/SaltForm.js index 1d9ebd484..77c5a6042 100755 --- a/modules/CmpdReg/src/client/src/SaltForm.js +++ b/modules/CmpdReg/src/client/src/SaltForm.js @@ -167,6 +167,8 @@ $(function() { this.marvinSketcherInstance.exportStructure(this.exportFormat).then(function (molecule) { if (molecule.indexOf("0 0 0 0 0 0 0 0 0 0999") > -1) mol = ''; + else if (molecule.indexOf("M V30 COUNTS 0 0 0 0 0") > -1) + mol = ''; else mol = molecule; self.model.set({molStructure: mol}); From 70fba5f8274a9b2d953b4aa4f12450ea8004e311 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 26 Oct 2017 13:06:54 -0700 Subject: [PATCH 275/576] Fixed typo in one case of author services. Fixes #341 --- modules/ServerAPI/src/server/routes/AuthorRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index e69d78ddc..3ef80654c 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -767,7 +767,7 @@ checkIfEmailHasChangedAndIsUnique = (author, savedAuthor, callback) -> if author.emailAddress == savedAuthor.emailAddress callback null else - checkEmailIsUnique author (err, isUnique) -> + checkEmailIsUnique author, (err, isUnique) -> if err? callback err else if isUnique From c6a68a5b66c7c8fa66813aa4aadb18918929aea8 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 26 Oct 2017 16:11:11 -0700 Subject: [PATCH 276/576] feature/protocol-editor-add-required-entity-type-select: Add configs for showing and requiring value for Required Entity Type select list in protocol editor, fixes issue #344 --- conf/config.properties.example | 5 +++ .../ServerAPI/conf/ProtocolConfJSON.coffee | 3 ++ modules/ServerAPI/src/client/Protocol.coffee | 44 +++++++++++++++++++ modules/ServerAPI/src/client/Protocol.html | 7 +++ 4 files changed, 59 insertions(+) diff --git a/conf/config.properties.example b/conf/config.properties.example index 8db34cb77..c01efb9bb 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -556,5 +556,10 @@ client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER # For whether protocols and experiments should have sequential user defined corpName labels client.entity.saveInitialsCorpName=false +# Whether to show the Required Entity Type GUI picklist +client.protocol.requiredEntityType.save=false +# Whether the required entity type is a required field +client.protocol.requiredEntityType.allowEmpty=true + client.compoundInventory.enforceUppercaseBarcodes=false client.compoundInventory.daughterVials.strictMatchPhysicalState=false diff --git a/modules/ServerAPI/conf/ProtocolConfJSON.coffee b/modules/ServerAPI/conf/ProtocolConfJSON.coffee index 97bd4f053..d5a5764e1 100644 --- a/modules/ServerAPI/conf/ProtocolConfJSON.coffee +++ b/modules/ServerAPI/conf/ProtocolConfJSON.coffee @@ -139,6 +139,9 @@ , typeName: "codeValue" kindName: "model fit transformation units" + , + typeName: "codeValue" + kindName: "required entity type" ] labeltypes: diff --git a/modules/ServerAPI/src/client/Protocol.coffee b/modules/ServerAPI/src/client/Protocol.coffee index 3b2354491..1d21913b9 100644 --- a/modules/ServerAPI/src/client/Protocol.coffee +++ b/modules/ServerAPI/src/client/Protocol.coffee @@ -49,6 +49,16 @@ class window.Protocol extends BaseEntity assayStage + getRequiredEntityType: -> + requiredEntityType = @.get('lsStates').getOrCreateValueByTypeAndKind "metadata", "protocol metadata", "codeValue", "required entity type" + if requiredEntityType.get('codeValue') is undefined or requiredEntityType.get('codeValue') is "" or requiredEntityType.get('codeValue') is null + requiredEntityType.set codeValue: "unassigned" + requiredEntityType.set codeType: "entity type" + requiredEntityType.set codeKind: "required entity type" + requiredEntityType.set codeOrigin: "ACAS Configured Entity" + + requiredEntityType + getAssayPrinciple: -> assayPrinciple = @.get('lsStates').getOrCreateValueByTypeAndKind "metadata", "protocol metadata", "clobValue", "assay principle" if assayPrinciple.get('clobValue') is undefined @@ -113,6 +123,13 @@ class window.Protocol extends BaseEntity errors.push attribute: 'assayTreeRule' message: "Assay tree rule should not end with '/'" + if window.conf.protocol?.requiredEntityType?.save? and window.conf.protocol.requiredEntityType.save + if window.conf.protocol?.requiredEntityType?.allowEmpty? and window.conf.protocol.requiredEntityType.allowEmpty is false + ret = @getRequiredEntityType().get('codeValue') + if ret is "unassigned" or ret is undefined or ret is "" or ret is null + errors.push + attribute: 'requiredEntityType' + message: "Required entity type must be set" if window.conf.protocol?.showCurveDisplayParams? showCurveDisplayParams = window.conf.protocol.showCurveDisplayParams else @@ -161,6 +178,7 @@ class window.ProtocolBaseController extends BaseEntityController "keyup .bv_protocolName": "handleNameChanged" "keyup .bv_assayTreeRule": "handleAssayTreeRuleChanged" "change .bv_assayStage": "handleAssayStageChanged" + "change .bv_requiredEntityType": "handleRequiredEntityTypeChanged" "change .bv_projectCode": "handleProjectCodeChanged" "keyup .bv_assayPrinciple": "handleAssayPrincipleChanged" "change .bv_creationDate": "handleCreationDateChanged" @@ -272,6 +290,13 @@ class window.ProtocolBaseController extends BaseEntityController @$('.bv_minY').val(@model.getCurveDisplayMin().get('numericValue')) else @$('.bv_group_curveDisplayWrapper').hide() + showRequiredEntityType = false + if window.conf.protocol?.requiredEntityType?.save? + showRequiredEntityType = window.conf.protocol.requiredEntityType.save + if showRequiredEntityType + @setupRequiredEntityType() + else + @$('.bv_group_requiredEntityType').hide() super() @ @@ -304,6 +329,21 @@ class window.ProtocolBaseController extends BaseEntityController name: "Select Assay Stage" selectedCode: @model.getAssayStage().get('codeValue') + setupRequiredEntityType: -> + if window.conf.protocol?.requiredEntityType?.allowEmpty? and window.conf.protocol.requiredEntityType.allowEmpty is false + @$('.bv_requiredEntityTypeLabel').html '*Required Entity Type' + else + @$('.bv_requiredEntityTypeLabel').html 'Required Entity Type' + @requiredEntityTypeList = new PickListList() + @requiredEntityTypeList.url = "/api/entitymeta/configuredEntityTypes/displayName?asCodes=true" + @requiredEntityTypeListController = new PickListSelectController + el: @$('.bv_requiredEntityType') + collection: @requiredEntityTypeList + insertFirstOption: new PickList + code: "unassigned" + name: "Select Entity Type" + selectedCode: @model.getRequiredEntityType().get('codeValue') + setupProjectSelect: -> @projectList = new PickListList() @projectList.url = "/api/projects" @@ -419,6 +459,10 @@ class window.ProtocolBaseController extends BaseEntityController value = @assayStageListController.getSelectedCode() @handleValueChanged "AssayStage", value + handleRequiredEntityTypeChanged: => + value = @requiredEntityTypeListController.getSelectedCode() + @handleValueChanged "RequiredEntityType", value + handleProjectCodeChanged: => value = @projectListController.getSelectedCode() @handleValueChanged "ProjectCode", value diff --git a/modules/ServerAPI/src/client/Protocol.html b/modules/ServerAPI/src/client/Protocol.html index 292ed55ca..6c0589db1 100644 --- a/modules/ServerAPI/src/client/Protocol.html +++ b/modules/ServerAPI/src/client/Protocol.html @@ -84,6 +84,13 @@
    +
    + +
    + +
    +
    +
    From 53d7da2a77308c684466c5577797eb08cf99d8b9 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 26 Oct 2017 16:21:09 -0700 Subject: [PATCH 277/576] #329: Guard against NA, "", and "unassigned" --- modules/GenericDataParser/src/server/generic_data_parser.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index e6452fede..4dae3efe8 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -2833,7 +2833,7 @@ runMain <- function(pathToGenericDataFormatExcelFile, reportFilePath=NULL, if(!newProtocol) { requiredMainCode <- getProtocolRequiredEntityCode(protocol) - if(length(requiredMainCode) > 0) { + if(length(requiredMainCode) > 0 && !is.na(requiredMainCode) && requiredMainCode != "" && requiredMainCode != "unassigned") { requiredMainCode <- requiredMainCode[[1]] if(mainCode != requiredMainCode) { warnUser(paste0("'",metaData$'Protocol Name'[1],"' requires an entity type of '",requiredMainCode,"'. Please update your file with this entity type instead of the entity type '",mainCode,"'")) From 2a1700d3d831d206eae315657a950b54a3dea3f4 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 26 Oct 2017 16:38:34 -0700 Subject: [PATCH 278/576] #328: changing the code of 'Aliquot' to the more descriptive 'Solution Container Tube' --- conf/ConfiguredEntityTypes.coffee | 2 +- .../src/server/routes/PreferredEntityCodeService.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/ConfiguredEntityTypes.coffee b/conf/ConfiguredEntityTypes.coffee index 2883a203f..645de3a40 100644 --- a/conf/ConfiguredEntityTypes.coffee +++ b/conf/ConfiguredEntityTypes.coffee @@ -75,7 +75,7 @@ exports.entityTypes = [ parent: false model: require("../routes/ServerUtilityFunctions.js").DefinitionContainerTube , - code: 'Aliquot' + code: 'Solution Container Tube' type: 'container' kind: 'tube' codeOrigin: 'ACAS LsContainer' diff --git a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee index 94fe08c4d..8132b1260 100644 --- a/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee +++ b/modules/ServerAPI/src/server/routes/PreferredEntityCodeService.coffee @@ -141,7 +141,7 @@ exports.referenceCodes = (requestData, csv, callback) -> console.debug reqList preferredContainerService = require "./InventoryServiceRoutes.js" # reqList = [reqList[0].requestName] - if entityType.code == "Aliquot" + if entityType.code == "Solution Container Tube" reqHashes = containerType: entityType.type containerKind: entityType.kind From 8b6e31aa7bb6e528eb7fdc4d944848191e4d622f Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sat, 28 Oct 2017 17:55:22 -0700 Subject: [PATCH 279/576] Fixes #351 numeric form field rounding interfered with input --- .../src/client/ACASFormFields.coffee | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 05268ea7f..a8c06ef9c 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -193,6 +193,10 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField template: _.template($("#ACASFormLSNumericValueFieldView").html()) + initialize: -> + super() + @userInputEvent = false + applyOptions: -> super() if @options.toFixed? @@ -214,6 +218,7 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField value: numVal ignored: false super() + @userInputEvent = false setEmptyValue: -> @getModel().set @@ -224,13 +229,14 @@ class window.ACASFormLSNumericValueFieldController extends ACASFormAbstractField @$('.bv_number').val inputValue renderModelContent: => - if @toFixed? and @getModel().get('value')? - if !isNaN @getModel().get('value') - @$('.bv_number').val @getModel().get('value').toFixed(@toFixed) + unless @userInputEvent + if @toFixed? and @getModel().get('value')? + if !isNaN @getModel().get('value') + @$('.bv_number').val @getModel().get('value').toFixed(@toFixed) + else + @$('.bv_number').val "" else - @$('.bv_number').val "" - else - @$('.bv_number').val @getModel().get('value') + @$('.bv_number').val @getModel().get('value') if @getModel().has 'unitKind' @$('.bv_units').html @getModel().get('unitKind') From 1e120d5a2b776b5640d4cfe45fcf06e356a59bd1 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sun, 29 Oct 2017 08:30:59 -0700 Subject: [PATCH 280/576] Fixes #353 added JQuery show/hide to location tree form field in addition to modal show/hide to fix issue where tree interferes with lot form editing even when hidden --- modules/Components/src/client/ACASFormLocationTree.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/Components/src/client/ACASFormLocationTree.coffee b/modules/Components/src/client/ACASFormLocationTree.coffee index c9be5cb32..5892c1269 100644 --- a/modules/Components/src/client/ACASFormLocationTree.coffee +++ b/modules/Components/src/client/ACASFormLocationTree.coffee @@ -52,6 +52,7 @@ class window.ACASFormLocationTreeController extends ACASFormAbstractFieldControl super() handleEditIconClicked: => + @$('.bv_locationTreeModal').show() @$('.bv_locationTreeModal').modal backdrop: 'static' @getContainerLocationTree() @@ -64,6 +65,7 @@ class window.ACASFormLocationTreeController extends ACASFormAbstractFieldControl error: (err) => alert 'Could not get container location tree. Please contact administrator' @$('.bv_locationTreeModal').modal 'hide' + @$('.bv_locationTreeModal').hide() success: (json) => @setupTree (json) @@ -109,6 +111,7 @@ class window.ACASFormLocationTreeController extends ACASFormAbstractFieldControl handleUpdateLocationClicked: => selectedLocation = @$('.bv_locationTree').jstree('get_selected', true) @$('.bv_locationTreeModal').modal 'hide' + @$('.bv_locationTreeModal').hide() value = selectedLocation[0].original.id breadcrumb = selectedLocation[0].original.breadcrumb From bd46dc564de3bdede6160b6acfe27c71bafb63ab Mon Sep 17 00:00:00 2001 From: John McNeil Date: Mon, 30 Oct 2017 13:40:32 -0700 Subject: [PATCH 281/576] Fixes #355 where location tree filter input has error styling when form field shows error --- .../src/client/ACASFormLocationTree.coffee | 8 ++++++++ .../src/client/ACASFormLocationTreeView.html | 15 ++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/modules/Components/src/client/ACASFormLocationTree.coffee b/modules/Components/src/client/ACASFormLocationTree.coffee index 5892c1269..8f03adaf9 100644 --- a/modules/Components/src/client/ACASFormLocationTree.coffee +++ b/modules/Components/src/client/ACASFormLocationTree.coffee @@ -9,6 +9,8 @@ class window.ACASFormLocationTreeController extends ACASFormAbstractFieldControl "click .bv_searchClear": "handleSearchClear" "click .bv_updateLocation": "handleUpdateLocationClicked" + className: "" + template: _.template($("#ACASFormLocationTreeView").html()) initialize: -> @@ -37,6 +39,12 @@ class window.ACASFormLocationTreeController extends ACASFormAbstractFieldControl error: (err) => alert 'error getting breadcrumb for container' + render: => + super() + + @$('.bv_controlGroup').addClass "bv_group_"+@modelKey + @ + setEmptyValue: -> @getModel().set value: null diff --git a/modules/Components/src/client/ACASFormLocationTreeView.html b/modules/Components/src/client/ACASFormLocationTreeView.html index 0bdc50d13..bc5ebba29 100644 --- a/modules/Components/src/client/ACASFormLocationTreeView.html +++ b/modules/Components/src/client/ACASFormLocationTreeView.html @@ -1,13 +1,14 @@ diff --git a/modules/CurveAnalysis/src/client/DoseResponseFit.coffee b/modules/CurveAnalysis/src/client/DoseResponseFit.coffee index fb93ff47c..b39da5031 100644 --- a/modules/CurveAnalysis/src/client/DoseResponseFit.coffee +++ b/modules/CurveAnalysis/src/client/DoseResponseFit.coffee @@ -50,6 +50,7 @@ class window.DoseResponseFitController extends Backbone.View drapType = window[parametersClass] controllerClass = curveFitClasses.get 'parametersController' drapcType = window[controllerClass] + parametersOptions = curveFitClasses.get 'parametersOptions' else drapType = 'unassigned' @@ -59,9 +60,9 @@ class window.DoseResponseFitController extends Backbone.View else @$('.bv_fitModelButton').show() if @options? && @options.initialAnalysisParameters? - drap = new drapType @options.initialAnalysisParameters + drap = new drapType @options.initialAnalysisParameters, parametersOptions else - drap = new drapType() + drap = new drapType null, parametersOptions @parameterController = new drapcType el: @$('.bv_analysisParameterForm') model: drap From 10e6b5840897eb482de8b1b8912efcc91fae5b28 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 30 Nov 2017 12:40:09 -0800 Subject: [PATCH 327/576] #423: Protocols created via SEL should have scientist set Currently protocols created through SEL do not have a scientist value. It should be set to be the same as the scientist for the experiment created through SEL --- .../src/server/generic_data_parser.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 94f449701..4da50f2b6 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -1716,6 +1716,17 @@ createNewProtocol <- function(metaData, lsTransaction, recordedBy) { if (is.null(protocolStatus) || protocolStatus == "") { protocolStatus <- "created" } + + protocolValues[[length(protocolValues)+1]] <- createStateValue( + recordedBy = recordedBy, + lsType = "codeValue", + lsKind = "scientist", + codeValue = metaData$Scientist, + codeType = "assay", + codeKind = "scientist", + codeOrigin = racas::applicationSettings$client.scientistCodeOrigin, + lsTransaction= lsTransaction) + protocolValues[[length(protocolValues)+1]] <- createStateValue( recordedBy = recordedBy, lsType = "codeValue", From 73fbb70b5ac7809529f58367be545e319f5027b9 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 4 Dec 2017 16:00:24 -0800 Subject: [PATCH 328/576] Added new config client.moduleMenus.logoTextLink to make homepage ACAS button URL configurable. Resolves #419 --- conf/config.properties.example | 1 + modules/ModuleMenus/src/client/ModuleMenus.coffee | 2 ++ 2 files changed, 3 insertions(+) diff --git a/conf/config.properties.example b/conf/config.properties.example index 545c93967..a21c6879b 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -185,6 +185,7 @@ client.service.external.lotDetails.url=/cmpdreg/#lot/ # If module is customized and the ACAS header name needs to be replaced, you can set the following options client.moduleMenus.logoText=ACAS client.moduleMenus.logoImageFilePath= +client.moduleMenus.logoTextLink=/ client.moduleMenus.homePageMessage=Welcome to ACAS client.moduleMenus.copyrightMessage=© John McNeil & Company 2012-2017 # client.moduleMenus.menuConfigurationSettings=ModuleMenusConfiguration diff --git a/modules/ModuleMenus/src/client/ModuleMenus.coffee b/modules/ModuleMenus/src/client/ModuleMenus.coffee index 946e6ca6b..e0f3d2804 100644 --- a/modules/ModuleMenus/src/client/ModuleMenus.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenus.coffee @@ -48,6 +48,8 @@ class window.ModuleMenusController extends Backbone.View if window.conf.moduleMenus.logoImageFilePath? logoInfo = ''+logoInfo @$('.bv_headerName').html logoInfo + if window.conf.moduleMenus.logoTextLink? + @$('.bv_headerName').attr('href', window.conf.moduleMenus.logoTextLink) if window.conf.moduleMenus.homePageMessage? @$('.bv_homePageMessage').html(window.conf.moduleMenus.homePageMessage) if window.conf.moduleMenus.copyrightMessage? From c6b64896e08ee1e29a060ed9038e6cb7b24e304b Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Fri, 8 Dec 2017 13:42:39 -0800 Subject: [PATCH 329/576] Rc/1.12.0: fixed bug in rendering unregistered entities for printing --- modules/ServerAPI/src/server/routes/index.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/index.coffee b/modules/ServerAPI/src/server/routes/index.coffee index b5afd7c0f..398c3c049 100644 --- a/modules/ServerAPI/src/server/routes/index.coffee +++ b/modules/ServerAPI/src/server/routes/index.coffee @@ -53,14 +53,16 @@ exports.toPrint = (req, res, moduleLaunchParams) -> console.log "toPrint" console.log req.query.moduleName console.log req.query.controller - #TODO: on redirect, pass in moduleName and code. Hardcoded right now. + unregistered = req.query.unregistered + if typeof unregistered is "string" + unregistered = (unregistered.toLowerCase() is "true") moduleLaunchParams = moduleName: req.query.moduleName code: req.params.code copy: false createFromOtherEntity: false print: true - unregistered: req.query.unregistered + unregistered: unregistered console.log "moduleLaunchParams" console.log moduleLaunchParams From e2877a720765f93e185aa86543461bd5d72ab515 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 12 Dec 2017 11:01:04 -0800 Subject: [PATCH 330/576] fixes #430: Curve curator doesn't fire a refit a when Smart mode is deselected --- modules/CurveAnalysis/src/client/DoseResponseAnalysis.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/CurveAnalysis/src/client/DoseResponseAnalysis.coffee b/modules/CurveAnalysis/src/client/DoseResponseAnalysis.coffee index 91ec19be8..f76bb53e0 100755 --- a/modules/CurveAnalysis/src/client/DoseResponseAnalysis.coffee +++ b/modules/CurveAnalysis/src/client/DoseResponseAnalysis.coffee @@ -101,7 +101,7 @@ class window.DoseResponseAnalysisParameters extends Backbone.Model errors.push attribute: 'theoreticalMax' message: "Theoretical max value must be set to a number" - if (_.isNaN(attrs.baseline.get('value')) or attrs.baseline.get('value') == null) + if (attrs.smartMode && (_.isNaN(attrs.baseline.get('value')) or attrs.baseline.get('value') == null)) errors.push attribute: 'baseline' message: "Baseline value must be set to a number" @@ -185,7 +185,6 @@ class window.DoseResponseAnalysisParametersController extends AbstractFormContro @$('.bv_theoreticalMax').attr 'disabled', 'disabled' @$('.bv_theoreticalMax').val "" @$('.bv_baseline').attr 'disabled', 'disabled' - @$('.bv_baseline').val "" @attributeChanged() From b3d3d69547a79a4fa5969f229699c4776f9cbed3 Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Thu, 14 Dec 2017 16:09:12 -0800 Subject: [PATCH 331/576] Rc/1.12.0: added config for which protocol clobValues from protocol metadata state are copied over to experiments --- conf/config.properties.example | 3 ++ .../ServerAPI/src/client/Experiment.coffee | 29 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 7995342f3..5f45a2a1b 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -534,3 +534,6 @@ client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER # For whether protocols and experiments should have sequential user defined corpName labels client.entity.saveInitialsCorpName=false + +# For which protocol clobValues from protocol metadata state are copied over to experiments +client.experiment.copiedProtocolMetadataAttrs= \ No newline at end of file diff --git a/modules/ServerAPI/src/client/Experiment.coffee b/modules/ServerAPI/src/client/Experiment.coffee index 678442dc4..83a6822ef 100644 --- a/modules/ServerAPI/src/client/Experiment.coffee +++ b/modules/ServerAPI/src/client/Experiment.coffee @@ -45,15 +45,22 @@ class window.Experiment extends BaseEntity resp copyProtocolAttributes: (protocol) => -#only need to copy protocol's experiment metadata attributes + #copy protocol's experiment metadata attributes + #copy protocol values from protocol metadata state as defined in config + if window.conf.experiment?.copiedProtocolMetadataAttrs? + protocolMetaAttrsToCopy = window.conf.experiment.copiedProtocolMetadataAttrs + protocolMetaAttrsToCopy = protocolMetaAttrsToCopy.split "," + else + protocolMetaAttrsToCopy = [] + pstates = protocol.get('lsStates') pExptMeta = pstates.getStatesByTypeAndKind "metadata", "experiment metadata" + eExptMeta = @.get('lsStates').getStatesByTypeAndKind "metadata", "experiment metadata" if pExptMeta.length > 0 - eExptMeta = @.get('lsStates').getStatesByTypeAndKind "metadata", "experiment metadata" if eExptMeta.length > 0 dapVal = eExptMeta[0].getValuesByTypeAndKind "clobValue", "data analysis parameters" if dapVal.length > 0 -#mark existing data analysis parameters, model fit parameters, and model fit type as ignored + #mark existing data analysis parameters, model fit parameters, and model fit type as ignored if dapVal[0].isNew() eExptMeta[0].get('lsValues').remove dapVal[0] else @@ -109,6 +116,22 @@ class window.Experiment extends BaseEntity mftu.unset 'lsTransaction' eExptMeta[0].get('lsValues').add mftu + pMetaState = pstates.getStatesByTypeAndKind "metadata", "protocol metadata" + if pMetaState.length > 0 + _.each protocolMetaAttrsToCopy, (pma) => + unless eExptMeta.length > 0 + eExptMeta = [@.get('lsStates').getOrCreateStateByTypeAndKind("metadata", "experiment metadata")] + pmaVal = eExptMeta[0].getValuesByTypeAndKind "clobValue", pma + if pmaVal.length > 0 + if pmaVal[0].isNew() + eExptMeta[0].get('lsValues').remove pmaVal[0] + else + pmaVal[0].set ignored: true + newVal = new Value(_.clone(pstates.getOrCreateValueByTypeAndKind "metadata", "protocol metadata", "clobValue", pma).attributes) + newVal.unset 'id' + newVal.unset 'lsTransaction' + eExptMeta[0].get('lsValues').add newVal + # commented because experiment base does not have these values # @getDryRunStatus().set ignored: true # @getDryRunStatus().set codeValue: 'not started' From 5b3c635b9dbfd2856042aaceb210ff8705cadcf0 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 14 Dec 2017 17:39:12 -0800 Subject: [PATCH 332/576] Set sensible defaults for new configs in config.properties.example, removed duplicate config, and reorganized related new configs to be together. Set sensible defaults for configuration.json and synced changes from client configuration.json and server. --- conf/config.properties.example | 19 +- .../src/client/custom/configuration.json | 207 ++++++++++-------- 2 files changed, 123 insertions(+), 103 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index a21c6879b..648d741a1 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -339,11 +339,6 @@ server.service.external.inventory.database.name= server.service.external.inventory.database.username= server.service.external.inventory.database.password= -client.compoundInventory.enforceUppercaseBarcodes=false -client.compoundInventory.rootLocationLabel=COMPANY -client.compoundInventory.trashLocationLabel=trash -client.compoundInventory.benchesLocationLabel=Benches - ## options for auth. strategy are: properties, database, ldap server.security.authstrategy=database #server.security.properties.path=/opt/seurat/SeuratServer/users.txt @@ -352,7 +347,7 @@ client.security.authstrategy=${server.security.authstrategy} #Controls whether Roo syncs the AuthorRole table with authorities granted in LDAP server.security.syncLdapAuthRoles=false -#to make authstrategy config accessible to the client +#to make syncLdapAuthRoles config accessible to the client client.security.syncLdapAuthRoles=${server.security.syncLdapAuthRoles} client.scientistCodeOrigin=ACAS authors client.molecularTargetCodeOrigin=ACAS DDICT @@ -448,8 +443,8 @@ client.protocol.showCurveDisplayParams=true client.experiment.uneditableFileTypes=["source file", "annotation file"] #Controls privileges for who can create/edit and delete authors -client.author.editingRoles= -client.author.deletingRoles= +client.author.editingRoles=ROLE_ACAS-ADMINS +client.author.deletingRoles=ROLE_ACAS-ADMINS # Default status for experiments loaded thorugh experiment loader server.sel.experimentStatus=approved @@ -571,8 +566,14 @@ client.protocol.requiredEntityType.save=false # Whether the required entity type is a required field client.protocol.requiredEntityType.allowEmpty=true +#Compound Inventory configs client.compoundInventory.enforceUppercaseBarcodes=false +#Default location tree labels +client.compoundInventory.rootLocationLabel=COMPANY +client.compoundInventory.trashLocationLabel=trash +client.compoundInventory.benchesLocationLabel=Benches +#Disallow loading daughter vials with different physical state than the parent vial client.compoundInventory.daughterVials.strictMatchPhysicalState=false # Whether to save location as a codeValue or as a full location interaction -client.compoundInventory.saveLocationAsCodeValue=false +client.compoundInventory.saveLocationAsCodeValue=true diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index cb3e1638f..8fa528ae4 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -1,15 +1,16 @@ { - "serverConnection": { - "connectToServer": true, - "baseServerURL": "/cmpdreg/", - "logoutURL": "/logout", - "acasURL": "http://localhost:8080/acas/api/v1/" - }, - "clientUILabels": { - "corpNameLabel": "CMPD Number", - "applicationNameForTitleBar": "ACAS Compound Registration", - "commonNameLabel": "Original ID:" - }, + "serverConnection": { + "connectToServer": true, + "baseServerURL": "/cmpdreg/", + "logoutURL": "/logout", + "acasURL": "http://localhost:8080/acas/api/v1/", + "acasAppURL": "http://localhost:3001/api/" + }, + "clientUILabels": { + "corpNameLabel": "CMPD Number", + "applicationNameForTitleBar": "ACAS Compound Registration", + "commonNameLabel": "Original ID:" + }, "ketcher": true, "regSearchResults": { "hideVirtualOption": false @@ -17,96 +18,114 @@ "searchForm": { "defaultMaxResults": 100 }, - "metaLot": { - "saltBeforeLot": false, - "lotCalledBatch": false, - "showBuid": false, - "includeAbbrevInIsoSaltOption": true, - "saltListNoneOption": "A: none", - "isotopeListNoneOption": "A: none", - "sortSaltsByAbbrev": true, - "sortIsotopesByAbbrev": true, - "showSelectCategoryOption": true, - "showSelectCompoundTypeOption": true, - "showSelectParentAnnotationOption": true, - "showSelectCompoundTypeList": false, - "showSelectParentAnnotationList": false, - "fileSaveDirNamedForBatchName": true, - "useExactMass": true, + "metaLot": { + "saltBeforeLot": false, + "lotCalledBatch": false, + "showBuid": false, + "includeAbbrevInIsoSaltOption": true, + "saltListNoneOption": "A: none", + "isotopeListNoneOption": "A: none", + "sortSaltsByAbbrev": true, + "sortIsotopesByAbbrev": true, + "showSelectCategoryOption": true, + "showSelectCompoundTypeOption": true, + "showSelectParentAnnotationOption": true, + "showSelectCompoundTypeList": false, + "showSelectParentAnnotationList": false, + "fileSaveDirNamedForBatchName": true, + "useExactMass": true, "useProjectRolesToRestrictLotDetails": false, "showTareWeight": false, "showTotalAmountStored": false, "disableAliasEdit": false, - "showLotInventory": true, + "showLotInventory": false, "disableEditMyLots": false - }, - "serverSettings": { + }, + "serverSettings": { "corpPrefix": "CMPD", - "corpSeparator": "-", - "saltSeparator": "", - "batchSeparator": "-", - "numberCorpDigits": 7, - "formatBatchDigits": 2, - "startingCorpNumber": 0, - "fancyCorpNumberFormat": false, - "corpParentFormat": "standard", - "corpBatchFormat": "corp_batch_saltcode", - "appendSaltCodeToLotName": true, - "noSaltCode": "A", - "usePredefinedList": false, - "newUserIsChemist": false, - "uniqueNotebook": false, - "notebookSavePath": "/opt/cmpdreg/noteBookFiles/noteBook/", - "exactMatchDef": "DUPLICATE_TAUTOMER", - "maxSearchTime": 180000, - "maxSearchResults": 5000, - "unitTestDB": true, - "initalDBLoad": true, - "compoundInventory": true, - "checkACASDependenciesByBarcode": false, - "projectRestrictions": false, - "jchemVersion": "16.4.25.0", + "corpSeparator": "-", + "saltSeparator": "", + "batchSeparator": "-", + "numberCorpDigits": 7, + "formatBatchDigits": 3, + "startingCorpNumber": 0, + "fancyCorpNumberFormat": false, + "corpParentFormat": "standard", + "corpBatchFormat": "corp_batch_saltcode", + "appendSaltCodeToLotName": true, + "noSaltCode": "", + "usePredefinedList": false, + "newUserIsChemist": false, + "uniqueNotebook": false, + "notebookSavePath": "/opt/cmpdreg/noteBookFiles/noteBook/", + "exactMatchDef": "DUPLICATE_TAUTOMER", + "maxSearchTime": 180000, + "maxSearchResults": 5000, + "unitTestDB": false, + "initalDBLoad": false, + "projectRestrictions": false, + "compoundInventory": false, + "disableTubeCreationIfNoBarcode": false, + "checkACASDependenciesByContainerCode": false, + "jchemVersion": "16.4.25.0", + "databaseType": "postgres", + "standardizeStructure": false, + "useExternalStandardizerConfig": false, + "standardizerConfigFilePath": "/opt/acas/conf/standardizerConfig.xml", "orderSelectLists": true - }, - "bulkLoadSettings": { - "useProjectRoles":false, - "dbProperties": [ - {"name": "Lot Synthesis Date", "dataType": "date", "required": false}, - {"name": "Lot Notebook Page", "dataType": "string", "required": false}, - {"name": "Lot Chemist", "dataType": "string", "required": true}, - {"name": "Lot Purity Measured By", "dataType": "string", "required": false}, - {"name": "Parent Stereo Category", "dataType": "string", "required": true}, - {"name": "Lot Corp Name", "dataType": "string", "required": false}, - {"name": "Lot Number", "dataType": "numeric", "required": false}, - {"name": "Lot Barcode", "dataType": "string", "required": false}, - {"name": "Lot Color", "dataType": "string", "required": false}, - {"name": "Lot Salt Abbrev", "dataType": "string", "required": false}, - {"name": "Lot Salt Equivalents", "dataType": "string", "required": false}, - {"name": "Lot Amount", "dataType": "numeric", "required": false}, - {"name": "Lot Amount Units", "dataType": "string", "required": false}, - {"name": "Lot Solution Amount", "dataType": "numeric", "required": false}, - {"name": "Lot Solution Amount Units", "dataType": "string", "required": false}, - {"name": "Lot Supplier", "dataType": "string", "required": false}, - {"name": "Lot Supplier ID", "dataType": "string", "required": false}, - {"name": "Lot Purity", "dataType": "numeric", "required": false}, - {"name": "Lot Purity Operator", "dataType": "string", "required": false}, - {"name": "Lot Percent ee", "dataType": "numeric", "required": false}, - {"name": "Lot Comments", "dataType": "string", "required": false}, - {"name": "Lot Is Virtual", "dataType": "boolean", "required": false}, - {"name": "Lot Physical State", "dataType": "string", "required": false}, - {"name": "Lot Vendor", "dataType": "string", "required": false}, - {"name": "Lot Retain", "dataType": "numeric", "required": false}, - {"name": "Lot Retain Units", "dataType": "string", "required": false}, - {"name": "Lot Melting Point", "dataType": "numeric", "required": false}, - {"name": "Lot Boiling Point", "dataType": "numeric", "required": false}, - {"name": "Lot Supplier Lot", "dataType": "string", "required": false}, - {"name": "Project", "dataType": "string", "required": false}, - {"name": "CAS Number", "dataType": "string", "required": false}, - {"name": "Parent Corp Name", "dataType": "string", "required": false}, - {"name": "Parent Common Name", "dataType": "string", "required": false}, - {"name": "Parent Stereo Comment", "dataType": "string", "required": false} + }, + "bulkLoadSettings": { + "useProjectRoles": false, + "dbProperties": [ + {"name": "CAS Number", "dataType": "string", "required": false}, + {"name": "Lot Absorbance", "dataType": "numeric", "required": false}, + {"name": "Lot Alias", "dateType": "string", "required": false}, + {"name": "Lot Amount Units", "dataType": "string", "required": false}, + {"name": "Lot Amount", "dataType": "numeric", "required": false}, + {"name": "Lot Barcode", "dataType": "string", "required": false}, + {"name": "Lot Boiling Point", "dataType": "numeric", "required": false}, + {"name": "Lot Chemist", "dataType": "string", "required": true}, + {"name": "Lot Color", "dataType": "string", "required": false}, + {"name": "Lot Comments", "dataType": "string", "required": false}, + {"name": "Lot Corp Name", "dataType": "string", "required": false}, + {"name": "Lot Is Virtual", "dataType": "boolean", "required": false}, + {"name": "Lot Lambda", "dataType": "numeric", "required": false}, + {"name": "Lot Melting Point", "dataType": "numeric", "required": false}, + {"name": "Lot Notebook Page", "dataType": "string", "required": false}, + {"name": "Lot Number", "dataType": "numeric", "required": false}, + {"name": "Lot Observed Mass #1", "dataType": "numeric", "required": false}, + {"name": "Lot Observed Mass #2", "dataType": "numeric", "required": false}, + {"name": "Lot Percent ee", "dataType": "numeric", "required": false}, + {"name": "Lot Physical State", "dataType": "string", "required": false}, + {"name": "Lot Purity Measured By", "dataType": "string", "required": false}, + {"name": "Lot Purity Operator", "dataType": "string", "required": false}, + {"name": "Lot Purity", "dataType": "numeric", "required": false}, + {"name": "Lot Retain Location", "dataType": "string", "required": false}, + {"name": "Lot Retain Units", "dataType": "string", "required": false}, + {"name": "Lot Retain", "dataType": "numeric", "required": false}, + {"name": "Lot Salt Abbrev", "dataType": "string", "required": false}, + {"name": "Lot Salt Equivalents", "dataType": "string", "required": false}, + {"name": "Lot Solution Amount Units", "dataType": "string", "required": false}, + {"name": "Lot Solution Amount", "dataType": "numeric", "required": false}, + {"name": "Lot Stock Location", "dataType": "string", "required": false}, + {"name": "Lot Stock Solvent", "dataType": "string", "required": false}, + {"name": "Lot Storage Location", "dataType": "string", "required": false}, + {"name": "Lot Supplier ID", "dataType": "string", "required": false}, + {"name": "Lot Supplier Lot", "dataType": "string", "required": false}, + {"name": "Lot Supplier", "dataType": "string", "required": false}, + {"name": "Lot Synthesis Date", "dataType": "date", "required": false}, + {"name": "Lot Tare Weight Units", "dataType": "string", "required": false}, + {"name": "Lot Tare Weight", "dataType": "numeric", "required": false}, + {"name": "Lot Total Amount Stored Units", "dataType": "string", "required": false}, + {"name": "Lot Total Amount Stored", "dataType": "numeric", "required": false}, + {"name": "Lot Vendor", "dataType": "string", "required": false}, + {"name": "Parent Alias", "dateType": "string", "required": false}, + {"name": "Parent Common Name", "dataType": "string", "required": false}, + {"name": "Parent Corp Name", "dataType": "string", "required": false}, + {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false}, + {"name": "Parent Stereo Category", "dataType": "string", "required": true}, + {"name": "Parent Stereo Comment", "dataType": "string", "required": false}, + {"name": "Project", "dataType": "string", "required": false} ] } } - - From 8e60b0366f9507c52adf298c286add59d44f5180 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 08:42:12 -0800 Subject: [PATCH 333/576] Cleaned up default ModuleMenusConfiguration by: Removing module stubs Chem Struct, Admin Panel, and Logging Making all headers consistently non-collapsible Moving Project Editor and Project Browser down into Admin section --- .../client/ModuleMenusConfiguration.coffee | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee index fa030dd68..6443792dc 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee @@ -3,12 +3,8 @@ window.ModuleMenusConfiguration = isHeader: true menuName: "Load Data" requireUserRoles: [window.conf.roles.acas.userRole] -# collapsible: true + collapsible: false , -# isHeader: false -# menuName: "Chem Struct" -# mainControllerClassName: "ACASFormChemicalStructureExampleController" -# , isHeader: false menuName: "Experiment Loader" mainControllerClassName: "GenericDataParserController" @@ -31,12 +27,6 @@ window.ModuleMenusConfiguration = mainControllerClassName: window.conf.experiment.mainControllerClassName autoLaunchName:"experiment_base" requireUserRoles: [window.conf.roles.acas.userRole] - , - isHeader: false - menuName: "Project Editor" - mainControllerClassName: "ProjectController" - autoLaunchName:"project" - requireUserRoles: [window.conf.roles.acas.adminRole] , isHeader: false, menuName: "Example Thing" mainControllerClassName: "ExampleThingController" @@ -45,7 +35,7 @@ window.ModuleMenusConfiguration = , isHeader: true menuName: "Search and Edit" - collapsible: true + collapsible: false requireUserRoles: [window.conf.roles.acas.userRole] , isHeader: false, menuName: "Protocol Browser" @@ -55,15 +45,21 @@ window.ModuleMenusConfiguration = isHeader: false, menuName: "Experiment Browser" mainControllerClassName: "ExperimentBrowserController" requireUserRoles: [window.conf.roles.acas.userRole] - , - isHeader: false, menuName: "Project Browser" - mainControllerClassName: "ProjectBrowserController" - requireUserRoles: [window.conf.roles.acas.userRole] , isHeader: true menuName: "Admin" - collapsible: true + collapsible: false requireUserRoles: [window.conf.roles.acas.adminRole] + , + isHeader: false + menuName: "Project Editor" + mainControllerClassName: "ProjectController" + autoLaunchName:"project" + requireUserRoles: [window.conf.roles.acas.adminRole] + , + isHeader: false, menuName: "Project Browser" + mainControllerClassName: "ProjectBrowserController" + requireUserRoles: [window.conf.roles.acas.userRole] , isHeader: false menuName: "Author Editor" @@ -75,16 +71,6 @@ window.ModuleMenusConfiguration = menuName: "Author Browser" mainControllerClassName: "AuthorBrowserController" requireUserRoles: [window.conf.roles.acas.adminRole] - , - isHeader: false - menuName: "Admin Panel" - mainControllerClassName: "AdminPanelController" - autoLaunchName: "admin_panel" - requireUserRoles: [window.conf.roles.acas.adminRole] - , - isHeader: false, menuName: "Logging" - mainControllerClassName: "LoggingController" - requireUserRoles: [window.conf.roles.acas.adminRole] , isHeader: false menuName: "System Test" @@ -101,7 +87,7 @@ window.ModuleMenusConfiguration = isHeader: true menuName: "CmpdReg Admin" requireUserRoles: [window.conf.roles.cmpdreg.adminRole] - collapsible: true + collapsible: false , isHeader: false menuName: "CmpdReg Vendors" From b06fb865422985e7c54f19bb31963d9f4bc4fddd Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 09:46:27 -0800 Subject: [PATCH 334/576] Fixed copyright year on CmpdReg --- modules/CmpdReg/src/client/templates/AppControllerView.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/templates/AppControllerView.inc b/modules/CmpdReg/src/client/templates/AppControllerView.inc index 38f3a99e1..62d1a34b3 100755 --- a/modules/CmpdReg/src/client/templates/AppControllerView.inc +++ b/modules/CmpdReg/src/client/templates/AppControllerView.inc @@ -31,7 +31,7 @@
    - + From aa57152cec68ecfc3f2d0d9c89331b87984158f8 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 11:30:03 -0800 Subject: [PATCH 335/576] Fixed configuration.json so it does not append salt code to lot corp name --- modules/CmpdReg/src/client/custom/configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index 8fa528ae4..2a9bff507 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -52,7 +52,7 @@ "fancyCorpNumberFormat": false, "corpParentFormat": "standard", "corpBatchFormat": "corp_batch_saltcode", - "appendSaltCodeToLotName": true, + "appendSaltCodeToLotName": false, "noSaltCode": "", "usePredefinedList": false, "newUserIsChemist": false, From 5ce70a4332025546293373791d9b594093561a8f Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 12:07:30 -0800 Subject: [PATCH 336/576] Added in four missing Parent properties to DB Properties list so the default list is a complete set of available DB properties. NOTE: Not all of these are exposed to all users in the UI --- modules/CmpdReg/src/client/custom/configuration.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index 2a9bff507..b9a575f73 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -120,8 +120,12 @@ {"name": "Lot Total Amount Stored", "dataType": "numeric", "required": false}, {"name": "Lot Vendor", "dataType": "string", "required": false}, {"name": "Parent Alias", "dateType": "string", "required": false}, + {"name": "Parent Annotation", "dateType": "string", "required": false}, + {"name": "Parent Comment", "dataType": "string", "required": false}, {"name": "Parent Common Name", "dataType": "string", "required": false}, + {"name": "Parent Compound Type", "dateType": "string", "required": false}, {"name": "Parent Corp Name", "dataType": "string", "required": false}, + {"name": "Parent Is Mixture", "dataType": "boolean", "required": false}, {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false}, {"name": "Parent Stereo Category", "dataType": "string", "required": true}, {"name": "Parent Stereo Comment", "dataType": "string", "required": false}, From 2eedee23d1ecbdd620ec1c50b277891cf6fecd86 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 13:40:46 -0800 Subject: [PATCH 337/576] Removed Example Thing from default ModuleMenus --- .../ModuleMenus/src/client/ModuleMenusConfiguration.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee index 6443792dc..ab025af82 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee @@ -27,11 +27,6 @@ window.ModuleMenusConfiguration = mainControllerClassName: window.conf.experiment.mainControllerClassName autoLaunchName:"experiment_base" requireUserRoles: [window.conf.roles.acas.userRole] - , - isHeader: false, menuName: "Example Thing" - mainControllerClassName: "ExampleThingController" - autoLaunchName:"example_thing" - requireUserRoles: [window.conf.roles.acas.userRole] , isHeader: true menuName: "Search and Edit" From 5b8f6b2b179810ab5db39f5fbceccd55108d6609 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 15 Dec 2017 14:19:54 -0800 Subject: [PATCH 338/576] Bumped CmpdReg copyright notice to 2018 --- modules/CmpdReg/src/client/templates/AppControllerView.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/client/templates/AppControllerView.inc b/modules/CmpdReg/src/client/templates/AppControllerView.inc index 62d1a34b3..fb66bab28 100755 --- a/modules/CmpdReg/src/client/templates/AppControllerView.inc +++ b/modules/CmpdReg/src/client/templates/AppControllerView.inc @@ -31,7 +31,7 @@
    - + From aa4f1b55c7298bd16175e673d0a9745393f274e0 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 15 Dec 2017 14:31:24 -0800 Subject: [PATCH 339/576] setting ACAS_TAG, db tag and tomcat tag for release --- .env | 2 +- docker-compose.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env b/.env index b6a34d3d5..d1785277b 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -ACAS_TAG=develop +ACAS_TAG=latest diff --git a/docker-compose.yml b/docker-compose.yml index ff54e7f45..9413322c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: - logs:/home/runner/logs command: ["bin/acas.sh", "run", "rservices"] db: - image: mcneilco/acas-postgres:bingo + image: mcneilco/acas-postgres:1.0-bingo restart: always volumes: - dbstore:/var/lib/postgresql/data @@ -60,7 +60,7 @@ services: ports: - "5432:5432" tomcat: - image: mcneilco/tomcat-maven:openjdk8 + image: mcneilco/tomcat-maven:1.0-openjdk8 restart: always depends_on: - db @@ -81,12 +81,12 @@ services: - ./conf/docker/cmpdreg/environment/cmpdreg.env command: catalina.sh run roo: - image: mcneilco/acas-roo-server-oss:RC-1.13.0 + image: mcneilco/acas-roo-server-oss:${ACAS_TAG} volumes: - /usr/local/tomcat/webapps/acas command: /bin/true cmpdreg: - image: mcneilco/acas-cmpdreg-roo-server-oss:RC-1.13.0 + image: mcneilco/acas-cmpdreg-roo-server-oss:${ACAS_TAG} volumes: - /usr/local/tomcat/webapps/cmpdreg # Add chemaxon license files here From 771ef146ffaf98c7a9f1039b6246d0f8e11db4da Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 4 Jan 2018 13:28:35 -0800 Subject: [PATCH 340/576] #435: Remove SEL R warnings that occur when configured entity types are a ragged array --- modules/GenericDataParser/src/server/generic_data_parser.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 2e2ac355f..373a9d9b6 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -641,7 +641,7 @@ getEntityCodeFromEntityDisplayNameOrCode <- function(mainCodeOrDisplayName) { "/api/entitymeta/configuredEntityTypes/"), requireJSON = TRUE)) # Expected column names: 'type', 'kind', 'codeOrigin', 'displayName', 'sourceExternal' - entityTypeAndKindTable <- as.data.table(do.call(rbind, entityTypeAndKindList)) + entityTypeAndKindTable <- rbindlist(entityTypeAndKindList, fill = TRUE) entityTypeAndKindTable[, displayName := unlist(displayName)] if (length(entityTypeAndKindTable[displayName == mainCodeOrDisplayName, code])) { mainCode <- entityTypeAndKindTable[displayName == mainCodeOrDisplayName, code][[1]] @@ -659,7 +659,7 @@ getDisplayNameFromEntityCode <- function(mainCode) { "/api/entitymeta/configuredEntityTypes/"), requireJSON = TRUE)) # Expected column names: 'type', 'kind', 'codeOrigin', 'displayName', 'sourceExternal' - entityTypeAndKindTable <- as.data.table(do.call(rbind, entityTypeAndKindList)) + entityTypeAndKindTable <- rbindlist(entityTypeAndKindList, fill = TRUE) entityTypeAndKindTable[, displayName := unlist(displayName)] if (length(entityTypeAndKindTable[code == mainCode, displayName])) { displayName <- entityTypeAndKindTable[code == mainCode, displayName][[1]] From 43bec12d97297648684f30f05bf240646496c5b0 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 5 Jan 2018 15:36:24 -0800 Subject: [PATCH 341/576] #437 Correct display name for SEL report file --- modules/GenericDataParser/src/server/generic_data_parser.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 2e2ac355f..bbf9e6eaa 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -2988,7 +2988,7 @@ runMain <- function(pathToGenericDataFormatExcelFile, reportFilePath=NULL, # Upload the data if this is not a dry run if(!dryRun & errorFree) { - reportFileSummary <- paste0(validatedMetaData$'Protocol Name', " - ", validatedMetaData$'Experiment Name') + reportFileSummary <- basename(reportFilePath) if(rawOnlyFormat) { uploadRawDataOnly(metaData = validatedMetaData, lsTransaction, subjectData = calculatedResults, experiment, fileStartLocation = pathToGenericDataFormatExcelFile, configList, From 20f5412c9375373de8dfcba4393abda7e07a95d5 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sun, 14 Jan 2018 13:19:24 -0800 Subject: [PATCH 342/576] exposed function getContainerCodesFromLabelsInternal on exports --- .../src/server/routes/InventoryServiceRoutes.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 6f02c1200..23d676089 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -1293,7 +1293,7 @@ getContainerCodesFromLabels = (req, callback) -> callback json ) -getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> +exports.getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> if global.specRunnerTestmode response = codeName: 'CONT-0000001' @@ -1362,7 +1362,7 @@ exports.updateWellContentInternal = (wellContent, copyPreviousValues, callCustom inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' resp.json inventoryServiceTestJSON.getContainerCodesByLabelsResponse else - console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent.wells) + console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent) config = require '../conf/compiled/conf.js' baseurl = config.all.client.service.persistence.fullpath+"containers/updateWellContent?copyPreviousValues="+copyPreviousValues console.debug 'base url: ', baseurl @@ -3152,7 +3152,7 @@ checkLocationsExist = (fileEntryArray, callback) -> callback missingLocations checkBarcodesExist = (barcodes, callback) -> - getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> + exports.getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> existingBarcodes = [] newBarcodes = [] _.each containerCodes, (containerCodeEntry) -> From a35b2a052fa3695363fb7c40dace33317e974aa7 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sun, 14 Jan 2018 13:24:06 -0800 Subject: [PATCH 343/576] Revert "exposed function getContainerCodesFromLabelsInternal on exports" This reverts commit 20f5412c9375373de8dfcba4393abda7e07a95d5. --- .../src/server/routes/InventoryServiceRoutes.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 23d676089..6f02c1200 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -1293,7 +1293,7 @@ getContainerCodesFromLabels = (req, callback) -> callback json ) -exports.getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> +getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> if global.specRunnerTestmode response = codeName: 'CONT-0000001' @@ -1362,7 +1362,7 @@ exports.updateWellContentInternal = (wellContent, copyPreviousValues, callCustom inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' resp.json inventoryServiceTestJSON.getContainerCodesByLabelsResponse else - console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent) + console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent.wells) config = require '../conf/compiled/conf.js' baseurl = config.all.client.service.persistence.fullpath+"containers/updateWellContent?copyPreviousValues="+copyPreviousValues console.debug 'base url: ', baseurl @@ -3152,7 +3152,7 @@ checkLocationsExist = (fileEntryArray, callback) -> callback missingLocations checkBarcodesExist = (barcodes, callback) -> - exports.getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> + getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> existingBarcodes = [] newBarcodes = [] _.each containerCodes, (containerCodeEntry) -> From 6e3f6080154a483cc558258f58fc43165d8ac1a8 Mon Sep 17 00:00:00 2001 From: John McNeil Date: Sun, 14 Jan 2018 13:27:12 -0800 Subject: [PATCH 344/576] Fixes #439 getContainerCodesFromLabelsInternal is not available outside InventoryServiceRoutes.coffee --- .../src/server/routes/InventoryServiceRoutes.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee index 6f02c1200..23d676089 100644 --- a/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/InventoryServiceRoutes.coffee @@ -1293,7 +1293,7 @@ getContainerCodesFromLabels = (req, callback) -> callback json ) -getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> +exports.getContainerCodesFromLabelsInternal = (barcodes, containerType, containerKind, callback) -> if global.specRunnerTestmode response = codeName: 'CONT-0000001' @@ -1362,7 +1362,7 @@ exports.updateWellContentInternal = (wellContent, copyPreviousValues, callCustom inventoryServiceTestJSON = require '../public/javascripts/spec/ServerAPI/testFixtures/InventoryServiceTestJSON.js' resp.json inventoryServiceTestJSON.getContainerCodesByLabelsResponse else - console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent.wells) + console.debug 'incoming updateWellContentInternal request: ', JSON.stringify(wellContent) config = require '../conf/compiled/conf.js' baseurl = config.all.client.service.persistence.fullpath+"containers/updateWellContent?copyPreviousValues="+copyPreviousValues console.debug 'base url: ', baseurl @@ -3152,7 +3152,7 @@ checkLocationsExist = (fileEntryArray, callback) -> callback missingLocations checkBarcodesExist = (barcodes, callback) -> - getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> + exports.getContainerCodesFromLabelsInternal barcodes, 'container', 'tube', (containerCodes) -> existingBarcodes = [] newBarcodes = [] _.each containerCodes, (containerCodeEntry) -> From 54e85216f819a53d37ff0e5d5b07028261ee4d45 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 16 Jan 2018 11:34:53 -0800 Subject: [PATCH 345/576] fixes #442: Logger should write to stderr when in docker --- conf/config.properties.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/config.properties.example b/conf/config.properties.example index 648d741a1..f7b45c0a9 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -149,6 +149,8 @@ server.ssl.cert.passphrase=selfCertForACASServer client.service.rapache.port=1080 server.rapache.listen=* +server.rapache.forceAllToStdErrOnly=true + #todo not in codebase 6/2015 client.service.rapache.use.ssl=${client.use.ssl} client.service.rapache.host=rservices From 3df98d8529cdaf1b2a64ba5481de2d18c046c42a Mon Sep 17 00:00:00 2001 From: Eva Gao Date: Tue, 16 Jan 2018 13:08:58 -0800 Subject: [PATCH 346/576] feature/add-ignore-option-for-cmpdreg-scientist: added ignore option for all of the cmpdreg admin modules. Fixes Issue #441 --- .../CmpdRegAdmin/src/client/AbstractCmpdRegAdmin.coffee | 9 +++++++++ .../src/client/AbstractCmpdRegAdminBrowser.coffee | 2 ++ .../src/client/AbstractCmpdRegAdminBrowserView.html | 2 ++ .../src/client/AbstractCmpdRegAdminView.html | 6 ++++++ modules/CmpdRegAdmin/src/client/CmpdRegScientist.coffee | 1 + 5 files changed, 20 insertions(+) diff --git a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdmin.coffee b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdmin.coffee index 9736c1570..397db4541 100644 --- a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdmin.coffee +++ b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdmin.coffee @@ -10,6 +10,7 @@ class window.AbstractCmpdRegAdminController extends AbstractFormController events: -> "keyup .bv_cmpdRegAdminCode": "handleCmpdRegAdminCodeNameChanged" "keyup .bv_cmpdRegAdminName": "handleCmpdRegAdminNameChanged" + "click .bv_cmpdRegAdminIgnore": "handleCmpdRegAdminIgnoreChanged" "click .bv_save": "handleSaveClicked" "click .bv_backToCmpdRegAdminBrowserBtn": "handleBackToCmpdRegAdminBrowserClicked" # "click .bv_newEntity": "handleNewEntityClicked" @@ -69,6 +70,11 @@ class window.AbstractCmpdRegAdminController extends AbstractFormController code = @model.get('code') @$('.bv_cmpdRegAdminCode').val(code) @$('.bv_cmpdRegAdminCode').html(code) + if @model.get('ignore') is true + @$('.bv_cmpdRegAdminIgnore').attr 'checked', 'checked' + else + @$('.bv_cmpdRegAdminIgnore').removeAttr 'checked' + if @model.isNew() @$('.bv_save').html("Save") # @$('.bv_newEntity').hide() @@ -153,6 +159,9 @@ class window.AbstractCmpdRegAdminController extends AbstractFormController handleCmpdRegAdminNameChanged: => @model.set("name", UtilityFunctions::getTrimmedInput @$('.bv_cmpdRegAdminName')) + handleCmpdRegAdminIgnoreChanged: => + @model.set("ignore", @$('.bv_cmpdRegAdminIgnore').is(":checked")) + handleSaveClicked: => @callNameValidationService() diff --git a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee index be4fb31df..a6cadc4f9 100644 --- a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee +++ b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowser.coffee @@ -92,6 +92,8 @@ class window.CmpdRegAdminRowSummaryController extends Backbone.View toDisplay = code: @model.get('code') name: @model.get('name') + ignored: if @model.get('ignore')? then @model.get('ignore') else false + $(@el).html(@template(toDisplay)) @ diff --git a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowserView.html b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowserView.html index fe3d6ddd0..d526cc797 100644 --- a/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowserView.html +++ b/modules/CmpdRegAdmin/src/client/AbstractCmpdRegAdminBrowserView.html @@ -36,6 +36,7 @@ + diff --git a/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoader.coffee b/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoader.coffee new file mode 100644 index 000000000..e59bf7bce --- /dev/null +++ b/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoader.coffee @@ -0,0 +1,93 @@ +class window.LotPropertyBulkLoader extends Backbone.Model + defaults: + overwriteExisting: false + + validate: (attrs) => + errors = [] + return null + + +class window.LotPropertyBulkLoaderController extends AbstractParserFormController + template: _.template($("#LotPropertyBulkLoaderView").html()) + + events: + "change .bv_overwriteExisting": "handleOverwriteExistingCheckboxChanged" + + initialize: -> + @errorOwnerName = 'LotPropertyBulkLoaderController' + $(@el).html @template @model.attributes + @setBindings() + @setupProjectSelect() + @setupProtocolSelect("Lot Property Bulk Loader") + + render: => + super() + @ + + updateModel: -> + @trigger 'amDirty' + + handleOverwriteExistingCheckboxChanged: () => + overwriteExisting = @$('.bv_overwriteExisting').is(":checked") + @model.set overwriteExisting: overwriteExisting + @attributeChanged() + + +class window.LotPropertyBulkLoaderParserController extends BasicFileValidateAndSaveController + template: _.template($("#BasicFileValidateAndSaveViewLotPropertyBulkLoader").html()) + + initialize: -> + @fileProcessorURL = "/api/LotPropertyBulkLoader/fileProcessor" + @errorOwnerName = 'LotPropertyBulkLoaderController' + @allowedFileTypes = ['xls','xlsx', 'csv'] + @loadReportFile = false + super() + @$('.bv_moduleTitle').html('Lot Property Bulk Loader') + + @lpblc = new LotPropertyBulkLoaderController + model: new LotPropertyBulkLoader() + el: @$('.bv_additionalValuesForm') + @lpblc.on 'valid', @handleLPBLFormValid + @lpblc.on 'invalid', @handleLPBLFormInvalid + @lpblc.on 'notifyError', @notificationController.addNotification + @lpblc.on 'clearErrors', @notificationController.clearAllNotificiations + @lpblc.on 'amDirty', => + @trigger 'amDirty' + @lpblc.render() + + handleLPBLFormValid: => + if @parseFileUploaded + @handleFormValid() + + handleLPBLFormInvalid: => + @handleFormInvalid() + + handleFormValid: -> + if @lpblc.isValid() + super() + + handleValidationReturnSuccess: (json) => + @additionalData = + inputParameters: @lpblc.model.toJSON() + super(json) + @lpblc.disableAllInputs() + + showFileSelectPhase: -> + super() + if @lpblc? + @lpblc.enableAllInputs() + + validateParseFile: => + @lpblc.updateModel() + unless !@lpblc.isValid() + @additionalData = + inputParameters: @lpblc.model.toJSON() + super() + + validateParseFile: => + @lpblc.updateModel() + unless !@lpblc.isValid() + @additionalData = + inputParameters: @lpblc.model.toJSON() + super() + diff --git a/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoaderView.html b/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoaderView.html new file mode 100644 index 000000000..974b482a3 --- /dev/null +++ b/modules/LotPropertyBulkLoader/src/client/LotPropertyBulkLoaderView.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.R b/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.R new file mode 100644 index 000000000..1cedc9858 --- /dev/null +++ b/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.R @@ -0,0 +1,250 @@ +# Brian Bolt +# 2018 +# brian.bolt@boltengineered.com +library(RCurl) +library(data.table) +library(xtable) + + +validateNumeric <- function(inputValue) { + value <- suppressWarnings(as.numeric(gsub(",", "", as.character(inputValue)))) + isNumeric <- !is.na(value) + return(list(value, isNumeric)) +} +areNumbersEqual <- function(val1, val2) { + if(is.na(val1) & is.na(val2)) { + return(TRUE) + } + if((is.na(val1) & !is.na(val2)) || (!is.na(val1) & is.na(val1))) { + return(FALSE) + } + identical(val1, val2) +} + +validateFileInput <- function(fileData, headerMap, errorEnv) { + expectedHeaders <- "Lot Corporate Name" + + fileHeaders <- as.character(fileData[1,]) + missingNames <- expectedHeaders[!expectedHeaders %in% fileHeaders] + if(length(missingNames) > 0) { + stopUser(paste0("Missing columns in file: ",paste0("'",paste(missingNames, collapse = "','"), "'"))) + } + optionalHeaders <- fileHeaders[ !fileHeaders %in% expectedHeaders] + if(length(optionalHeaders) == 0) { + stopUser(paste0("You must provide atleast one property to load: ",paste0("'",paste(headerMap$fileHeader, collapse = "','"), "'"))) + } + unusedColumns <- optionalHeaders[!optionalHeaders %in% headerMap$fileHeader] + if(length(unusedColumns) > 0) { + racasMessenger$addWarning(paste0("The following columns are unused: ",paste0("'",paste(unusedColumns, collapse = "','"), "'"))) + } + usedHeaders <- optionalHeaders[ !optionalHeaders %in% unusedColumns] + if(length(usedHeaders) == 0) { + stopUser(paste0("You must provide atleast one property to load: ",paste0("'",paste(headerMap$fileHeader, collapse = "','"), "'"))) + } + validatedFileData <- fileData[2:nrow(fileData),fileHeaders %in% c(expectedHeaders, usedHeaders)] + names(validatedFileData) <- c(expectedHeaders, usedHeaders) + + mainCode <- "Corporate Batch ID" + newBatchIds <- as.data.table(getPreferredId2(validatedFileData$`Lot Corporate Name`, displayName = mainCode)) + newBatchIds[ , c("error", "warning", "Issues") := { + issues <- NA_character_ + error <- FALSE + warning <- FALSE + if (is.null(Reference.Code) || is.na(Reference.Code) || Reference.Code == "") { + error <- TRUE + issues <- paste0(mainCode, " '", Requested.Name, + "' has not been registered in the system. Contact your system administrator for help.") + } else if (as.character(Requested.Name) != as.character(Reference.Code)) { + warning <- TRUE + issues <- paste0("A ", mainCode, " that you entered, '", Requested.Name, + "', was replaced by preferred ", mainCode, " '", Reference.Code, + "'. If this is not what you intended, replace the ", mainCode, " with the correct ID.") + + } + list(error, warning, issues) + }, by = "Requested.Name"] + + validatedFileDataDT <- as.data.table(validatedFileData) + newBatchIds <- as.data.table(newBatchIds) + setkey(newBatchIds, 'Requested.Name' ) + setkey(validatedFileDataDT, 'Lot Corporate Name' ) + validatedFileDataDT[newBatchIds, c('Lot Corporate Name','error','warning','Issues') := list(Reference.Code, error, warning, Issues)] + return(validatedFileDataDT) +} +requestHandler <- function(request) { + racasMessenger <- messenger() + racasMessenger$reset() + racasMessenger$logger <- logger(logName = "com.mcneilco.acas.LotPropertyBulkLoader", reset=TRUE) + + fileData <- readExcelOrCsv(getUploadedFilePath(request$fileToParse)) + errorEnv <- globalenv() + + headerMap <- data.frame(fileHeader = c("Purity","Observed Mass #1","Observed Mass #2"), + ## These settings should come from the bulk load configuration but remove "Lot" from the name as this is already the context + mappedProperty = c("purity", "observedMassOne", "observedMassTwo"), + type = c("numeric", "numeric", "numeric") + , stringsAsFactors = FALSE) + + validatedData <- validateFileInput(fileData, headerMap, errorEnv) + + metaLotBaseURL <- paste0(applicationSettings$client.service.cmpdReg.persistence.basepath,"/metalots") + metaLotCorpNameURL <- paste0(metaLotBaseURL,"/corpName") + getPropertyNames <- names(validatedData)[ !names(validatedData) %in% c('Lot Corporate Name', 'error','warning', 'Issues')] + getProperties <- headerMap[match(getPropertyNames, headerMap$fileHeader),] + overwriteExisting <- as.logical(request$inputParameters$overwriteExisting) + validatedData[ error == FALSE, c(paste0(c("Current ", "New "), rep(getProperties$fileHeader, each = 2)),"error","warning","Issues") := { + metaLotBaseURL <- paste0(applicationSettings$client.service.cmpdReg.persistence.basepath,"/metalots") + metaLotCorpNameURL <- paste0(metaLotBaseURL,"/corpName") + metaLot <- fromJSON(getURL(paste0(metaLotCorpNameURL, "/",`Lot Corporate Name`)), ) + properties <- metaLot$lot[getProperties$mappedProperty] + values <- list() + issues <- Issues + for(i in 1:nrow(getProperties)) { + type <- getProperties$type[[i]] + if(is.null(properties[[i]])) { + currentProperty <- switch(type, + numeric = NA_real_, + character = NA_character_ + ) + } else { + currentProperty <- properties[[i]] + } + + inputProperty <- get(getProperties$fileHeader[[i]]) + validatedInputProperty <- switch(type, + numeric = validateNumeric(inputProperty), + character = validateCharacter(inputProperty)) + + if(!is.na(inputProperty) && validatedInputProperty[[2]] == FALSE) { + error <- TRUE + issues <- c(issues,"Input value not numeric") + } else { + numbersEqual <- areNumbersEqual(currentProperty, validatedInputProperty[[1]]) + if(!numbersEqual & !is.na(currentProperty)) { + if(overwriteExisting) { + warning <- TRUE + } else { + error <- TRUE + } + issues <- c(issues, paste0(getProperties$fileHeader[[i]]," overwrite")) + } + } + if(!error) { + print(error) + if(is.na(validatedInputProperty[[1]])) { + metaLot$lot[getProperties$mappedProperty[[i]]] <- NULL + } else { + metaLot$lot[getProperties$mappedProperty[[i]]] <- validatedInputProperty[[1]] + } + } + values <- c(values,list(currentProperty, validatedInputProperty[[1]])) + } + issues <- stats::na.omit(issues) + if(length(issues) > 0) { + issues <- paste0(issues, collapse = ", ") + } else { + issues <- NA_character_ + } + if(!as.logical(request$dryRunMode)) { + if(!error) { + metaLotJson <- toJSON(metaLot) + postJSONURL(metaLotBaseURL, metaLotJson) + } + } + c(values, list(error, warning, unique(issues))) + }, by = 'Lot Corporate Name', with = FALSE] + + validatedData[ , getProperties$fileHeader := NULL, with = FALSE] + + hasError <- FALSE + if(any(validatedData$error) | length(racasMessenger$errors) > 0) { + hasError <- TRUE + } + hasWarning <- FALSE + if(any(validatedData$warning) | length(racasMessenger$warnings) > 0) { + hasWarning <- TRUE + } + errorList <- lapply(racasMessenger$errors, function(x) {x$message}) + warningList <- lapply(racasMessenger$warnings, function(x) {x$message}) + errorMessages <- list() + errorMessages <- c(errorMessages, lapply(errorList, function(x) {list(errorLevel="error", message=x)})) + errorMessages <- c(errorMessages, lapply(warningList, function(x) {list(errorLevel="warning", message=x)})) + transactionId = NULL + fileName <- request[[1]] + dryRun <- request[[2]] + addToRow <- NULL + + if(!as.logical(request$dryRun)) { + validatedData[ , names(validatedData)[grepl("Current", names(validatedData))] := NULL, with = FALSE] + validatedData[ , "Issues" := NULL, with = FALSE] + } else { + errorRows <- which(validatedData$error)-1 + warningRows <- which(validatedData$warning & !validatedData$error)-1 + addToRows <- c(errorRows, warningRows) + if(length(errorRows) > 0) { + errorList <- c(errorList, list("See table for additional errors")) + } + if(length(warningRows) > 0) { + warningList <- c(warningList, list("See table for additional warnings")) + } + if(length(addToRows) > 0) { + command <- c(rep("error", times = length(errorRows)), rep("warning", times = length(warningRows))) + addToRow <- list(pos = as.list(addToRows), command = command) + } + } + + table <- print.xtable( + xtable(validatedData[ , c("error", "warning") := NULL]), "html",hline.after=c(-1,0, 1:nrow(validatedData)), + include.rownames=FALSE, caption.placement='top', + html.table.attributes='align="left"', print.rules = FALSE, size = "small", + add.to.row = addToRow + ) + + table <- paste('',table) + table <- gsub("",'
    ', table) + table <- gsub("warning ", '', table) + table <- gsub("error ", '', table) + + htmlSummary <- createHtmlSummary(hasError, errorList, hasWarning, warningList, + summaryInfo=NULL, as.logical(request$dryRun)) + + htmlSummary <- paste0(htmlSummary,"
    ",table) + + racasMessenger + response <- list( + commit= FALSE, + transactionId= transactionId, + results= list( + path= getwd(), + fileToParse= fileName, + dryRun= dryRun, + htmlSummary= htmlSummary + ), + hasError= hasError, + hasWarning = hasWarning, + errorMessages= errorMessages + ) + return( response) + +} \ No newline at end of file diff --git a/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.Rmd b/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.Rmd new file mode 100644 index 000000000..a5ef4d1d4 --- /dev/null +++ b/modules/LotPropertyBulkLoader/src/server/r/LotPropertyBulkLoader.Rmd @@ -0,0 +1,22 @@ +--- +title: "Lot Property Bulk Loader" +author: "`r request$user`" +date: '`r format(Sys.time(), "%Y/%m/%d")`' +output: + html_document: + self_contained: no +--- + + + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = FALSE) +``` + +```{r} +validatedData +``` \ No newline at end of file diff --git a/modules/LotPropertyBulkLoader/src/server/routes/LotPropertyBulkLoader.coffee b/modules/LotPropertyBulkLoader/src/server/routes/LotPropertyBulkLoader.coffee new file mode 100644 index 000000000..90112d0bd --- /dev/null +++ b/modules/LotPropertyBulkLoader/src/server/routes/LotPropertyBulkLoader.coffee @@ -0,0 +1,30 @@ +### To install this Module + +Add this line to modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee + isHeader: false + menuName: "Substance Loader" + mainControllerClassName: "SubstanceLoaderController" +### + +exports.setupAPIRoutes = (app, loginRoutes) -> + app.post '/api/lotPropertyBulkLoader/fileProcessor', exports.lotPropertyBulkLoader + +exports.setupRoutes = (app, loginRoutes) -> + app.post '/api/lotPropertyBulkLoader/fileProcessor', loginRoutes.ensureAuthenticated, exports.lotPropertyBulkLoader + +config = require '../conf/compiled/conf.js' +serverUtilityFunctions = require "./ServerUtilityFunctions" + +exports.lotPropertyBulkLoader = (request, response) -> + request.connection.setTimeout 86400000 + serverUtilityFunctions = require './ServerUtilityFunctions.js' + + response.writeHead(200, {'Content-Type': 'application/json'}); + serverUtilityFunctions.runRFunction( + request, + "src/r/LotPropertyBulkLoader/LotPropertyBulkLoader.R", + "requestHandler", + (rReturn) -> + response.end rReturn + ) + From 3380cca386c007d89526728194c55a7de3ed89b8 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 7 Dec 2018 16:34:32 -0800 Subject: [PATCH 409/576] modify creg to get scientists from customer specific functions, use strings for chemist, registered_by and modified_by --- conf/config.properties.example | 4 +-- docker-compose.yml | 18 +++++----- .../CmpdReg/src/client/custom/Lot_Custom.js | 2 +- modules/CmpdReg/src/client/src/MetaLot.js | 4 +-- modules/CmpdReg/src/client/src/Parent.js | 2 +- .../src/server/routes/CmpdRegRoutes.coffee | 33 +++++++------------ .../src/server/routes/loginRoutes.coffee | 12 ++++++- .../CustomerSpecificServerFunctions.coffee | 9 +++-- 8 files changed, 42 insertions(+), 42 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 9f1a8e332..ecd989330 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -371,9 +371,9 @@ client.browser.enableSearchAll=true ## Cmpd Reg Bulk Loader Options client.service.cmpdReg.persistence.host=tomcat -client.service.cmpdReg.persistence.path=cmpdreg/api/v1 +client.service.cmpdReg.persistence.path=acas/api/v1 client.service.cmpdReg.persistence.fullpath=http://${client.service.cmpdReg.persistence.host}:${client.service.persistence.port}/${client.service.cmpdReg.persistence.path}/ -client.service.cmpdReg.persistence.basepath=http://${client.service.cmpdReg.persistence.host}:${client.service.persistence.port}/cmpdreg +client.service.cmpdReg.persistence.basepath=http://${client.service.cmpdReg.persistence.host}:${client.service.persistence.port}/acas # If project select is shown separately in the Configuration part of the GUI client.cmpdReg.showProjectSelect=true diff --git a/docker-compose.yml b/docker-compose.yml index b0b214547..091a94cb5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,24 +75,24 @@ services: volumes_from: - roo - acas - - cmpdreg + # - cmpdreg env_file: - ./conf/docker/acas/environment/acas.env - ./conf/docker/roo/environment/roo.env - ./conf/docker/cmpdreg/environment/cmpdreg.env command: catalina.sh run roo: - image: mcneilco/acas-roo-server-oss:${ACAS_TAG} + image: test volumes: - /usr/local/tomcat/webapps/acas command: /bin/true - cmpdreg: - image: mcneilco/acas-cmpdreg-roo-server-oss:${ACAS_TAG} - volumes: - - /usr/local/tomcat/webapps/cmpdreg - # Add chemaxon license files here - # - ./chemaxon/licenses/marvin4js-license.cxl:/usr/local/tomcat/webapps/ROOT/marvin4js-license.cxl - # - ./chemaxon/licenses/license.cxl:/root/.chemaxon/license.cxl + # cmpdreg: + # image: test + # volumes: + # - /usr/local/tomcat/webapps/cmpdreg + # # Add chemaxon license files here + # # - ./chemaxon/licenses/marvin4js-license.cxl:/usr/local/tomcat/webapps/ROOT/marvin4js-license.cxl + # # - ./chemaxon/licenses/license.cxl:/root/.chemaxon/license.cxl command: /bin/true volumes: dbstore: diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index 716d197ae..7484bdf0e 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -435,7 +435,7 @@ $(function() { this.model.set({ notebookPage: jQuery.trim(this.$('.notebookPage').val()), synthesisDate: jQuery.trim(this.$('.synthesisDate').val()), - chemist: this.chemistCodeController.getSelectedModel(), + chemist: this.chemistCodeController.getSelectedModel().get("code"), lotNumber: (jQuery.trim(this.$('.lotNumber').val())=='') ? null : parseInt(jQuery.trim(this.$('.lotNumber').val())) diff --git a/modules/CmpdReg/src/client/src/MetaLot.js b/modules/CmpdReg/src/client/src/MetaLot.js index 9db8b498d..27861aae1 100755 --- a/modules/CmpdReg/src/client/src/MetaLot.js +++ b/modules/CmpdReg/src/client/src/MetaLot.js @@ -380,7 +380,7 @@ $(function() { this.lotController.updateModel(); if (this.lotController.model.isNew() && this.user != null) { this.lotController.model.set({ - 'registeredBy': this.user + 'registeredBy': this.user.get("code") }); } this.parentController.updateModel(); @@ -422,7 +422,7 @@ $(function() { if (this.user.get('isAdmin')) { return true; - }else if (!window.configuration.metaLot.disableEditMyLots && (this.user.get('code') == chemist.get('code') || (registeredBy != null && this.user.get('code') == registeredBy.code))) { + }else if (!window.configuration.metaLot.disableEditMyLots && (this.user.get('code') == chemist || (registeredBy != null && this.user.get('code') == registeredBy))) { return true; } else { return false; diff --git a/modules/CmpdReg/src/client/src/Parent.js b/modules/CmpdReg/src/client/src/Parent.js index d0933d724..a738b4108 100755 --- a/modules/CmpdReg/src/client/src/Parent.js +++ b/modules/CmpdReg/src/client/src/Parent.js @@ -32,7 +32,7 @@ $(function() { exactMass: js.exactMass, molFormula: js.molFormula, comment: js.comment, - chemist: new PickList(js.chemist), + chemist: js.chemist, parentAliases: new AliasCollection(js.parentAliases), registrationDate: js.registrationDate, cdId: js.cdId, diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 75e63fd7a..0114eddf2 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -1,12 +1,13 @@ exports.setupAPIRoutes = (app) -> app.post '/api/cmpdReg', exports.postAssignedProperties - app.get '/cmpdReg/scientists', exports.getBasicCmpdReg + app.get '/cmpdReg/scientists', exports.getScientists app.get '/cmpdReg/metalots/corpName/[\\S]*', exports.getMetaLot + app.post '/cmpdReg/metalots', exports.metaLots exports.setupRoutes = (app, loginRoutes) -> app.get '/cmpdReg', loginRoutes.ensureAuthenticated, exports.cmpdRegIndex app.get '/marvin4js-license.cxl', loginRoutes.ensureAuthenticated, exports.getMarvinJSLicense - app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getAPICmpdReg + app.get '/cmpdReg/scientists', loginRoutes.ensureAuthenticated, exports.getScientists app.get '/cmpdReg/parentAliasKinds', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/units', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg app.get '/cmpdReg/solutionUnits', loginRoutes.ensureAuthenticated, exports.getBasicCmpdReg @@ -102,7 +103,7 @@ exports.cmpdRegIndex = (req, res) -> cmpdRegConfig: cmpdRegConfig syncCmpdRegUser = (req, cmpdRegUser) -> - exports.getScientists req, (scientistResponse) -> + exports.getScientistsInternal (scientistResponse) -> foundScientists = JSON.parse scientistResponse if (_.findWhere foundScientists, {code: cmpdRegUser.code})? #update scientist @@ -224,24 +225,14 @@ exports.updateProjects = (jsonBody, callback) -> callback JSON.stringify {error: "something went wrong :("} ) -exports.getScientists = (req, callback) -> - console.log 'in getScientists' - cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + "/scientists" - request( - method: 'GET' - url: cmpdRegCall - json: true - , (error, response, json)=> - if !error - console.log JSON.stringify json - callback JSON.stringify json - else - console.log 'got ajax error trying to get CmpdReg scientists' - console.log error - console.log json - console.log response - callback JSON.stringify {error: "something went wrong :("} - ) +exports.getScientists = (req, resp) => + exports.getScientistsInternal (authors) -> + resp.json authors + +exports.getScientistsInternal = (callback) -> + loginRoutes = require './loginRoutes.js' + loginRoutes.getAuthorsInternal {}, (statusCode, authors) => + callback authors exports.saveScientists = (jsonBody, callback) -> console.log 'in saveScientists' diff --git a/modules/Login/src/server/routes/loginRoutes.coffee b/modules/Login/src/server/routes/loginRoutes.coffee index 7c4a87299..6c7541bda 100644 --- a/modules/Login/src/server/routes/loginRoutes.coffee +++ b/modules/Login/src/server/routes/loginRoutes.coffee @@ -222,4 +222,14 @@ exports.getAuthors = (req, resp) -> baseEntityServiceTestJSON = require '../src/javascripts/spec/testFixtures/BaseEntityServiceTestJSON.js' resp.end JSON.stringify baseEntityServiceTestJSON.authorsList else - csUtilities.getAuthors req, resp + if csUtilities.getAuthors? + csUtilities.getAuthors req, resp + else + opts = req.query + exports.getAuthorsInternal opts, (statusCode, response) => + resp.json response + +exports.getAuthorsInternal = (opts, callback) -> + csUtilities.getAllAuthors(opts, (statusCode, response) -> + callback statusCode, response + ) \ No newline at end of file diff --git a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee index 11d82342d..893feecd1 100644 --- a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee +++ b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee @@ -240,16 +240,15 @@ exports.validateCloneAndGetTarget = (req, resp) -> psProtocolServiceTestJSON = require "#{ACAS_HOME}/public/javascripts/spec/PrimaryScreen/testFixtures/PrimaryScreenProtocolServiceTestJSON.js" resp.json psProtocolServiceTestJSON.successfulCloneValidation -exports.getAuthors = (req, resp) -> #req passed in as input to be able to filter users by roles +exports.getAllAuthors = (opts, callback) -> config = require "#{ACAS_HOME}/conf/compiled/conf.js" serverUtilityFunctions = require "#{ACAS_HOME}/routes/ServerUtilityFunctions.js" baseurl = config.all.client.service.persistence.fullpath+"authors/codeTable" - - if req.query.roleType? and req.query.roleKind? and req.query.roleName? + if opts.roleType? and opts.roleKind? and opts.roleName? baseurl = config.all.client.service.persistence.fullpath+"authors/findByRoleTypeKindAndName" baseurl += "?roleType=#{req.query.roleType}&roleKind=#{req.query.roleKind}&roleName=#{req.query.roleName}&format=codeTable" - - serverUtilityFunctions.getFromACASServer(baseurl, resp) + serverUtilityFunctions.getFromACASServerInternal baseurl, (statusCode, json) -> + callback statusCode, json exports.getAllAuthorObjectsInternal = (callback) -> config = require "#{ACAS_HOME}/conf/compiled/conf.js" From 9ad3986cff452ae6f64f20bab6de2b19d1408264 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 20 Dec 2018 09:31:50 -0800 Subject: [PATCH 410/576] fixes #209: Adding multiple gpg key-servers for reliability of builds (#541) --- Dockerfile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8b3958f67..5fa365a71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,16 +12,19 @@ RUN \ # node RUN set -ex \ && for key in \ - 9554F04D7259F04124DE6B476D5A82AC7E37093B \ 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ - 0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93 \ FD3A5288F042B6850C66B31F09FE44734EB7990E \ 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ - B9AE9905FFD7803F25714661B63B535A4C206CA9 \ C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + B9AE9905FFD7803F25714661B63B535A4C206CA9 \ + 56730D5401028683275BD23C23EFEFE93C4CFFFE \ + 77984A986EBC2AA786BC0F66B01FBB92821C587A \ + 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \ ; do \ - gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \ + gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ + gpg --keyserver keyserver.pgp.com --recv-keys "$key"; \ done ENV NPM_CONFIG_LOGLEVEL warn From 28ff907568473aca66e4a9d7c8210b7d6c028984 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 20 Dec 2018 09:50:13 -0800 Subject: [PATCH 411/576] fixes 542: install gulp 4 from tagged npm version, fix issue with watch for npm install (#543) --- Dockerfile | 2 +- gulpfile.coffee | 5 ++++- package.json | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5fa365a71..07645b23d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,7 @@ ENV ACAS_BASE /home/runner/acas ENV ACAS_CUSTOM /home/runner/acas_custom ENV ACAS_SHARED /home/runner/acas_shared ENV APACHE Redhat -RUN npm install -g gulpjs/gulp.git#4.0 forever nodemon mocha coffee-script +RUN npm install -g gulp@4.0.0 forever nodemon mocha coffeescript COPY package.json $ACAS_BASE/package.json RUN chown -R runner:runner $ACAS_BASE USER runner diff --git a/gulpfile.coffee b/gulpfile.coffee index 53246ca97..85c7cbc79 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -218,6 +218,9 @@ taskConfigs = command: 'npm' args: [ 'install' ] options: _.extend _.clone(globalExecuteOptions), cwd: build + src: [ + build + '/package.json' + ] , taskName: "prepare_config_files" command: 'node' @@ -397,7 +400,7 @@ createExecuteTask = (options) => return command.on 'exit', (code) -> cb code - unless watch == false + unless watch == false || !options.src? watchTaskName = "watch:#{taskName}" watchOptions = watch?.options ? {} gulp.task watchTaskName, -> diff --git a/package.json b/package.json index ca031238e..80d6d619c 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "devDependencies": { "coffee-script": "^1.12.4", "del": "^2.2.2", - "gulp": "github:gulpjs/gulp#4.0", + "gulp": "^4.0.0", "gulp-coffee": "^2.3.3", "gulp-coffeeify": "^0.1.8", "gulp-copy": "^1.0.0", From 1f8f14e6f4c481c4e49fa9feee28c00044028936 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 20 Dec 2018 09:50:23 -0800 Subject: [PATCH 412/576] fixes #544: A bug caused by (a6f202f) was comparing the code name with id, this reverts that bug (#545) --- modules/GenericDataParser/src/server/generic_data_parser.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 2a625a20d..7c368cfa6 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -1782,7 +1782,7 @@ getExperimentByNameCheck <- function(experimentName, protocol, configList, dupli protocolOfExperiment <- getProtocolByCodeName(experimentList[[1]]$protocol$codeName) - if (is.na(protocol) || protocolOfExperiment$id != protocol$codeName) { + if (is.na(protocol) || protocolOfExperiment$id != protocol$id) { if (duplicateNamesAllowed) { experiment <- NA } else { From 9213ec938f9672ce9472adecc0f328b268667601 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 20 Dec 2018 12:26:09 -0800 Subject: [PATCH 413/576] fixes #546 #547: allow case mismatch in image file names and SEL matching, instead of error warn user if an image is uploaded without a cooresponding SEL line --- .../src/server/generic_data_parser.R | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 7c368cfa6..11d27f840 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -980,20 +980,22 @@ validateUploadedImages <- function(imageLocation, listedImageFiles, experimentFo uploadedImageFiles <- list.files(imageLocation) # Make sure all elements are part of both vectors. - # We allow the same file to be listed multiple times -- setdiff disregards duplicates (and you can't + # We allow the same file to be listed multiple times -- %in% disregards duplicates (and you can't # put the same file into a directory twice, so we don't have a problem there) - notUploaded <- setdiff(listedImageFiles, uploadedImageFiles) - notListed <- setdiff(uploadedImageFiles, listedImageFiles) + listedImageFilesLower <- tolower(listedImageFiles) + uploadedImageFilesLower <- tolower(uploadedImageFiles) + notUploaded <- listedImageFiles[!listedImageFilesLower %in% uploadedImageFilesLower] + notListed <- uploadedImageFiles[!uploadedImageFilesLower %in% listedImageFilesLower] if (length(notListed) > 0) { unlink(experimentFolderLocation, recursive = TRUE) - stopUser(paste0("The following files were uploaded in a zip file, but were not listed in the spreadsheet: ", - paste(notListed, collapse = ", "), ". If in doubt, please check your capitalization.")) + warnUser(paste0("The following files were uploaded in a zip file, but were not listed in the spreadsheet: ", + paste(notListed, collapse = ", "))) } if (length(notUploaded) > 0) { unlink(experimentFolderLocation, recursive = TRUE) stopUser(paste0("The following files were listed in the spreadsheet, but were not uploaded in a zip file: ", - paste(notUploaded, collapse = ", "), ". If in doubt, please check your capitalization.")) + paste(notUploaded, collapse = ", "))) } return(TRUE) From 60062c88f4e212d7395882a8106bb9bc99b5ea39 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 26 Dec 2018 11:57:15 -0800 Subject: [PATCH 414/576] setting flyway path for oss --- conf/docker/acas/environment/acas.env | 2 +- conf/docker/roo/environment/roo.env | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/docker/acas/environment/acas.env b/conf/docker/acas/environment/acas.env index 70f8f5848..22fbc845e 100644 --- a/conf/docker/acas/environment/acas.env +++ b/conf/docker/acas/environment/acas.env @@ -5,4 +5,4 @@ ACAS_DB_NAME=acas ACAS_SCHEMA=acas ACAS_USERNAME=acas ACAS_PASSWORD=acas -ACAS_FLYWAY_LOCATION=com.labsynch.labseer.db.migration.postgres,db/migration/postgres +ACAS_FLYWAY_LOCATION=com.labsynch.labseer.db.migration.postgres,db/migration/postgres,db/migration/indigo/postgres diff --git a/conf/docker/roo/environment/roo.env b/conf/docker/roo/environment/roo.env index e4d4a5d15..f81c10cbc 100644 --- a/conf/docker/roo/environment/roo.env +++ b/conf/docker/roo/environment/roo.env @@ -3,5 +3,4 @@ DATABASE_DRIVER=org.postgresql.Driver DATABASE_URL=jdbc:postgresql://db:5432/${ACAS_DB_NAME} VALIDATION_QUERY=select version() CATALINA_OPTS="-Xms512M -Xmx1536M -XX:MaxPermSize=512m" -ACAS_FLYWAY_LOCATION=com.labsynch.labseer.db.migration.postgres,db/migration/postgres From ed4f427129a5860a3098323e5b1c68d5243116bc Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 26 Dec 2018 12:47:58 -0800 Subject: [PATCH 415/576] setting flyway location in acas configs --- conf/config.properties.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index ecd989330..1621bc157 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -538,7 +538,7 @@ server.curveRender.drawPointsForRejectedCurve=true # Restrict experiment search by project server.service.projects.restrictExperiments=false -flyway.location=db/migration/postgres,com.labsynch.labseer.db.migration.postgres +flyway.location=db/migration/postgres,com.labsynch.labseer.db.migration.postgres,db/migration/indigo/postgres flyway.schema=acas flyway.database.username=${server.database.username} flyway.database.password=${server.database.password} From fb94206f7db1a44c21847707b3a5e7ce5dbb709e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 26 Dec 2018 14:54:36 -0800 Subject: [PATCH 416/576] send project code only when registering a lot --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index 7484bdf0e..7c4419108 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -458,7 +458,7 @@ $(function() { purityOperator: null, amountUnits: null, purityMeasuredBy: null, - project: this.projectCodeController.getSelectedModel(), + project: this.projectCodeController.getSelectedModel().get("code"), supplierLot: '', meltingPoint: null, boilingPoint: null, @@ -536,7 +536,7 @@ $(function() { tareWeightUnits: tareWeightUnits, totalAmountStoredUnits: totalAmountStoredUnits, purityMeasuredBy: purityMeasuredBy, - project: this.projectCodeController.getSelectedModel(), + project: this.projectCodeController.getSelectedModel().get("code"), vendor: vendor, supplierLot: jQuery.trim(this.$('.supplierLot').val()), meltingPoint: From 0fa620206b3cad8a86dd500b3e60ef297de7b37a Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 26 Dec 2018 16:45:52 -0800 Subject: [PATCH 417/576] fixing issue due to an easter egg built into search --- modules/CmpdReg/src/client/src/SearchForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/src/SearchForm.js b/modules/CmpdReg/src/client/src/SearchForm.js index cf1ca9801..c68d15e02 100755 --- a/modules/CmpdReg/src/client/src/SearchForm.js +++ b/modules/CmpdReg/src/client/src/SearchForm.js @@ -7,7 +7,7 @@ $(function() { _.each(attributes, function(att, key) { if( (att != '' && att!=null) && key!='aliasContSelect' && key!='searchType' && key!='percentSimilarity' && key!='maxResults' && key!='loggedInUser' ) { if( key=='chemist') { - if( att.get('code')!='anyone') { + if( att!='anyone') { allEmpty = false; } } else { @@ -187,7 +187,7 @@ $(function() { percentSimilarity: (jQuery.trim(this.$('.percentSimilarity').val())=='') ? null : parseFloat(jQuery.trim(this.$('.percentSimilarity').val())), - chemist: this.chemistCodeController.getSelectedModel(), + chemist: this.chemistCodeController.getSelectedModel().get("code"), maxResults: (jQuery.trim(this.$('.maxResults').val())=='') ? null : parseFloat(jQuery.trim(this.$('.maxResults').val())), From d61e351b093ae3917958f858631f98d8a32639d4 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 28 Dec 2018 15:43:16 -0800 Subject: [PATCH 418/576] call validation service to validate the submitted chemists --- .../routes/CmpdRegBulkLoaderRoutes.coffee | 86 ++++++++++++++----- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee index 9eafb358e..d76c48f8b 100644 --- a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee +++ b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee @@ -3,6 +3,7 @@ path = require 'path' exports.setupAPIRoutes = (app) -> app.post '/api/cmpdRegBulkLoader', exports.postAssignedProperties app.post '/api/cmpdRegBulkLoader/registerCmpds', exports.registerCmpds + app.post '/api/cmpdRegBulkLoader/validationProperties', exports.validationProperties app.get '/api/cmpdRegBulkLoader/getFilesToPurge', exports.getFilesToPurge app.post '/api/cmpdRegBulkLoader/purgeFile', exports.purgeFile @@ -126,12 +127,41 @@ exports.saveTemplate = (req, resp) -> resp.end JSON.stringify "Error" ) +exports.validationProperties = (req, resp) -> + req.connection.setTimeout 6000000 + exports.validationPropertiesInternal req.body, (json) => + resp.json json + +exports.validationPropertiesInternal = (reqObject, callback) -> + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.cmpdReg.persistence.fullpath+"bulkload/validationProperties" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: reqObject + json: true + , (error, response, json) => + console.log json + if !error && response.statusCode == 200 + callback json + else + console.error 'got ajax error trying to validate sdf properties' + console.error error + console.error json + console.error response + callback {error: json} + ) + +exports.getScientistsInternal = (callback) -> + loginRoutes = require './loginRoutes.js' + loginRoutes.getAuthorsInternal {}, (statusCode, authors) => + callback authors + exports.registerCmpds = (req, resp) -> req.connection.setTimeout 6000000 createSummaryZip = (fileName, json) -> #remove .sdf from fileName - console.log "fileName" - console.log fileName fileName = fileName.substring(0, fileName.length-4) zipFileName = fileName+".zip" fs = require 'fs' @@ -166,28 +196,38 @@ exports.registerCmpds = (req, resp) -> else fileName = req.body.fileName delete req.body.fileName - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.cmpdReg.persistence.fullpath+"bulkload/registerSdf" - request = require 'request' - request( - method: 'POST' - url: baseurl - body: req.body - json: true - , (error, response, json) => - console.log json - if !error && response.statusCode == 200 && json.reportFiles? - createSummaryZip fileName, json - else - console.log 'got ajax error trying to register compounds' - console.log error - console.log json - console.log response - if json.summary? - resp.json [json] + exports.getScientistsInternal (authors) => + _ = require 'underscore' + + authorCodes = _.pluck authors, "code" + + exports.validationPropertiesInternal req.body, (sdfProperties) => + missingAuthorCodes = _.difference sdfProperties.chemists, authorCodes + if missingAuthorCodes.length > 0 + resp.json [{summary: "Some chemists have not been registered please make sure they are registered and try again: #{JSON.stringify(missingAuthorCodes)}"}] + return else - resp.end JSON.stringify "Error" - ) + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.cmpdReg.persistence.fullpath+"bulkload/registerSdf" + request = require 'request' + request( + method: 'POST' + url: baseurl + body: req.body + json: true + , (error, response, json) => + if !error && response.statusCode == 200 && json.reportFiles? + createSummaryZip fileName, json + else + console.log 'got ajax error trying to register compounds' + console.log error + console.log json + console.log response + if json.summary? + resp.json [json] + else + resp.end JSON.stringify "Error" + ) moveSdfFile = (req, resp, callback) -> fileName = req.body.fileName From 81fd608b68b6a0437da1493e8b0b647a9ea90186 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 2 Jan 2019 18:19:35 -0800 Subject: [PATCH 419/576] add route/function to get users allowed projects using customer specific server functions, pass the list of allowed projects to search routes for experiments and protocols --- .../src/server/routes/AuthorRoutes.coffee | 37 +++++++++++++++++++ .../routes/ExperimentServiceRoutes.coffee | 14 ++++--- .../routes/ProtocolServiceRoutes.coffee | 16 +++++--- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 3ef80654c..65f8bdcdf 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -16,6 +16,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/author', loginRoutes.ensureAuthenticated, exports.saveAuthor app.put '/api/author/:id', loginRoutes.ensureAuthenticated, exports.updateAuthor app.get '/activateUser', exports.activateUserAndRedirectToChangePassword + app.get '/api/allowedProjects', loginRoutes.ensureAuthenticated, exports.allowedProjects serverUtilityFunctions = require './ServerUtilityFunctions.js' _ = require 'underscore' @@ -39,6 +40,42 @@ FirstLsThingItxList = serverUtilityFunctions.FirstLsThingItxList SecondLsThingItxList = serverUtilityFunctions.SecondLsThingItxList +exports.allowedProjects = (req, resp) -> + exports.allowedProjectsInternal req.user, (statusCode, json) -> + resp.statusCode = statusCode + resp.json json + +exports.allowedProjectsInternal = (user, callback) -> + csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' + csUtilities.getProjectStubsInternal (statusCode, allProjects) -> + config = require '../conf/compiled/conf.js' + _ = require 'underscore' + if (config.all.server.project.roles.enable) + filteredProjects = [] + isAdmin = false; + roles = user.roles + allowedProjectCodes = [] + user.roles.forEach (role) -> + if role.roleEntry.lsType != null && role.roleEntry.lsType == "Project" + allowedProjectCodes.push role.lsKind + else if role.roleEntry.roleName == config.all.client.roles.acas.adminRole + isAdmin = true + if isAdmin + filteredProjects = allProjects + else + allProjects.forEach (project) -> + isRestricted = project.isRestricted + if isRestricted + if _.contains(allowedProjectCodes, project.code) + filteredProjects.push(project) + else + filteredProjects.push(project) + projects = filteredProjects + else + projects = allProjects + callback 200, projects + + exports.getAuthorByUsername = (req, resp) -> exports.getAuthorByUsernameInternal req.params.username, (json, statusCode) -> resp.statusCode = statusCode diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index 0ebc3af33..d3d1a1470 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -490,11 +490,15 @@ exports.genericExperimentSearch = (req, res) -> username = req.query.username else username = "none" - baseurl = config.all.client.service.persistence.fullpath+"experiments/search?q="+req.params.searchTerm+"&userName="+username - console.log "baseurl" - console.log baseurl - serverUtilityFunctions = require './ServerUtilityFunctions.js' - serverUtilityFunctions.getFromACASServer(baseurl, res) + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> + _ = require "underscore" + allowedProjectCodes = _.pluck(allowedUserProjects, "code") + baseurl = config.all.client.service.persistence.fullpath+"experiments/search?q="+req.params.searchTerm+"&projects=#{allowedProjectCodes.join(',')}" + console.log "baseurl" + console.log baseurl + serverUtilityFunctions = require './ServerUtilityFunctions.js' + serverUtilityFunctions.getFromACASServer(baseurl, res) exports.editExperimentLookupAndRedirect = (req, res) -> if global.specRunnerTestmode diff --git a/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee index fd0ee05e5..ca0927c54 100644 --- a/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee @@ -490,12 +490,16 @@ exports.genericProtocolSearch = (req, res) -> else res.end JSON.stringify [protocolServiceTestJSON.fullSavedProtocol, protocolServiceTestJSON.fullDeletedProtocol] else - config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"protocols/search?q="+req.params.searchTerm+"&userName="+req.user.username - console.log "baseurl" - console.log baseurl - serverUtilityFunctions = require './ServerUtilityFunctions.js' - serverUtilityFunctions.getFromACASServer(baseurl, res) + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> + _ = require "underscore" + allowedProjectCodes = _.pluck(allowedUserProjects, "code") + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"protocols/search?q="+req.params.searchTerm+"&projects=#{allowedProjectCodes.join(',')}" + console.log "baseurl" + console.log baseurl + serverUtilityFunctions = require './ServerUtilityFunctions.js' + serverUtilityFunctions.getFromACASServer(baseurl, res) exports.deleteProtocol = (req, res) -> if global.specRunnerTestmode From c262339c134753627a615f3c53f909e78a45aafb Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 3 Jan 2019 10:48:51 -0800 Subject: [PATCH 420/576] use author routes to get authorized projects --- modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 0114eddf2..cf35d4030 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -140,9 +140,10 @@ exports.getAPICmpdReg = (req, resp) -> req.pipe(request(cmpdRegCall)).pipe(resp) exports.getAuthorizedCmpdRegProjects = (req, resp) -> - exports.getAuthorizedCmpdRegProjectsInternal req, (response) => + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> resp.status "200" - resp.end JSON.stringify response + resp.end JSON.stringify allowedUserProjects exports.getAuthorizedCmpdRegProjectsInternal = (req, callback) -> exports.getACASProjects req, (statusCode, acasProjectsResponse)-> From 84a750947990ccd52be2f40a7de79be5e7e2572b Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sun, 6 Jan 2019 14:48:38 -0800 Subject: [PATCH 421/576] adding call to Boostrap.js to allow for custom setup scripts --- app_api_template.coffee | 1 + modules/ServerAPI/src/server/Bootstrap.coffee | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 modules/ServerAPI/src/server/Bootstrap.coffee diff --git a/app_api_template.coffee b/app_api_template.coffee index 31d143ccd..406a132c4 100644 --- a/app_api_template.coffee +++ b/app_api_template.coffee @@ -49,6 +49,7 @@ startApp = -> httpServer = http.createServer(app).listen(app.get('port'), -> console.log("ACAS API server listening on port " + app.get('port')) + require "./src/javascripts/ServerAPI/Bootstrap.js" ) ###TO_BE_REPLACED_BY_PREPAREMODULEINCLUDES### diff --git a/modules/ServerAPI/src/server/Bootstrap.coffee b/modules/ServerAPI/src/server/Bootstrap.coffee new file mode 100644 index 000000000..aa7ab17aa --- /dev/null +++ b/modules/ServerAPI/src/server/Bootstrap.coffee @@ -0,0 +1,14 @@ +# Do not add content to this file, this is strictly for client usage to allow a script to be executed on start + +# Example install of LD client python library on start +console.log "No custom Bootstrap script provided" +# ACAS_HOME="../../.." +# config = require "#{ACAS_HOME}/conf/compiled/conf.js" +# exec = require('child_process').exec +# command = "pip2.7 install --user #{config.all.client.service.result.viewer.liveDesign.baseUrl}/ldclient.tar.gz" +# console.log "About to call python using command: "+command +# child = exec command, (error, stdout, stderr) -> +# console.log stdout +# if error? +# console.warn "Error installing ld client. This can happen if Live Design was down during ACAS start or otherwise ACAS could not communicate with Live Design. This is just a warning as LD client might already be installed and be the correction version." +# console.error stderr From 1bc9ab9a546ed0521e8130413b057c57b21305de Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sun, 6 Jan 2019 14:50:31 -0800 Subject: [PATCH 422/576] remove unneccessary routes from cmpd reg, reconfiguring internal functions to call author services allowed projects function --- .../src/server/routes/CmpdRegRoutes.coffee | 43 +------------------ .../server/routes/ProjectServiceRoutes.coffee | 13 ++---- .../src/server/routes/AuthorRoutes.coffee | 1 + 3 files changed, 6 insertions(+), 51 deletions(-) diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index cf35d4030..102c3a4ac 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -145,46 +145,6 @@ exports.getAuthorizedCmpdRegProjects = (req, resp) -> resp.status "200" resp.end JSON.stringify allowedUserProjects -exports.getAuthorizedCmpdRegProjectsInternal = (req, callback) -> - exports.getACASProjects req, (statusCode, acasProjectsResponse)-> - acasProjects = acasProjectsResponse - exports.getProjects req, (cmpdRegProjectsResponse)-> - cmpdRegProjects = JSON.parse cmpdRegProjectsResponse - allowedProjectCodes = _.pluck acasProjects, 'code' - allowedProjects = _.filter cmpdRegProjects, (cmpdRegProject) -> - return (cmpdRegProject.code in allowedProjectCodes) - callback allowedProjects - - -exports.getACASProjects = (req, callback) -> - csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' - if !req.user? - req.user = {} - req.user.username = req.params.username - if global.specRunnerTestmode - resp.end JSON.stringify "testMode not implemented" - else - csUtilities.getProjectsInternal req, callback - -exports.getProjects = (req, callback) -> - console.log 'in getProjects' - cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + "/projects" - request( - method: 'GET' - url: cmpdRegCall - json: true - , (error, response, json)=> - if !error - console.log JSON.stringify json - callback JSON.stringify json - else - console.log 'got ajax error trying to get CmpdReg projects' - console.log error - console.log json - console.log response - callback JSON.stringify {error: "something went wrong :("} - ) - exports.saveProjects = (jsonBody, callback) -> console.log 'in saveProjects' @@ -325,7 +285,7 @@ exports.getMetaLot = (req, resp) -> if json?.lot?.project?.code? projectCode = json.lot.project.code if cmpdRegConfig.metaLot.useProjectRolesToRestrictLotDetails - exports.getACASProjects req, (statusCode, acasProjectsForUsers) => + authorRoutes.allowedProjectsInternal req.user, (statusCode, acasProjectsForUsers) => if statusCode != 200 resp.statusCode = statusCode resp.end JSON.stringify acasProjectsForUsers @@ -343,7 +303,6 @@ exports.getMetaLot = (req, resp) -> resp.end JSON.stringify "Could not find lot" else resp.json json - else console.log 'got ajax error trying to get CmpdReg MetaLot' console.log error diff --git a/modules/Components/src/server/routes/ProjectServiceRoutes.coffee b/modules/Components/src/server/routes/ProjectServiceRoutes.coffee index f8234ef92..5c3935107 100644 --- a/modules/Components/src/server/routes/ProjectServiceRoutes.coffee +++ b/modules/Components/src/server/routes/ProjectServiceRoutes.coffee @@ -8,15 +8,10 @@ exports.setupRoutes = (app, loginRoutes) -> app.get '/api/projects/getAllProjects/stubs', loginRoutes.ensureAuthenticated, exports.getProjectStubs exports.getProjects = (req, resp) -> - csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' - if !req.user? - req.user = {} - req.user.username = req.params.username - if global.specRunnerTestmode - projectServiceTestJSON = require '../public/javascripts/spec/testFixtures/projectServiceTestJSON.js' - resp.end JSON.stringify projectServiceTestJSON.projects - else - csUtilities.getProjects req, resp + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> + resp.status "200" + resp.end JSON.stringify allowedUserProjects exports.getProjectStubs = (req, resp) -> csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 65f8bdcdf..f79f2c826 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -73,6 +73,7 @@ exports.allowedProjectsInternal = (user, callback) -> projects = filteredProjects else projects = allProjects + console.error "Authorized projects: #{JSON.stringify(projects)}" callback 200, projects From 667d7cb1bfbfdb331f2e9c32fd10cf1e804c03a7 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 07:57:45 -0800 Subject: [PATCH 423/576] adding ld client install as part of default bootstrap if setting is set to true --- conf/config.properties.example | 1 + modules/ServerAPI/src/server/Bootstrap.coffee | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 1621bc157..8693e7d64 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -285,6 +285,7 @@ client.service.result.viewer.liveDesign.database.username= client.service.result.viewer.liveDesign.database.password= client.service.result.viewer.liveDesign.database.hostname= client.service.result.viewer.liveDesign.database.port= +server.liveDesign.installClientOnStart=false client.service.result.viewer.simpleSAR.baseUrl= client.service.result.viewer.seurat.protocolPrefix=http://${client.service.persistence.host}:9080/seurat/runseurat?cmd=newjob&AssayName= client.service.result.viewer.seurat.experimentPrefix=&AssayProtocol= diff --git a/modules/ServerAPI/src/server/Bootstrap.coffee b/modules/ServerAPI/src/server/Bootstrap.coffee index aa7ab17aa..67e135b8b 100644 --- a/modules/ServerAPI/src/server/Bootstrap.coffee +++ b/modules/ServerAPI/src/server/Bootstrap.coffee @@ -1,11 +1,21 @@ # Do not add content to this file, this is strictly for client usage to allow a script to be executed on start # Example install of LD client python library on start -console.log "No custom Bootstrap script provided" -# ACAS_HOME="../../.." -# config = require "#{ACAS_HOME}/conf/compiled/conf.js" +ACAS_HOME="../../.." +config = require "#{ACAS_HOME}/conf/compiled/conf.js" +if config.all.server.liveDesign.installClientOnStart? && config.all.server.liveDesign.installClientOnStart + exec = require('child_process').exec + command = "pip2.7 install --upgrade --force-reinstall --user #{config.all.client.service.result.viewer.liveDesign.baseUrl}/ldclient.tar.gz" + console.log "About to call python using command: "+command + child = exec command, (error, stdout, stderr) -> + console.log stdout + if error? + console.warn "Error installing ld client. This can happen if Live Design was down during ACAS start or otherwise ACAS could not communicate with Live Design. This is just a warning as LD client might already be installed and be the correction version." + console.error stderr + +# Example install of LD client python library from local location on start # exec = require('child_process').exec -# command = "pip2.7 install --user #{config.all.client.service.result.viewer.liveDesign.baseUrl}/ldclient.tar.gz" +# command = "pip2.7 install --upgrade --force-reinstall --user /tmp/ld_client" # console.log "About to call python using command: "+command # child = exec command, (error, stdout, stderr) -> # console.log stdout From 840c019e2c5cdb6f0bfe738370feed9dc2c43eca Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 07:58:30 -0800 Subject: [PATCH 424/576] removing unnecessary logging --- modules/ServerAPI/src/server/routes/AuthorRoutes.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index f79f2c826..65f8bdcdf 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -73,7 +73,6 @@ exports.allowedProjectsInternal = (user, callback) -> projects = filteredProjects else projects = allProjects - console.error "Authorized projects: #{JSON.stringify(projects)}" callback 200, projects From ab0888b202ab271f1cbfb499c37854b1be5b390b Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 07:59:14 -0800 Subject: [PATCH 425/576] ignore venv and remove commented out code --- .gitignore | 1 + app_template.coffee | 12 ------------ gulpfile.coffee | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index fbc19bffb..a18177bca 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ Dockerfile* docker-compose*.yml build licenses +venv #this is for the soft link in .dockerfile (git ignore .git by default) .git chemaxon diff --git a/app_template.coffee b/app_template.coffee index 182b4d9d2..6f64ba8eb 100644 --- a/app_template.coffee +++ b/app_template.coffee @@ -48,18 +48,6 @@ startApp = -> else passport.use new LocalStrategy csUtilities.loginStrategy -# passport.isAdmin = (req, resp, next) -> -# if req.isAuthenticated() and csUtilities.isUserAdmin(req.user) -# next() -# else -# next new handler.NotAuthorizedError "Sorry, you don't have the right!" -# passport.isAuthenticated = (req, resp, next) -> -# console.log "running passort.isAuthenticated" -# unless req.isAuthenticated() -# next new handler.NotAuthorizedError "Sorry, you don't have the right!" -# else -# next() - loginRoutes = require './routes/loginRoutes' MemoryStore = express.session.MemoryStore; sessionStore = new MemoryStore(); diff --git a/gulpfile.coffee b/gulpfile.coffee index 85c7cbc79..63586d4d6 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -299,7 +299,7 @@ taskConfigs = renameFunction: getRPath , taskName: "python" - src: getGlob('modules/**/src/server/python/**') + src: getGlob('modules/**/src/server/python/**', '!modules/**/src/server/python/**/venv/**') dest: build + '/src/python' options: _.extend _.clone(globalCopyOptions), {} renameFunction: getPythonPath From 095f8c64eba7b1344415e34d3fb89f1cd99d5ff3 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 08:00:54 -0800 Subject: [PATCH 426/576] add a translation layer between ld client and acas customer specific server functions --- .../src/server/python/acas_ldclient/README.md | 4 + .../python/acas_ldclient/acasldclient.py | 189 ++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 modules/ServerAPI/src/server/python/acas_ldclient/README.md create mode 100644 modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/README.md b/modules/ServerAPI/src/server/python/acas_ldclient/README.md new file mode 100644 index 000000000..35a1ae724 --- /dev/null +++ b/modules/ServerAPI/src/server/python/acas_ldclient/README.md @@ -0,0 +1,4 @@ +SERVER_FQDN= +PYTHON=/usr/local/bin/python +virtualenv -p $PYTHON venv && source venv/bin/activate +pip install https://$SERVER_FQDN/livedesign/ldclient.tar.gz \ No newline at end of file diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py new file mode 100644 index 000000000..22643512c --- /dev/null +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -0,0 +1,189 @@ +""" +Interface for fetching data for ACAS API +""" +from ldclient.client import LDClient +import argparse +import json +import os, sys + +def get_parser(): + """ + @return: + A command-line parser with switches and defaults defined. + @rtype: + argparse.ArgumentParser + + """ + # Get an argsparse parser, and populate it + script_usage = "python {} [-t] (optional: none curerntly) [Test(s) to run]".format(os.path.basename(sys.argv[0])) + script_desc = "Run lambda_function tests" + parser = argparse.ArgumentParser( + usage=script_usage, + description=script_desc + ) + parser.add_argument( + '--ldserver', + '-l', + help='Live Design Server', + dest='ld_server', + required=True + ) + parser.add_argument( + '--user', + '-u', + help='LD client user', + dest='username', + required=True + ) + parser.add_argument( + '--password', + '-p', + help='LD client password', + dest='password', + required=True + ) + parser.add_argument( + '--method', + '-m', + help='Method to call', + dest='method', + required=True + ) + parser.add_argument( + '--args', + '-a', + nargs='*', + help='arguments to the method', + dest='args', + required=False + ) + return parser + +def list_groups(client, test): + print test + return client.list_groups() + +def auth_check(client, username, password): + auth_return = client.auth_check(username = username, password = password) + return auth_return + +def ld_group_to_acas_role(group): + # u'LDAP_Auto_ROLE_SEURAT-USERS' + # u'Project_ProjectX_Administrator' + split = group["name"].split('_') + ls_type = split[0] + ls_kind = split[1] + role_name = "_".join(split[2:len(split)]) + acas_role = { + "id": group["id"], + "roleEntry": { + 'id': group["id"], + 'lsType': ls_type, + 'lsKind': ls_kind, + 'lsTypeAndKind': ls_type + "_" + ls_kind, + 'roleDescription': "Original group fetched from Live Design: "+ group["name"], + 'roleName': role_name, + 'version': 0 + } + } + return acas_role + +def ld_user_to_acas_user_code_table(ld_user): + acas_user = { + "code":ld_user["username"], + "id":ld_user["id"], + "ignored":False, + "name":ld_user["username"] + } + return acas_user + +def get_users(client, ls_type = None, ls_kind = None, role_name = None): + ld_users = client.list_users() + if ls_type == None and ls_kind == None and role_name == None: + acas_users = map(ld_user_to_acas_user_code_table, ld_users) + else: + groups = client.list_groups() + roles = map(ld_group_to_acas_role, groups) + role = [r for r in roles if r["roleEntry"]["lsType"] == ls_type and r["roleEntry"]["lsKind"] == ls_kind and r["roleEntry"]["roleName"] == role_name] + if len(role) == 0: + return [] + users = [] + memberships = client.list_memberships() + userDict = {} + for u in ld_users: + userDict[u["id"]] = u + for m in memberships: + if m["group_id"] == role[0]["id"]: + users.append(userDict[m["user_id"]]) + acas_users = map(ld_user_to_acas_user_code_table, users) + return acas_users + +def ld_user_to_acas_user(ld_user, roles): + acas_user = { + 'id': ld_user["id"], + 'username': ld_user["username"], + 'email': ld_user["username"], + 'firstName': ld_user["username"], + 'lastName': ld_user["username"], + 'roles': roles + } + return acas_user + +def get_users_roles(client, users): + usersDict = {} + for u in users: + usersDict[u["id"]] = u + groups = client.list_groups() + roles = map(ld_group_to_acas_role, groups) + roleDict = {} + for r in roles: + roleDict[r["id"]] = r + memberships = client.list_memberships() + for m in memberships: + usersDict[m["user_id"]].append(roleDict[m["group_id"]]) + users = [] + for user in usersDict.iteritems(): + users.append(user) + return users + +def get_user(client, username): + user = client.get_user(username) + permissions = client.list_permissions() + memberships = client.list_memberships() + groups = client.list_groups() + user_memberships = [m for m in memberships if m['user_id'] == user['id']] + user_groups = [next(g for g in groups if g["id"]==m["group_id"]) for m in user_memberships] + roles = map(ld_group_to_acas_role, user_groups) + acas_user = ld_user_to_acas_user(user, roles) + # user = {"id":1,"username":"bob","email":"bob@mcneilco.com","firstName":"Ham","lastName":"Cheese","roles":[{"id":2,"roleEntry":{"id":1,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-USERS autocreated by ACAS","roleName":"ROLE_ACAS-USERS","version":0},"version":0},{"id":1,"roleEntry":{"id":2,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-ADMINS autocreated by ACAS","roleName":"ROLE_ACAS-ADMINS","version":0},"version":0},{"id":4,"roleEntry":{"id":4,"lsKind":"CmpdReg","lsType":"System","lsTypeAndKind":"System_CmpdReg","roleDescription":"ROLE_CMPDREG-ADMINS autocreated by ACAS","roleName":"ROLE_CMPDREG-ADMINS","version":0},"version":0},{"id":3,"roleEntry":{"id":5,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-CROSS-PROJECT-LOADER autocreated by ACAS","roleName":"ROLE_ACAS-CROSS-PROJECT-LOADER","version":0},"version":0},{"id":5,"roleEntry":{"id":3,"lsKind":"CmpdReg","lsType":"System","lsTypeAndKind":"System_CmpdReg","roleDescription":"ROLE_CMPDREG-USERS autocreated by ACAS","roleName":"ROLE_CMPDREG-USERS","version":0},"version":0},{"id":6,"roleEntry":{"id":6,"lsKind":"PROJ-00000001","lsType":"Project","lsTypeAndKind":"Project_PROJ-00000001","roleDescription":"User autocreated by ACAS","roleName":"User","version":0},"version":0}]} + + return acas_user + +def get_projects(client): + ld_projects = client.projects() + projects = map(ld_project_to_acas, ld_projects) + return projects + +def ld_project_to_acas(ld_project): + acas_project = { + 'id': ld_project.id, + 'code': ld_project.alternate_id, + 'alias': ld_project.name, + 'active': True if ld_project.active == "Y" else False, + 'isRestricted': ld_project.restricted, + 'name': ld_project.name + } + return acas_project + +def main(): + parser = get_parser() + args = parser.parse_args() + endpoint = "{0}/api".format(args.ld_server) + client = LDClient(host=endpoint, username=args.username, password=args.password) + method = eval(args.method) + + result = method(client, *args.args) + print json.dumps(result) + +if __name__ == "__main__": + main() From afbb9e5c61a34685a0b03ff69604054e8fa2cea3 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 08:10:55 -0800 Subject: [PATCH 427/576] adding more key servers --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 07645b23d..07061e2cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,10 @@ RUN set -ex \ ; do \ gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \ gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ - gpg --keyserver keyserver.pgp.com --recv-keys "$key"; \ + gpg --keyserver keyserver.pgp.com --recv-keys "$key"; || \ + gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys "$key"; || \ + gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 "$key"; || \ + gpg --keyserver pgp.mit.edu "$key"; \ done ENV NPM_CONFIG_LOGLEVEL warn From 8d22d44de8c11dc330a89b62ffeacdaa27fb5fa8 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 08:18:22 -0800 Subject: [PATCH 428/576] fixing syntax --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 07061e2cd..4401e511a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,9 @@ RUN set -ex \ ; do \ gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \ gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ - gpg --keyserver keyserver.pgp.com --recv-keys "$key"; || \ - gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys "$key"; || \ - gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 "$key"; || \ + gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \ + gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys "$key" || \ + gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 "$key" || \ gpg --keyserver pgp.mit.edu "$key"; \ done From ab7176777d4b8aba73b5d3739246c8edad926066 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 11 Jan 2019 12:54:13 -0800 Subject: [PATCH 429/576] removing flywaylocation from configs --- conf/config.properties.example | 1 - 1 file changed, 1 deletion(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 8693e7d64..e5e5ffb6b 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -539,7 +539,6 @@ server.curveRender.drawPointsForRejectedCurve=true # Restrict experiment search by project server.service.projects.restrictExperiments=false -flyway.location=db/migration/postgres,com.labsynch.labseer.db.migration.postgres,db/migration/indigo/postgres flyway.schema=acas flyway.database.username=${server.database.username} flyway.database.password=${server.database.password} From 4376ca250b6c478003702970ce9bae236cb502b1 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 16 Jan 2019 07:48:15 -0800 Subject: [PATCH 430/576] jchem license route --- modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 102c3a4ac..7b07b9632 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -335,7 +335,7 @@ exports.regSearch = (req, resp) -> ) exports.getMarvinJSLicense = (req, resp) -> - cmpdRegCall = (config.all.client.service.cmpdReg.persistence.basepath).replace '\/cmpdreg', "/" + cmpdRegCall = (config.all.client.service.cmpdReg.persistence.basepath).replace '\/acas', "/" licensePath = cmpdRegCall + 'marvin4js-license.cxl' console.log licensePath req.pipe(request(licensePath)).pipe(resp) From 7e15ea99d7abfe78536c68910ceaece2fe468fb9 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 17 Jan 2019 08:48:40 -0800 Subject: [PATCH 431/576] the file value should match the file name not the text given by the user which may be upper or lower case versions of the file name --- modules/GenericDataParser/src/server/generic_data_parser.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 11d27f840..f81489b23 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -997,8 +997,8 @@ validateUploadedImages <- function(imageLocation, listedImageFiles, experimentFo stopUser(paste0("The following files were listed in the spreadsheet, but were not uploaded in a zip file: ", paste(notUploaded, collapse = ", "))) } - - return(TRUE) + matchedUploadedFileNames <- uploadedImageFiles[match(listedImageFilesLower,uploadedImageFilesLower)] + return(matchedUploadedFileNames) } getExcelColumnFromNumber <- function(number) { @@ -1654,7 +1654,8 @@ addImageFiles <- function(imagesFile, calculatedResults, experiment, dryRun, rec if (!is.null(imagesFile)) { imageLocation <- unzipUploadedImages(imagesFile = racas::getUploadedFilePath(imagesFile), experimentFolderLocation = experimentFolderLocation) listedImageFiles <- calculatedResults[!is.na(calculatedResults$inlineFileValue),]$inlineFileValue - isValid <- validateUploadedImages(imageLocation = imageLocation, listedImageFiles = listedImageFiles, experimentFolderLocation = experimentFolderLocation) + matchedUploadedFileNames <- validateUploadedImages(imageLocation = imageLocation, listedImageFiles = listedImageFiles, experimentFolderLocation = experimentFolderLocation) + calculatedResults[!is.na(calculatedResults$inlineFileValue),]$inlineFileValue <- matchedUploadedFileNames calculatedResults <- addComment(calculatedResults = calculatedResults) if (racas::applicationSettings$server.service.external.file.type == "custom") { fileValueVector <- ifelse(is.na(calculatedResults$inlineFileValue), From 3cfa30b0a972cae1606f7c00e28891e37b35679e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 17 Jan 2019 15:44:15 -0800 Subject: [PATCH 432/576] remove cmpdreg roo container --- docker-compose.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 091a94cb5..48b0440f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,25 +75,16 @@ services: volumes_from: - roo - acas - # - cmpdreg env_file: - ./conf/docker/acas/environment/acas.env - ./conf/docker/roo/environment/roo.env - ./conf/docker/cmpdreg/environment/cmpdreg.env command: catalina.sh run roo: - image: test + image: mcneilco/acas-roo-server-oss:${ACAS_TAG} volumes: - /usr/local/tomcat/webapps/acas command: /bin/true - # cmpdreg: - # image: test - # volumes: - # - /usr/local/tomcat/webapps/cmpdreg - # # Add chemaxon license files here - # # - ./chemaxon/licenses/marvin4js-license.cxl:/usr/local/tomcat/webapps/ROOT/marvin4js-license.cxl - # # - ./chemaxon/licenses/license.cxl:/root/.chemaxon/license.cxl - command: /bin/true volumes: dbstore: filestore: From 6f7c059bb2b0da240385f5cfff93d226b62fef74 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 29 Jan 2019 10:55:49 -0800 Subject: [PATCH 433/576] pass allowed projects to creg search, push postgres default container to one that does not include seurat/compound schema creation --- docker-compose.yml | 3 +- .../src/server/routes/CmpdRegRoutes.coffee | 44 +++++++++++-------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 48b0440f0..151a67aba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: - logs:/home/runner/logs command: ["bin/acas.sh", "run", "rservices"] db: - image: mcneilco/acas-postgres:1.0-bingo + image: mcneilco/acas-postgres:1.1-bingo restart: always volumes: - dbstore:/var/lib/postgresql/data @@ -56,7 +56,6 @@ services: - ./conf/docker/db/environment/db.env - ./conf/docker/acas/environment/acas.env - ./conf/docker/cmpdreg/environment/cmpdreg.env - - ./conf/docker/seurat/environment/seurat.env ports: - "5432:5432" tomcat: diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 7b07b9632..c3a7fdd44 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -236,25 +236,31 @@ exports.updateScientists = (jsonBody, callback) -> ) exports.searchCmpds = (req, resp) -> - cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + '/search/cmpds' - request( - method: 'POST' - url: cmpdRegCall - body: JSON.stringify req.body - json: true - timeout: 6000000 - , (error, response, json) => - if !error - console.log JSON.stringify json - resp.setHeader('Content-Type', 'application/json') - resp.end JSON.stringify json - else - console.log 'got ajax error trying to search for compounds' - console.log error - console.log json - console.log response - resp.end JSON.stringify {error: "something went wrong :("} - ) + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> + _ = require "underscore" + allowedProjectCodes = _.pluck(allowedUserProjects, "code") + req.body.projects = allowedProjectCodes + console.log req.body + cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + '/search/cmpds' + request( + method: 'POST' + url: cmpdRegCall + body: JSON.stringify req.body + json: true + timeout: 6000000 + , (error, response, json) => + if !error + console.log JSON.stringify json + resp.setHeader('Content-Type', 'application/json') + resp.end JSON.stringify json + else + console.log 'got ajax error trying to search for compounds' + console.log error + console.log json + console.log response + resp.end JSON.stringify {error: "something went wrong :("} + ) exports.getStructureImage = (req, resp) -> imagePath = (req.originalUrl).replace /\/cmpdreg\/structureimage/, "" From e8f119a6eda067247ddc13bcbee8755923402d74 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 30 Jan 2019 09:52:20 -0800 Subject: [PATCH 434/576] set modified by to chemist code, pass modified by to lot and parent update services if null in the request --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 1 + .../src/server/routes/CmpdRegRoutes.coffee | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index 7c4419108..2626ba371 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -536,6 +536,7 @@ $(function() { tareWeightUnits: tareWeightUnits, totalAmountStoredUnits: totalAmountStoredUnits, purityMeasuredBy: purityMeasuredBy, + chemist: this.chemistCodeController.getSelectedModel().get("code"), project: this.projectCodeController.getSelectedModel().get("code"), vendor: vendor, supplierLot: jQuery.trim(this.$('.supplierLot').val()), diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index c3a7fdd44..47a015ca4 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -358,6 +358,8 @@ exports.fileSave = (req, resp) -> req.pipe(request[req.method.toLowerCase()](cmpdRegCall)).pipe(resp) exports.metaLots = (req, resp) -> + if req.user? && !req.body.modifiedBy? + req.body.lot.modifiedBy = req.user.username cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + '/metalots' request( method: 'POST' @@ -547,7 +549,8 @@ exports.validateParent = (req, resp) -> exports.updateParent = (req, resp) -> console.log "exports.updateParent" - + if req.user? && !req.body.modifiedBy? + req.body.modifiedBy = req.user.username cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + '/parents/updateParent' request( method: 'POST' @@ -575,7 +578,8 @@ exports.updateLotMetadata = (req, resp) -> console.log 'in update lot metaData' cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + '/parentLot/updateLot/metadata' console.log cmpdRegCall - + if req.user? && !req.body.modifiedBy? + req.body.modifiedBy = req.user.username request( method: 'POST' url: cmpdRegCall @@ -599,7 +603,11 @@ exports.updateLotsMetadata = (req, resp) -> console.log 'in update lot array metaData' cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + '/parentLot/updateLot/metadata/jsonArray' console.log cmpdRegCall - + if req.user? + req.body.map((lot) -> + if !lot.modifiedBy? + lot.modifiedBy = req.user.username + ) request( method: 'POST' url: cmpdRegCall @@ -623,7 +631,8 @@ exports.updateParentMetadata = (req, resp) -> console.log 'in update parent metaData' cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + '/parents/updateParent/metadata' console.log cmpdRegCall - + if req.user? && !req.body.modifiedBy? + req.body.modifiedBy = req.user.username request( method: 'POST' url: cmpdRegCall From 5f4bb2e6d70b87f54d2720fa8ddfc235aa2b9293 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 4 Feb 2019 14:50:39 -0800 Subject: [PATCH 435/576] fixes #556: Replace new line characters in entity reference codes and warn the user of the changed data --- .../src/server/generic_data_parser.R | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index 7c368cfa6..ce00610f0 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -495,6 +495,19 @@ validateCalculatedResults <- function(calculatedResults, dryRun, curveNames, tes # Get the current batch Ids batchesToCheck <- calculatedResults$originalMainID != replaceFakeCorpBatchId + + # The preferred id function strips new line characters from entity reference codes because it + # posts the list as a new line seperated string. The best way to deal with this is to clean up + # the codes beforehand and warn the user of the changed data. + newLineCharacterRegex <- "\n|\r" + batchIdsWithNewLine <- grepl(newLineCharacterRegex,calculatedResults$batchCode) + if(any(batchIdsWithNewLine)) { + calculatedResults$batchCode[batchIdsWithNewLine] <- gsub(newLineCharacterRegex, "", calculatedResults$batchCode[batchIdsWithNewLine]) + warnUser(paste0("The loader found and removed new line characters from ", mainCode, " rows: '", + paste(unique(calculatedResults$batchCode[batchIdsWithNewLine]),collapse="' ,'"), + "'.")) + } + batchIds <- unique(calculatedResults$batchCode[batchesToCheck]) if (inputFormat == "Gene ID Data") { From f29799ba4451d0a03e1f1c8c940203e8714c12fb Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 7 Feb 2019 15:22:13 -0800 Subject: [PATCH 436/576] fix issue with filling in saved chemist, project from saved lot json --- modules/CmpdReg/src/client/custom/Lot_Custom.js | 12 ++++++------ modules/CmpdReg/src/client/src/Lot.js | 10 +++++++++- modules/CmpdReg/src/client/src/MetaLot.js | 7 +++---- modules/CmpdReg/src/client/src/SaltForm.js | 1 + 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/Lot_Custom.js b/modules/CmpdReg/src/client/custom/Lot_Custom.js index 2626ba371..e323e2587 100755 --- a/modules/CmpdReg/src/client/custom/Lot_Custom.js +++ b/modules/CmpdReg/src/client/custom/Lot_Custom.js @@ -56,8 +56,8 @@ $(function() { totalAmountStoredUnits: new PickList(js.totalAmountStoredUnits), purityMeasuredBy: new PickList(js.purityMeasuredBy), purityOperator: new PickList(js.purityOperator), - chemist: new PickList(js.chemist), - project: new PickList(js.project), + chemist: new PickList({selectedCode: js.chemist}), + project: new PickList({selectedCode: js.project}), vendor: new PickList(js.vendor) }) // replace composite object pointers with real objects @@ -435,7 +435,7 @@ $(function() { this.model.set({ notebookPage: jQuery.trim(this.$('.notebookPage').val()), synthesisDate: jQuery.trim(this.$('.synthesisDate').val()), - chemist: this.chemistCodeController.getSelectedModel().get("code"), + chemist: this.chemistCodeController.getSelectedModel(), lotNumber: (jQuery.trim(this.$('.lotNumber').val())=='') ? null : parseInt(jQuery.trim(this.$('.lotNumber').val())) @@ -458,7 +458,7 @@ $(function() { purityOperator: null, amountUnits: null, purityMeasuredBy: null, - project: this.projectCodeController.getSelectedModel().get("code"), + project: this.projectCodeController.getSelectedModel(), supplierLot: '', meltingPoint: null, boilingPoint: null, @@ -536,8 +536,8 @@ $(function() { tareWeightUnits: tareWeightUnits, totalAmountStoredUnits: totalAmountStoredUnits, purityMeasuredBy: purityMeasuredBy, - chemist: this.chemistCodeController.getSelectedModel().get("code"), - project: this.projectCodeController.getSelectedModel().get("code"), + chemist: this.chemistCodeController.getSelectedModel(), + project: this.projectCodeController.getSelectedModel(), vendor: vendor, supplierLot: jQuery.trim(this.$('.supplierLot').val()), meltingPoint: diff --git a/modules/CmpdReg/src/client/src/Lot.js b/modules/CmpdReg/src/client/src/Lot.js index 23a5cd377..609ed617f 100755 --- a/modules/CmpdReg/src/client/src/Lot.js +++ b/modules/CmpdReg/src/client/src/Lot.js @@ -5,6 +5,8 @@ $(function() { getModelForSave: function() { var mts = new Backbone.Model(this.attributes); mts.set({fileList: this.get('fileList').getUploadedFiles()}); + mts.set({chemist: this.get('chemist').get('code')}); + mts.set({project: this.get('project').get('code')}); mts.unset('json'); return mts; @@ -58,7 +60,13 @@ $(function() { setupCodeController: function(elClass, type, attribute) { var tcode = ''; if(this.model.get(attribute)) { - tcode = this.model.get(attribute).get('code'); + if(this.model.get(attribute).get("code")) { + tcode = this.model.get(attribute).get('code'); + } else { + if(this.model.get(attribute).get("selectedCode")) { + tcode = this.model.get(attribute).get("selectedCode"); + } + } } return new PickListSelectController({ el: this.$('.'+elClass), diff --git a/modules/CmpdReg/src/client/src/MetaLot.js b/modules/CmpdReg/src/client/src/MetaLot.js index 27861aae1..2ae999091 100755 --- a/modules/CmpdReg/src/client/src/MetaLot.js +++ b/modules/CmpdReg/src/client/src/MetaLot.js @@ -258,7 +258,7 @@ $(function() { this.saltFormController.updateModel(function () { if (mlself.saltFormController.model.isNew()) { mlself.saltFormController.model.set({ - 'chemist': mlself.lotController.model.get('chemist') + 'chemist': mlself.lotController.model.get('chemist').get("code") }); } @@ -288,7 +288,6 @@ $(function() { message: 'Saving ' + (lisb ? 'batch' : 'lot') + '...' }); mlself.delegateEvents({}); // stop listening to buttons - $.ajax({ type: "POST", url: url, @@ -386,7 +385,7 @@ $(function() { this.parentController.updateModel(); if (this.parentController.model.isNew()) { this.parentController.model.set({ - 'chemist': this.lotController.model.get('chemist') + 'chemist': this.lotController.model.get('chemist').get("code") }); } }, @@ -415,7 +414,7 @@ $(function() { }, allowedToUpdate: function () { - var chemist = this.model.get('lot').get('chemist'); + var chemist = this.model.get('lot').get('chemist').get('selectedCode'); var registeredBy = this.model.get('lot').get('registeredBy'); if (this.user == null || chemist == null || this.model.get('lot').isNew()) return true; // test mode or new diff --git a/modules/CmpdReg/src/client/src/SaltForm.js b/modules/CmpdReg/src/client/src/SaltForm.js index 77c5a6042..5c623528f 100755 --- a/modules/CmpdReg/src/client/src/SaltForm.js +++ b/modules/CmpdReg/src/client/src/SaltForm.js @@ -33,6 +33,7 @@ $(function() { id: js.id, corpName: js.corpName, casNumber: js.casNumber, + chemist: js.chemist, isosalts: isel, molStructure: js.molStructure }); From 738fc64143cf480b600d19deb0f72c81cdc38605 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 7 Feb 2019 15:51:43 -0800 Subject: [PATCH 437/576] remove call to auth check --- .../python/acas_ldclient/acasldclient.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index 22643512c..18f0d2924 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -63,8 +63,13 @@ def list_groups(client, test): print test return client.list_groups() -def auth_check(client, username, password): - auth_return = client.auth_check(username = username, password = password) +def auth_check(endpoint, username, password): + auth_return = {'authorized': True, 'error': None} + try: + sessionClient = LDClient(host=endpoint, username=username, password=password) + except Exception as e: + auth_return['authorized'] = False + auth_return['error'] = e.message return auth_return def ld_group_to_acas_role(group): @@ -75,7 +80,7 @@ def ld_group_to_acas_role(group): ls_kind = split[1] role_name = "_".join(split[2:len(split)]) acas_role = { - "id": group["id"], + "id": group["id"], "roleEntry": { 'id': group["id"], 'lsType': ls_type, @@ -179,10 +184,13 @@ def main(): parser = get_parser() args = parser.parse_args() endpoint = "{0}/api".format(args.ld_server) - client = LDClient(host=endpoint, username=args.username, password=args.password) - method = eval(args.method) + if args.method != "auth_check": + client = LDClient(host=endpoint, username=args.username, password=args.password) + method = eval(args.method) + result = method(client, *args.args) + else: + result = auth_check(endpoint, *args.args) - result = method(client, *args.args) print json.dumps(result) if __name__ == "__main__": From 3d7c8abe78a92a5b701a111266fc99a31030a11b Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 8 Feb 2019 09:42:03 -0800 Subject: [PATCH 438/576] fix error reporting string when service returns fail --- modules/GenericDataParser/src/server/generic_data_parser.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index c26bb0e5d..3be7ddb8e 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -1760,7 +1760,7 @@ getExperimentByNameCheck <- function(experimentName, protocol, configList, dupli tryCatch({ experimentList <- getExperimentsByName(experimentName) }, error = function(e) { - stopUser("There was an error checking if the ",racas::applicationSettings$client.experiment.label," already exists. Please contact your system administrator.") + stopUser(paste0("There was an error checking if the ",racas::applicationSettings$client.experiment.label," already exists. Please contact your system administrator.")) }) # Warn the user if the experiment already exists (the else block) @@ -1785,7 +1785,7 @@ getExperimentByNameCheck <- function(experimentName, protocol, configList, dupli } } }, error = function(e) { - stopUser("There was an error checking if the experiment is in the correct ",racas::applicationSettings$client.protocol.label,". Please contact your system administrator.") + stopUser(paste0("There was an error checking if the experiment is in the correct ",racas::applicationSettings$client.protocol.label,". Please contact your system administrator.")) }) # Finish if the previous experiment was part of a deleted protocol, we can just delete and reload if (experimentList[[1]]$protocol$ignored) { @@ -2133,7 +2133,7 @@ validateProject <- function(projectName, configList, username, protocolName = NU tryCatch({ protocolList <- getProtocolsByName(protocolName) }, error = function(e) { - stopUser("There was an error in accessing the ",racas::applicationSettings$client.protocol.label,". Please contact your system administrator.") + stopUser(paste0("There was an error in accessing the ",racas::applicationSettings$client.protocol.label,". Please contact your system administrator.")) }) if (length(protocolList) !=0) { protocol <- getProtocolByCodeName(protocolList[[1]]$codeName) From 0cf3844248630fb6f18dd4fdd0838f6b3f7b1966 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 11 Feb 2019 12:19:00 -0800 Subject: [PATCH 439/576] fixes #558: Additional codes in scientist picklists --- modules/CmpdReg/conf/CmpdRegConfJSON.coffee | 9 +++++++++ .../src/server/routes/CmpdRegRoutes.coffee | 2 +- .../routes/CmpdRegBulkLoaderRoutes.coffee | 2 +- .../src/server/generic_data_parser.R | 17 +++++++++-------- modules/ServerAPI/conf/ProtocolConfJSON.coffee | 8 ++++---- modules/ServerAPI/src/client/BaseEntity.coffee | 2 +- .../CustomerSpecificServerFunctions.coffee | 10 +++++++++- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/modules/CmpdReg/conf/CmpdRegConfJSON.coffee b/modules/CmpdReg/conf/CmpdRegConfJSON.coffee index e71bb94f4..313db5931 100644 --- a/modules/CmpdReg/conf/CmpdRegConfJSON.coffee +++ b/modules/CmpdReg/conf/CmpdRegConfJSON.coffee @@ -21,5 +21,14 @@ typeName: "id" kindName: "corpName" ] + ddicttypes: + [ + typeName: "compound" + ] + ddictkinds: + [ + typeName: "compound" + kindName: "scientist" + ] ) (if (typeof process is "undefined" or not process.versions) then window.cmpdRegConfJSON = window.cmpdRegConfJSON or {} else exports) diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 47a015ca4..7280ffb75 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -192,7 +192,7 @@ exports.getScientists = (req, resp) => exports.getScientistsInternal = (callback) -> loginRoutes = require './loginRoutes.js' - loginRoutes.getAuthorsInternal {}, (statusCode, authors) => + loginRoutes.getAuthorsInternal {additionalCodeType: 'compound', additionalCodeKind: 'scientist'}, (statusCode, authors) => callback authors exports.saveScientists = (jsonBody, callback) -> diff --git a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee index d76c48f8b..541445c78 100644 --- a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee +++ b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee @@ -155,7 +155,7 @@ exports.validationPropertiesInternal = (reqObject, callback) -> exports.getScientistsInternal = (callback) -> loginRoutes = require './loginRoutes.js' - loginRoutes.getAuthorsInternal {}, (statusCode, authors) => + loginRoutes.getAuthorsInternal {additionalCodeType: 'compound', additionalCodeKind: 'scientist'}, (statusCode, authors) => callback authors exports.registerCmpds = (req, resp) -> diff --git a/modules/GenericDataParser/src/server/generic_data_parser.R b/modules/GenericDataParser/src/server/generic_data_parser.R index c072cc01b..e57c9e41b 100644 --- a/modules/GenericDataParser/src/server/generic_data_parser.R +++ b/modules/GenericDataParser/src/server/generic_data_parser.R @@ -2224,7 +2224,7 @@ validateScientist <- function(scientistName, configList, testMode = FALSE) { if (!testMode) { response <- tryCatch({ - getURL(URLencode(paste0(racas::applicationSettings$server.nodeapi.path, racas::applicationSettings$client.service.users.path, "/", scientistName))) + getURL(URLencode(paste0(racas::applicationSettings$server.nodeapi.path, "/api/authors?additionalCodeType=assay&additionalCodeKind=scientist"))) }, error = function(e) { addError( paste("There was an error in validating the scientist's name:", scientistName)) return("") @@ -2238,14 +2238,15 @@ validateScientist <- function(scientistName, configList, testMode = FALSE) { response <- toJSON(list(username = scientistName)) } } - - if (response == "") { - addError( paste0("The Scientist you supplied, '", scientistName, "', is not a valid name. Please enter the scientist's login name.")) - return("") - } - + username <- tryCatch({ - fromJSON(response)$username + response <- jsonlite::fromJSON(response) + if (nrow(response) == 0 || nrow(response[response$code == scientistName, ]) == 0) { + addError( paste0("The Scientist you supplied, '", scientistName, "', is not a valid name. Please enter the scientist's name.")) + return("") + } else { + return(scientistName) + } }, error = function(e) { addError( paste("There was an error in validating the scientist's name:", scientistName)) return("") diff --git a/modules/ServerAPI/conf/ProtocolConfJSON.coffee b/modules/ServerAPI/conf/ProtocolConfJSON.coffee index d5a5764e1..2dc0c277b 100644 --- a/modules/ServerAPI/conf/ProtocolConfJSON.coffee +++ b/modules/ServerAPI/conf/ProtocolConfJSON.coffee @@ -470,11 +470,11 @@ name: "None" ignored: false , - codeType: "model fit" - codeKind: "transformation units" + codeType: "compound" + codeKind: "scientist" codeOrigin: "ACAS DDICT" - code: "%" - name: "%" + code: "Pharmaron" + name: "Pharmaron" ignored: false ] diff --git a/modules/ServerAPI/src/client/BaseEntity.coffee b/modules/ServerAPI/src/client/BaseEntity.coffee index 7f983e3c7..980bdf02a 100644 --- a/modules/ServerAPI/src/client/BaseEntity.coffee +++ b/modules/ServerAPI/src/client/BaseEntity.coffee @@ -441,7 +441,7 @@ class window.BaseEntityController extends AbstractThingFormController #TODO: che setupScientistSelect: -> @scientistList = new PickListList() - @scientistList.url = "/api/authors" + @scientistList.url = "/api/authors?additionalCodeType=assay&additionalCodeKind=scientist" @scientistListController = new PickListSelectController el: @$('.bv_scientist') collection: @scientistList diff --git a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee index 893feecd1..6f0f54362 100644 --- a/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee +++ b/modules/ServerAPI/src/server/CustomerSpecificServerFunctions.coffee @@ -248,7 +248,15 @@ exports.getAllAuthors = (opts, callback) -> baseurl = config.all.client.service.persistence.fullpath+"authors/findByRoleTypeKindAndName" baseurl += "?roleType=#{req.query.roleType}&roleKind=#{req.query.roleKind}&roleName=#{req.query.roleName}&format=codeTable" serverUtilityFunctions.getFromACASServerInternal baseurl, (statusCode, json) -> - callback statusCode, json + # If additional codeType and codeKind parameters are supplied then append the code values for the additional authors + # This is was added for the purpose of allowing additional non-authors to show up in picklists throughout ACAS and Creg + if opts.additionalCodeType? and opts.additionalCodeKind? + codeTableServiceRoutes = require "#{ACAS_HOME}/routes/CodeTableServiceRoutes.js" + codeTableServiceRoutes.getCodeTableValuesInternal opts.additionalCodeType, opts.additionalCodeKind, (codes) -> + Array::push.apply json, codes + callback statusCode, json + else + callback statusCode, json exports.getAllAuthorObjectsInternal = (callback) -> config = require "#{ACAS_HOME}/conf/compiled/conf.js" From 0c944ab1cba70489ff55ce61b84873c344dddc4e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 11 Feb 2019 12:55:43 -0800 Subject: [PATCH 440/576] removing mistaken file checkin changes --- modules/ServerAPI/conf/ProtocolConfJSON.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ServerAPI/conf/ProtocolConfJSON.coffee b/modules/ServerAPI/conf/ProtocolConfJSON.coffee index 2dc0c277b..d5a5764e1 100644 --- a/modules/ServerAPI/conf/ProtocolConfJSON.coffee +++ b/modules/ServerAPI/conf/ProtocolConfJSON.coffee @@ -470,11 +470,11 @@ name: "None" ignored: false , - codeType: "compound" - codeKind: "scientist" + codeType: "model fit" + codeKind: "transformation units" codeOrigin: "ACAS DDICT" - code: "Pharmaron" - name: "Pharmaron" + code: "%" + name: "%" ignored: false ] From 5674eabb55bff9947a223ef258d21e75fe73a44f Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 27 Feb 2019 16:46:06 -0800 Subject: [PATCH 441/576] fixes #562: dont sync to compound project any longer --- .../server/routes/SyncProjectsUsers.coffee | 71 +++---------------- 1 file changed, 9 insertions(+), 62 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/SyncProjectsUsers.coffee b/modules/ServerAPI/src/server/routes/SyncProjectsUsers.coffee index b197bab4e..5ffe3157b 100644 --- a/modules/ServerAPI/src/server/routes/SyncProjectsUsers.coffee +++ b/modules/ServerAPI/src/server/routes/SyncProjectsUsers.coffee @@ -14,16 +14,15 @@ exports.syncLiveDesignProjectsUsers = (req, resp) -> exports.syncLiveDesignProjects caughtErrors, pythonErrors, configJSON, projectsJSON, (caughtErrors, pythonErrors) -> console.debug caughtErrors console.debug pythonErrors - exports.syncCmpdRegProjects req, acasGroupsAndProjects, caughtErrors, (syncCmpdRegProjectsCallback, caughtErrors) -> - exports.syncLiveDesignRoles caughtErrors, pythonErrors, configJSON, groupsJSON, (caughtErrors, pythonErrors) -> - if !caughtErrors - resp.statusCode = 200 - console.log "Successfully synced projects and permissions with LiveDesign and Compound Reg" - resp.end "Successfully synced projects and permissions with LiveDesign and Compound Reg" - else - resp.statusCode = 500 - console.log "An error has occurred trying to sync projects and permissions with LiveDesign and Compound Reg. Please contact an administrator." - resp.end "An error has occurred trying to sync projects and permissions with LiveDesign and Compound Reg. Please contact an administrator." + exports.syncLiveDesignRoles caughtErrors, pythonErrors, configJSON, groupsJSON, (caughtErrors, pythonErrors) -> + if !caughtErrors + resp.statusCode = 200 + console.log "Successfully synced projects and permissions with LiveDesign" + resp.end "Successfully synced projects and permissions with LiveDesign" + else + resp.statusCode = 500 + console.log "An error has occurred trying to sync projects and permissions with LiveDesign. Please contact an administrator." + resp.end "An error has occurred trying to sync projects and permissions with LiveDesign. Please contact an administrator." exports.getGroupsJSON = (callback) -> request = require 'request' @@ -114,58 +113,6 @@ exports.syncLiveDesignProjects = (caughtErrors, pythonErrors, configJSON, projec console.warn "some live design configs are null, skipping sync of live design projects" callback caughtErrors, pythonErrors -exports.syncCmpdRegProjects = (req, acasGroupsAndProjects, caughtErrors, callback) -> - config = require '../conf/compiled/conf.js' - if config.all.server.project?.sync?.cmpdReg? && config.all.server.project.sync.cmpdReg - cmpdRegRoutes = require '../routes/CmpdRegRoutes.js' - _ = require "underscore" - #Get or create projects in CmpdReg - projectCodes = _.pluck acasGroupsAndProjects.projects, 'code' - console.debug 'project codes are:' + JSON.stringify projectCodes - cmpdRegRoutes.getProjects req, (projectResponse) -> - if (projectResponse.indexOf '') > -1 - console.error "Caught error getting CmpdReg projects" - console.error projectResponse - caughtErrors = true - callback 'ERROR', caughtErrors - foundProjects = JSON.parse projectResponse - foundProjectCodes = _.pluck foundProjects, 'code' - console.debug 'found projects are: '+foundProjectCodes - newProjectCodes = _.difference projectCodes, foundProjectCodes - newProjects = _.filter acasGroupsAndProjects.projects, (project) -> - return project.code in newProjectCodes - projectsToUpdate = _.filter acasGroupsAndProjects.projects, (project) -> - found = (_.findWhere foundProjects, {code: project.code})? - unchanged = (_.findWhere foundProjects, {code: project.code, name: project.name})? - return (found and !unchanged) - if (newProjects? and newProjects.length > 0) or (projectsToUpdate? and projectsToUpdate.length > 0) - if (newProjects? and newProjects.length > 0) - console.debug 'saving new projects with JSON: '+ JSON.stringify newProjects - cmpdRegRoutes.saveProjects newProjects, (saveProjectsResponse) -> - if (saveProjectsResponse.indexOf '') > -1 - console.error "Caught error saving CmpdReg projects" - console.error projectResponse - caughtErrors = true - callback 'ERROR', caughtErrors - else - for projectToUpdate in projectsToUpdate - oldProject = _.findWhere foundProjects, {code: projectToUpdate.code} - projectToUpdate.id = oldProject.id - projectToUpdate.version = oldProject.version - console.debug 'updating projects with JSON: '+ JSON.stringify projectsToUpdate - cmpdRegRoutes.updateProjects projectsToUpdate, (updateProjectsResponse) -> - if (updateProjectsResponse.indexOf '') > -1 - console.error "Caught error updating CmpdReg projects" - console.error updateProjectsResponse - caughtErrors = true - callback 'ERROR', caughtErrors - else - console.debug 'CmpdReg projects are up-to-date' - callback 'CmpdReg projects are up-to-date', caughtErrors - else - console.warn "config server.project.sync.cmpdReg is not set to true, skipping sync of CmpdReg projects" - callback 'Skipped sync of CmpdReg projects', caughtErrors - exports.validateLiveDesignConfigs = (configJSON) -> _ = require "underscore" countNull = _.map configJSON, (configs) -> From 810228379923145edd155a190e0dd61cf9d1d25c Mon Sep 17 00:00:00 2001 From: keng Date: Thu, 16 May 2019 15:50:27 -0700 Subject: [PATCH 442/576] Updated README, added shell script for creating initial user for local development environment --- README.md | 26 ++++++++++++++++++++++---- docker_bob_setup.sh | 7 +++++++ 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100755 docker_bob_setup.sh diff --git a/README.md b/README.md index 5b94fb228..9fca2fcf7 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ cd acas-cmpdreg-roo-server * Login and/or Sign-Up * Click Download > JChem Suite > JChem * Scroll down to Archives and select 16.4.25.0, click Get Archive -* Download jchem-merged-lib-16.4.25.0.zip +* Download [jchem-merged-lib-16.4.25.0.zip](https://chemaxon.com/download?dl=%2Fdata%2Fdownload%2Fjchem%2F16.4.25.0%2Fjchem-merged-lib-16.4.25.0.zip) * Unzip it and rename jchem.jar to jchem-16.4.25.0.jar ##### Add chemaxon jar file to a lib folder in the checkout @@ -157,17 +157,29 @@ docker-compose up -d ``` #### Login + +Visit `http://localhost:3000` in your browser to login. You will need to create a user in order to access the web app: + +```bash +curl localhost:3001/api/systemTest/getOrCreateACASBob +curl localhost:3001/api/systemTest/getOrCreateGlobalProject +curl localhost:3001/api/systemTest/getOrCreateGlobalProjectRole +curl localhost:3001/api/systemTest/giveBobRoles +curl localhost:3001/api/systemTest/getOrCreateCmpdRegBob +curl localhost:3001/api/systemTest/syncRoles ``` -http://localhost:3000 -``` +Optionally you could run the shell script `docker_bob_setup.sh` in this repository instead of manually `curl`ing each endpoint. #### Viewing logs +``` docker-compose logs --tail=20 -f +``` e.g. for all containers + ``` -docker-compose logs --tail=20 -f +docker-compose logs -f ``` e.g. for only tomcat @@ -176,6 +188,12 @@ e.g. for only tomcat docker-compose logs --tail=20 -f tomcat ``` +Once you want to bring everything down: + +```bash +docker-compose down +``` + #### Troubleshooting * There is a known timing issue where tomcat may try to connect to the database before the database is accepting connections. If this happens, try restarting tomcat. diff --git a/docker_bob_setup.sh b/docker_bob_setup.sh new file mode 100755 index 000000000..a9bd2bff0 --- /dev/null +++ b/docker_bob_setup.sh @@ -0,0 +1,7 @@ +curl localhost:3001/api/systemTest/getOrCreateACASBob +curl localhost:3001/api/systemTest/getOrCreateGlobalProject +curl localhost:3001/api/systemTest/getOrCreateGlobalProjectRole +curl localhost:3001/api/systemTest/giveBobRoles +curl localhost:3001/api/systemTest/getOrCreateCmpdRegBob +curl localhost:3001/api/systemTest/syncRoles +#curl localhost:3001/api/systemTest/getOrCreateCmpds \ No newline at end of file From 3fd6c4cd4070239c45ab4deb975e03b4655f56e2 Mon Sep 17 00:00:00 2001 From: keng Date: Thu, 16 May 2019 16:00:31 -0700 Subject: [PATCH 443/576] Edit to readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fca2fcf7..f368305fa 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,8 @@ curl localhost:3001/api/systemTest/syncRoles ``` Optionally you could run the shell script `docker_bob_setup.sh` in this repository instead of manually `curl`ing each endpoint. +This will create a user "bob" with password "secret". + #### Viewing logs ``` @@ -188,7 +190,7 @@ e.g. for only tomcat docker-compose logs --tail=20 -f tomcat ``` -Once you want to bring everything down: +Stop the web stack ```bash docker-compose down From fe4fc8ff73be6fda2ffbc9c7886420381da0ecac Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sun, 23 Jun 2019 12:39:20 -0700 Subject: [PATCH 444/576] call api endpoint instead of client method so we can include_permissions=false url attrute --- .../ServerAPI/src/server/python/acas_ldclient/acasldclient.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index 18f0d2924..466c76a77 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -103,7 +103,8 @@ def ld_user_to_acas_user_code_table(ld_user): return acas_user def get_users(client, ls_type = None, ls_kind = None, role_name = None): - ld_users = client.list_users() + # ld_users = client.list_users() + ld_users = client.client.get("/users?include_permissions=false", '') if ls_type == None and ls_kind == None and role_name == None: acas_users = map(ld_user_to_acas_user_code_table, ld_users) else: From 89e08fe514ef81d0d5683cea84c9352e3a44a800 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sun, 23 Jun 2019 13:32:34 -0700 Subject: [PATCH 445/576] switching to using psycopg2-binary as psycopg2 seems to require postgres-devel also be installed now --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4401e511a..2a03bb2a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -79,7 +79,7 @@ USER root RUN curl -SLO dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm && rpm -ivh epel-release-6-8.noarch.rpm && rm epel-release-6-8.noarch.rpm RUN yum install -y centos-release-SCL RUN yum install -y python-pip python-psycopg2 python27 -RUN source /opt/rh/python27/enable && pip install argparse requests psycopg2 +RUN source /opt/rh/python27/enable && pip install argparse requests psycopg2-binary USER runner EXPOSE 3000 From 8c90fbac74601b206bdd95a4271adbbc548d51ce Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 26 Jun 2019 14:27:31 -0700 Subject: [PATCH 446/576] Fixes #567: allowed project codes was getting set to null in author routes so the user was not getting access to a project they should have --- modules/ServerAPI/src/server/routes/AuthorRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 65f8bdcdf..6354264a9 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -57,7 +57,7 @@ exports.allowedProjectsInternal = (user, callback) -> allowedProjectCodes = [] user.roles.forEach (role) -> if role.roleEntry.lsType != null && role.roleEntry.lsType == "Project" - allowedProjectCodes.push role.lsKind + allowedProjectCodes.push role.roleEntry.lsKind else if role.roleEntry.roleName == config.all.client.roles.acas.adminRole isAdmin = true if isAdmin From 7813239b7bbc7885024cda48740caccf5783853f Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 1 Jul 2019 19:29:56 -0700 Subject: [PATCH 447/576] #568: Remove CmpdReg scientists module, add Additional Compound Scientists and Additional Assay Scientists code table editors --- .../src/client/CmpdRegScientist.coffee | 67 ---- .../src/client/CmpdRegScientist.html | 8 - .../src/client/AbstractCodeTablesAdmin.coffee | 252 ++++++++++++++ .../AbstractCodeTablesAdminBrowser.coffee | 308 ++++++++++++++++++ .../AbstractCodeTablesAdminBrowserView.html | 146 +++++++++ .../client/AbstractCodeTablesAdminView.html | 125 +++++++ .../client/CodeTablesAssayScientist.coffee | 69 ++++ .../src/client/CodeTablesAssayScientist.html | 8 + .../client/CodeTablesCompoundScientist.coffee | 69 ++++ .../client/CodeTablesCompoundScientist.html | 8 + .../routes/CodeTablesAdminRoutes.coffee | 176 ++++++++++ .../routes/CodeTableServiceRoutes.coffee | 54 ++- .../client/ModuleMenusConfiguration.coffee | 12 +- 13 files changed, 1213 insertions(+), 89 deletions(-) delete mode 100644 modules/CmpdRegAdmin/src/client/CmpdRegScientist.coffee delete mode 100644 modules/CmpdRegAdmin/src/client/CmpdRegScientist.html create mode 100644 modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdmin.coffee create mode 100644 modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowser.coffee create mode 100644 modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowserView.html create mode 100644 modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html create mode 100644 modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.coffee create mode 100644 modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.html create mode 100644 modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.coffee create mode 100644 modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.html create mode 100644 modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee diff --git a/modules/CmpdRegAdmin/src/client/CmpdRegScientist.coffee b/modules/CmpdRegAdmin/src/client/CmpdRegScientist.coffee deleted file mode 100644 index d05527a11..000000000 --- a/modules/CmpdRegAdmin/src/client/CmpdRegScientist.coffee +++ /dev/null @@ -1,67 +0,0 @@ -############################################################################ -# models -############################################################################ -class window.Scientist extends Backbone.Model - urlRoot: "/api/cmpdRegAdmin/scientists" - defaults: - name: null - code: null - id: null - ignored: false - - validate: (attrs) -> - errors = [] - if attrs.code? and @isNew() - validChars = attrs.code.match(/[a-zA-Z0-9 _\-+]/g) - unless validChars.length is attrs.code.length - errors.push - attribute: 'scientistCode' - message: "Scientist code can not contain special characters" - if !attrs.name? or attrs.name is "" - errors.push - attribute: 'scientistName' - message: "Scientist name must be set and unique" - if errors.length > 0 - return errors - else - return null - -############################################################################ -class window.Scientists extends Backbone.Collection - model: Scientist -############################################################################ - -############################################################################ -# controllers -############################################################################ - -class window.ScientistController extends AbstractCmpdRegAdminController - wrapperTemplate: _.template($("#ScientistView").html()) - moduleLaunchName: "scientist" - entityType: "scientist" - entityTypePlural: "scientists" - entityTypeUpper: "Scientist" - entityTypeUpperPlural: "Scientists" - modelClass: "Scientist" - showIgnore: true - - completeInitialization: => - @errorOwnerName = 'ScientistController' - $(@el).empty() - $(@el).html @wrapperTemplate() -# @$('.bv_scientistControllerDiv').html - console.log @$('.bv_scientistControllerDiv') - console.log "completing initialization of ScientistController" - @$('.bv_scientistControllerDiv').html super() - - -class window.ScientistBrowserController extends AbstractCmpdRegAdminBrowserController - wrapperTemplate: _.template($("#ScientistBrowserView").html()) - entityType: "scientist" - entityTypePlural: "scientists" - entityTypeUpper: "Scientist" - entityTypeUpperPlural: "Scientists" - entityClass: "Scientist" - entityControllerClass: "ScientistController" - moduleLaunchName: "scientist_browser" - showIgnore: true diff --git a/modules/CmpdRegAdmin/src/client/CmpdRegScientist.html b/modules/CmpdRegAdmin/src/client/CmpdRegScientist.html deleted file mode 100644 index af4f979a0..000000000 --- a/modules/CmpdRegAdmin/src/client/CmpdRegScientist.html +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdmin.coffee b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdmin.coffee new file mode 100644 index 000000000..517c09d60 --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdmin.coffee @@ -0,0 +1,252 @@ +class window.AbstractCodeTablesAdminController extends AbstractFormController + ### + Instances of this controller must supply "moduleLaunchName", "codeType", and "modelClass" + e.g. other required fields + codeType: "assay" + codeKind: "scientist" + displayName: "assay scientist" + pluralDisplayName: "assay scientists" + upperDisplayName: "Assay Scientist" + upperPluralDisplayName: "Assay Scientists" + entityClass: "AssayScientist" + entityControllerClass: "AssayScientistController" + moduleLaunchName: "assay_scientist_browser" + ### + template: _.template($("#AbstractCodeTablesAdminView").html()) + + events: -> + "keyup .bv_codeTablesAdminCode": "handleCodeTablesAdminCodeNameChanged" + "keyup .bv_codeTablesAdminName": "handleCodeTablesAdminNameChanged" + "click .bv_codeTablesAdminIgnore": "handleCodeTablesAdminIgnoreChanged" + "click .bv_save": "handleSaveClicked" + "click .bv_backToCodeTablesAdminBrowserBtn": "handleBackToCodeTablesAdminBrowserClicked" +# "click .bv_newEntity": "handleNewEntityClicked" + "click .bv_cancel": "handleCancelClicked" + "click .bv_cancelClear": "handleCancelClearClicked" + "click .bv_confirmClear": "handleConfirmClearClicked" + + initialize: -> + console.log "initializing Abstract Controller" + if @model? + @completeInitialization() + else + if window.AppLaunchParams.moduleLaunchParams? + if window.AppLaunchParams.moduleLaunchParams.moduleName == @moduleLaunchName + $.ajax + type: 'GET' + url: @model.urlRoot + '/codeName/'+window.AppLaunchParams.moduleLaunchParams.code + dataType: 'json' + error: (err) => + alert "Error getting #{@codeType} for code in this URL. Creating a new project" + @completeInitialization() + success: (json) => + if json.id? + @model = new window[@modelClass] json + else + alert "Could not get #{@codeType} for code in this URL. Creating a new project" + @completeInitialization() + else + @completeInitialization() + else + @completeInitialization() + + + completeInitialization: => + unless @model? + @model=new window[@modelClass]() + @setBindings() + if @options.readOnly? + @readOnly = @options.readOnly + else + @readOnly = false + unless @showIgnore? + @showIgnore = false + @listenTo @model, 'saveFailed', @handleSaveFailed + @listenTo @model, 'sync', @modelSaveCallback + @listenTo @model, 'change', @modelChangeCallback + $(@el).empty() + toDisplay = + displayName: @displayName + pluralDisplayName: @pluralDisplayName + upperDisplayName: @upperDisplayName + upperPluralDisplayName: @upperPluralDisplayName + $(@el).html(@template(toDisplay)) + if @showIgnore + @$(".bv_group_codeTablesAdminIgnore").show() + else + @$(".bv_group_codeTablesAdminIgnore").hide() + @render() + + render: => + unless @model? + @model = new window[@modelClass]() + code = @model.get('code') + @$('.bv_codeTablesAdminCode').val(code) + @$('.bv_codeTablesAdminCode').html(code) + if @showIgnore + if @model.get('ignored') is true + @$('.bv_codeTablesAdminIgnore').attr 'checked', 'checked' + else + @$('.bv_codeTablesAdminIgnore').removeAttr 'checked' + + if @model.isNew() + @$('.bv_save').html("Save") +# @$('.bv_newEntity').hide() + else + @$('.bv_save').html("Update") + # @$('.bv_newEntity').show() + @$('.bv_codeTablesAdminName').val @model.get('name') + + if @readOnly is true + @displayInReadOnlyMode() + @$('.bv_backToCodeTablesAdminBrowserBtn').hide() + @$('.bv_save').attr('disabled','disabled') + @$('.bv_cancel').attr('disabled','disabled') + + @ + + handleSaveFailed: => + @$('.bv_saveFailed').show() + @$('.bv_saveComplete').hide() + @$('.bv_saving').hide() + + modelSaveCallback: (method, model) => + @$('.bv_save').show() + @$('.bv_save').attr('disabled', 'disabled') + unless @$('.bv_saveFailed').is(":visible") + @$('.bv_saveComplete').show() + @$('.bv_saving').hide() + @$('.bv_cancel').removeAttr 'disabled' + @render() + @trigger 'amClean' + + modelChangeCallback: (method, model) => + @trigger 'amDirty' + @checkFormValid() + @$('.bv_saveComplete').hide() + @$('.bv_saveFailed').hide() + @$('.bv_cancel').removeAttr('disabled') + @$('.bv_cancelComplete').hide() + + handleCodeTablesAdminCodeNameChanged: => + code = UtilityFunctions::getTrimmedInput @$('.bv_codeTablesAdminCode') + + if code is "" + @model.set 'code', null + else + @model.set 'code', code + @model.trigger 'change' + + handleBackToCodeTablesAdminBrowserClicked: => + @trigger 'backToBrowser' + + +# handleNewEntityClicked: => +# @$('.bv_confirmClearEntity').modal('show') +# @$('.bv_confirmClear').removeAttr('disabled') +# @$('.bv_cancelClear').removeAttr('disabled') +# @$('.bv_closeModalButton').removeAttr('disabled') + + handleCancelClearClicked: => + @$('.bv_confirmClearEntity').modal('hide') + + handleConfirmClearClicked: => + @$('.bv_confirmClearEntity').modal('hide') + @model = null + @completeInitialization() + @trigger 'amClean' + + handleCancelClicked: => + if @model.isNew() + @model = null + @completeInitialization() + else + @$('.bv_canceling').show() + @model.fetch + success: @handleCancelComplete + @trigger 'amClean' + + handleCancelComplete: => + @$('.bv_canceling').hide() + @$('.bv_cancelComplete').show() + + handleCodeTablesAdminNameChanged: => + @model.set("name", UtilityFunctions::getTrimmedInput @$('.bv_codeTablesAdminName')) + + handleCodeTablesAdminIgnoreChanged: => + @model.set("ignored", @$('.bv_codeTablesAdminIgnore').is(":checked")) + + handleSaveClicked: => + @callNameValidationService() + + @$('.bv_saving').show() + @$('.bv_saveFailed').hide() + @$('.bv_saveComplete').hide() + + + callNameValidationService: => + @$('.bv_saving').show() + @$('.bv_save').attr('disabled', 'disabled') + validateURL = @model.urlRoot + '/validateBeforeSave' + dataToPost = + data: JSON.stringify(@model) + $.ajax + type: 'POST' + url: validateURL + data: dataToPost + dataType: 'json' + success: (response) => + @handleValidateReturn(response) + error: (err) => + @handleValidateError(JSON.parse err.responseText) + + handleValidateError: (err) => + if err?[0]?.level? and err[0].level is "ERROR" + alert "The requested code has already been used" + @$('.bv_saving').hide() + @$('.bv_saveFailed').show() + + handleValidateReturn: (validateResp) => + if validateResp?[0]?.errorLevel? + alert "The requested #{@codeType} #{@codeKind} code has already been registered. Please choose a new #{@codeType} #{@codeKind} code." + @$('.bv_saving').hide() + @$('.bv_saveFailed').show() + else + @saveCodeTablesAdmin() + + saveCodeTablesAdmin: => + if @model.isNew() + @$('.bv_saveComplete').html('Save Complete') + else + @$('.bv_saveComplete').html('Update Complete') + @$('.bv_save').attr('disabled', 'disabled') + + @model.save null, + success: (model, response) => + if response is "update #{@codeType} failed" + @model.trigger 'saveFailed' + + validationError: => + super() + @$('.bv_save').attr('disabled', 'disabled') + + clearValidationErrorStyles: => + super() + @$('.bv_save').removeAttr('disabled') + + checkDisplayMode: => + if @readOnly is true + @displayInReadOnlyMode() + + displayInReadOnlyMode: => + @$(".bv_save").hide() + @$(".bv_cancel").hide() + # @$(".bv_newEntity").hide() + @$('button').attr 'disabled', 'disabled' + @disableAllInputs() + + checkFormValid: => + if @isValid() + @$('.bv_save').removeAttr('disabled') + else + @$('.bv_save').attr('disabled', 'disabled') \ No newline at end of file diff --git a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowser.coffee b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowser.coffee new file mode 100644 index 000000000..cc5dfa18a --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowser.coffee @@ -0,0 +1,308 @@ +############################################################################ +# models +############################################################################ +class window.CodeTablesAdminSearch extends Backbone.Model + defaults: + codeType: null + codeKind: null + name: null + code: null + id: null + +############################################################################ +class window.CodeTablesAdminList extends Backbone.Collection + model: CodeTablesAdminSearch + +############################################################################ +# controllers +############################################################################ +class window.CodeTablesAdminSimpleSearchController extends AbstractFormController + ### + Instantiating controller must provide urlRoot and toDisplay in options + ### + + initialize: -> + @searchUrl = "" + @searchUrl = @options.urlRoot + '/search/' + + events: + 'keyup .bv_codeTablesAdminSearchTerm': 'updateCodeTablesAdminSearchTerm' + 'click .bv_doSearch': 'handleDoSearchClicked' + "click .bv_createNewCodeTablesAdminBtn": "handleCreateNewCodeTablesAdminClicked" + + render: => + console.log "rendering SimpleSearchController" + template = _.template($("#CodeTablesAdminSimpleSearchView").html()) + $(@el).empty() + console.log @options.toDisplay + $(@el).html(template(@options.toDisplay)) + @ + + updateCodeTablesAdminSearchTerm: (e) => + ENTER_KEY = 13 + codeTablesAdminSearchTerm = $.trim(@$(".bv_codeTablesAdminSearchTerm").val()) + if codeTablesAdminSearchTerm isnt "" + @$(".bv_doSearch").attr("disabled", false) + if e.keyCode is ENTER_KEY + $(':focus').blur() + @trigger 'searchRequested' + else + @$(".bv_doSearch").attr("disabled", true) + + doSearch: (codeTablesAdminSearchTerm) => +# disable the search text field while performing a search + @$(".bv_codeTablesAdminSearchTerm").attr "disabled", true + @$(".bv_doSearch").attr "disabled", true + @trigger 'find' + unless codeTablesAdminSearchTerm is "" + $.ajax + type: 'GET' + url: @searchUrl + codeTablesAdminSearchTerm + dataType: "json" + data: + testMode: false + +#fullObject: true + success: (codeTablesAdmin) => + @trigger "searchReturned", codeTablesAdmin + error: (result) => + @trigger "searchReturned", null + complete: => +# re-enable the search text field regardless of if any results found + @$(".bv_codeTablesAdminSearchTerm").attr "disabled", false + @$(".bv_doSearch").attr "disabled", false + + handleCreateNewCodeTablesAdminClicked: => + @trigger 'createNewCodeTablesAdmin' + +############################################################################ +class window.CodeTablesAdminRowSummaryController extends Backbone.View + tagName: 'tr' + className: 'dataTableRow' + events: + "click": "handleClick" + + handleClick: => + @trigger "gotClick", @model + $(@el).closest("table").find("tr").removeClass "info" + $(@el).addClass "info" + + initialize: -> + @template = _.template($('#CodeTablesAdminRowSummaryView').html()) + if @options.showIgnore? + @showIgnore = @options.showIgnore + else + @showIgnore = false + + render: => + toDisplay = + code: @model.get('code') + name: @model.get('name') + + $(@el).html(@template(toDisplay)) + if @showIgnore + ignored = if @model.get('ignored')? then @model.get('ignored') else false + + @$(".bv_codeTablesAdminIgnore").show() + @$(".bv_codeTablesAdminIgnore").html ignored.toString() + else + @$(".bv_codeTablesAdminIgnore").hide() + @ + +############################################################################ +class window.CodeTablesAdminSummaryTableController extends Backbone.View + initialize: -> + if @options.showIgnore? + @showIgnore = @options.showIgnore + else + @showIgnore = false + + selectedRowChanged: (row) => + @trigger "selectedRowUpdated", row + + render: => + @template = _.template($('#CodeTablesAdminSummaryTableView').html()) + $(@el).html @template(@options.toDisplay) + #will always be instantiated with at least one model in collection + @collection.each (admin) => + prsc = new CodeTablesAdminRowSummaryController + model: admin + showIgnore: @showIgnore + prsc.on "gotClick", @selectedRowChanged + @$("tbody").append prsc.render().el + + @$("table").dataTable oLanguage: + sSearch: "Filter results: " #rename summary table's search bar + + if @showIgnore + @$(".bv_ignoredHeader").show() + else + @$(".bv_ignoredHeader").hide() + + @ + +############################################################################ +class window.AbstractCodeTablesAdminBrowserController extends Backbone.View + ### + Instantiating controller must provide: + codeType + entityClass + entityControllerClass + moduleLaunchName + ### + includeDuplicateAndEdit: false + events: + "click .bv_deleteCodeTablesAdmin": "handleDeleteCodeTablesAdminClicked" + "click .bv_editCodeTablesAdmin": "handleEditCodeTablesAdminClicked" + "click .bv_confirmDeleteCodeTablesAdminButton": "handleConfirmDeleteCodeTablesAdminClicked" + "click .bv_cancelDelete": "handleCancelDeleteClicked" + + + initialize: -> + template = _.template( $("#AbstractCodeTablesAdminBrowserView").html()); + $(@el).empty() + @toDisplay = + displayName: @displayName + pluralDisplayName: @pluralDisplayName + upperDisplayName: @upperDisplayName + upperPluralDisplayName: @upperPluralDisplayName + $(@el).html(template(@toDisplay)) + unless @showIgnore? + @showIgnore = false + @searchController = new CodeTablesAdminSimpleSearchController + model: new CodeTablesAdminSearch() + el: @$('.bv_codeTablesAdminSearchController') + urlRoot: "/api/codeTablesAdmin/#{@codeType}/#{@codeKind}" + toDisplay: @toDisplay + @searchController.render() + @searchController.on "searchRequested", @handleSearchRequested + @searchController.on "searchReturned", @setupCodeTablesAdminSummaryTable + @searchController.on "createNewCodeTablesAdmin", @handleCreateNewCodeTablesAdminClicked + #@searchController.on "resetSearch", @destroyCodeTablesAdminSummaryTable + + setupCodeTablesAdminSummaryTable: (codeTablesAdmins) => + @destroyCodeTablesAdminSummaryTable() + + @$(".bv_searchingCodeTablesAdminsMessage").addClass "hide" + if codeTablesAdmins is null + @$(".bv_errorOccurredPerformingSearch").removeClass "hide" + + else if codeTablesAdmins.length is 0 + @$(".bv_noMatchingCodeTablesAdminsFoundMessage").removeClass "hide" + @$(".bv_codeTablesAdminTableController").html "" + else + @$(".bv_searchCodeTablesAdminsStatusIndicator").addClass "hide" + @$(".bv_codeTablesAdminTableController").removeClass "hide" + @codeTablesAdminSummaryTable = new CodeTablesAdminSummaryTableController + collection: new CodeTablesAdminList codeTablesAdmins + toDisplay: @toDisplay + showIgnore: @showIgnore + + @codeTablesAdminSummaryTable.on "selectedRowUpdated", @selectedCodeTablesAdminUpdated + @$(".bv_codeTablesAdminTableController").html @codeTablesAdminSummaryTable.render().el + + selectedCodeTablesAdminUpdated: (codeTablesAdmin) => + @trigger "selectedCodeTablesAdminUpdated" + if @codeTablesAdminController? + @codeTablesAdminController.undelegateEvents() + @codeTablesAdminController = new window[@entityControllerClass] + model: new window[@entityClass] codeTablesAdmin.attributes + readOnly: true + + @$('.bv_codeTablesAdminController').html @codeTablesAdminController.render().el + @$(".bv_codeTablesAdminController").removeClass("hide") + @$(".bv_codeTablesAdminControllerContainer").removeClass("hide") + @$('.bv_editCodeTablesAdmin').show() + @$('.bv_deleteCodeTablesAdmin').show() + + handleSearchRequested: => + @$(".bv_codeTablesAdminTableController").addClass "hide" + @$(".bv_errorOccurredPerformingSearch").addClass "hide" + codeTablesAdminSearchTerm = $.trim(@$(".bv_codeTablesAdminSearchTerm").val()) + if codeTablesAdminSearchTerm isnt "" + @$(".bv_noMatchingCodeTablesAdminsFoundMessage").addClass "hide" + @$(".bv_codeTablesAdminBrowserSearchInstructions").addClass "hide" + @$(".bv_searchCodeTablesAdminsStatusIndicator").removeClass "hide" + if !window.conf.browser.enableSearchAll and codeTablesAdminSearchTerm is "*" + @$(".bv_moreSpecificCodeTablesAdminSearchNeeded").removeClass "hide" + else + @$(".bv_searchingCodeTablesAdminsMessage").removeClass "hide" + @$(".bv_codeTablesAdminSearchTerm").html codeTablesAdminSearchTerm + @$(".bv_moreSpecificCodeTablesAdminSearchNeeded").addClass "hide" + @searchController.doSearch codeTablesAdminSearchTerm + + handleDeleteCodeTablesAdminClicked: => + @$(".bv_codeTablesAdminCodeName").html @codeTablesAdminController.model.get("code") + @$(".bv_deleteButtons").removeClass "hide" + @$(".bv_okayButton").addClass "hide" + @$(".bv_errorDeletingCodeTablesAdminMessage").addClass "hide" + @$(".bv_deleteWarningMessage").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_codeTablesAdminDeletedSuccessfullyMessage").addClass "hide" + @$(".bv_confirmDeleteCodeTablesAdmin").removeClass "hide" + @$('.bv_confirmDeleteCodeTablesAdmin').modal({ + keyboard: false, + backdrop: true + }) + + handleConfirmDeleteCodeTablesAdminClicked: => + @$(".bv_deleteWarningMessage").addClass "hide" + @$(".bv_deletingStatusIndicator").removeClass "hide" + @$(".bv_deleteButtons").addClass "hide" + $.ajax( + url: "/api/codeTablesAdmin/#{@codeTablesAdminController.model.get("id")}", + type: 'DELETE', + success: (result) => + @$(".bv_okayButton").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_codeTablesAdminDeletedSuccessfullyMessage").removeClass "hide" + @handleSearchRequested() + error: (result) => + @$(".bv_okayButton").removeClass "hide" + @$(".bv_deletingStatusIndicator").addClass "hide" + @$(".bv_errorDeletingCodeTablesAdminMessage").removeClass "hide" + ) + + handleCancelDeleteClicked: => + @$(".bv_confirmDeleteCodeTablesAdmin").modal('hide') + + handleEditCodeTablesAdminClicked: => + @createCodeTablesAdminController @codeTablesAdminController.model + + destroyCodeTablesAdminSummaryTable: => + if @codeTablesAdminSummaryTable? + @codeTablesAdminSummaryTable.remove() + if @codeTablesAdminController? + @codeTablesAdminController.remove() + + @$(".bv_codeTablesAdminController").addClass("hide") + @$(".bv_codeTablesAdminControllerContainer").addClass("hide") + @$(".bv_noMatchingCodeTablesAdminsFoundMessage").addClass("hide") + + render: => + @ + + handleCreateNewCodeTablesAdminClicked: => + @createCodeTablesAdminController new window[@entityClass]() + + createCodeTablesAdminController: (mdl) => + @$('.bv_codeTablesAdminBrowserWrapper').hide() + @$('.bv_codeTablesAdminControllerWrapper').show() + if @codeTablesAdminController? + @codeTablesAdminController.undelegateEvents() + @$(".bv_codeTablesAdminControllerWrapper").html "" + @codeTablesAdminController = new window[@entityControllerClass] + model: mdl + @codeTablesAdminController.on 'backToBrowser', @handleBackToCodeTablesAdminBrowserClicked + @codeTablesAdminController.on 'amDirty', => + @trigger 'amDirty' + @codeTablesAdminController.on 'amClean', => + @trigger 'amClean' + @$(".bv_codeTablesAdminControllerWrapper").append @codeTablesAdminController.render().el + @codeTablesAdminController.$('.bv_backToCodeTablesAdminBrowserBtn').show() + + handleBackToCodeTablesAdminBrowserClicked: => + @$('.bv_codeTablesAdminBrowserWrapper').show() + @$('.bv_codeTablesAdminControllerWrapper').hide() + @handleSearchRequested() + @trigger 'amClean' diff --git a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowserView.html b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowserView.html new file mode 100644 index 000000000..8d7d339c7 --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminBrowserView.html @@ -0,0 +1,146 @@ + + + + + + + diff --git a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html new file mode 100644 index 000000000..ee252bc18 --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html @@ -0,0 +1,125 @@ + + diff --git a/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.coffee b/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.coffee new file mode 100644 index 000000000..a0f74dc25 --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.coffee @@ -0,0 +1,69 @@ +############################################################################ +# models +############################################################################ +class window.AssayScientist extends Backbone.Model + urlRoot: "/api/codeTablesAdmin/assay/scientist" + defaults: + name: null + code: null + id: null + ignored: false + + validate: (attrs) -> + errors = [] + if attrs.code? and @isNew() + validChars = attrs.code.match(/[a-zA-Z0-9 _\-+]/g) + unless validChars.length is attrs.code.length + errors.push + attribute: 'scientistCode' + message: "Assay Scientist code can not contain special characters" + if !attrs.code? or attrs.code is "" + errors.push + attribute: 'scientistCode' + message: "Assay Scientist code must be set and unique" + if !attrs.name? or attrs.name is "" + errors.push + attribute: 'scientistName' + message: "Assay Scientist name must be set and unique" + if errors.length > 0 + return errors + else + return null + +############################################################################ +# controllers +############################################################################ + +class window.AssayScientistController extends AbstractCodeTablesAdminController + wrapperTemplate: _.template($("#AssayScientistView").html()) + moduleLaunchName: "scientist" + codeType: "assay" + codeKind: "scientist" + displayName: "assay scientist" + pluralDisplayName: "assay scientists" + upperDisplayName: "Assay Scientist" + upperPluralDisplayName: "Scientists" + modelClass: "AssayScientist" + showIgnore: true + + completeInitialization: => + @errorOwnerName = 'AssayScientistController' + $(@el).empty() + $(@el).html @wrapperTemplate() + console.log @$('.bv_assayAssayScientistControllerDiv') + console.log "completing initialization of AssayScientistController" + @$('.bv_assayAssayScientistControllerDiv').html super() + + +class window.AssayScientistBrowserController extends AbstractCodeTablesAdminBrowserController + wrapperTemplate: _.template($("#AssayScientistBrowserView").html()) + codeType: "assay" + codeKind: "scientist" + displayName: "assay scientist" + pluralDisplayName: "assay scientists" + upperDisplayName: "Assay Scientist" + upperPluralDisplayName: "Assay Scientists" + entityClass: "AssayScientist" + entityControllerClass: "AssayScientistController" + moduleLaunchName: "assay_scientist_browser" + showIgnore: true diff --git a/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.html b/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.html new file mode 100644 index 000000000..a1198b5d8 --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/CodeTablesAssayScientist.html @@ -0,0 +1,8 @@ + + + diff --git a/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.coffee b/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.coffee new file mode 100644 index 000000000..49cc6a0cd --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.coffee @@ -0,0 +1,69 @@ +############################################################################ +# models +############################################################################ +class window.CompoundScientist extends Backbone.Model + urlRoot: "/api/codeTablesAdmin/compound/scientist" + defaults: + name: null + code: null + id: null + ignored: false + + validate: (attrs) -> + errors = [] + if attrs.code? and @isNew() + validChars = attrs.code.match(/[a-zA-Z0-9 _\-+]/g) + unless validChars.length is attrs.code.length + errors.push + attribute: 'scientistCode' + message: "Compound Scientist code can not contain special characters" + if !attrs.code? or attrs.code is "" + errors.push + attribute: 'scientistCode' + message: "Compound Scientist code must be set and unique" + if !attrs.name? or attrs.name is "" + errors.push + attribute: 'scientistName' + message: "Compound Scientist name must be set and unique" + if errors.length > 0 + return errors + else + return null + +############################################################################ +# controllers +############################################################################ + +class window.CompoundScientistController extends AbstractCodeTablesAdminController + wrapperTemplate: _.template($("#CompoundScientistView").html()) + moduleLaunchName: "scientist" + codeType: "compound" + codeKind: "scientist" + displayName: "compound scientist" + pluralDisplayName: "compound scientists" + upperDisplayName: "Compound Scientist" + upperPluralDisplayName: "Scientists" + modelClass: "CompoundScientist" + showIgnore: true + + completeInitialization: => + @errorOwnerName = 'CompoundScientistController' + $(@el).empty() + $(@el).html @wrapperTemplate() + console.log @$('.bv_compoundCompoundScientistControllerDiv') + console.log "completing initialization of CompoundScientistController" + @$('.bv_compoundCompoundScientistControllerDiv').html super() + + +class window.CompoundScientistBrowserController extends AbstractCodeTablesAdminBrowserController + wrapperTemplate: _.template($("#CompoundScientistBrowserView").html()) + codeType: "compound" + codeKind: "scientist" + displayName: "compound scientist" + pluralDisplayName: "compound scientists" + upperDisplayName: "Compound Scientist" + upperPluralDisplayName: "Compound Scientists" + entityClass: "CompoundScientist" + entityControllerClass: "CompoundScientistController" + moduleLaunchName: "compound_scientist_browser" + showIgnore: true diff --git a/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.html b/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.html new file mode 100644 index 000000000..e8a18af2a --- /dev/null +++ b/modules/CodeTablesAdmin/src/client/CodeTablesCompoundScientist.html @@ -0,0 +1,8 @@ + + + diff --git a/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee b/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee new file mode 100644 index 000000000..970fda4f8 --- /dev/null +++ b/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee @@ -0,0 +1,176 @@ +exports.setupAPIRoutes = (app, loginRoutes) -> + app.get '/api/codeTablesAdmin/:entityType', exports.getCodeTablesEntities + app.get '/api/codeTablesAdmin/:entityType/d/:code', exports.validateCodeTablesEntity + app.get '/api/codeTablesAdmin/:entityType/codeName/:code', exports.getCodeTablesEntityByCode + app.post '/api/codeTablesAdmin/:codeType/:codeKind/validateBeforeSave', exports.validateCodeTablesEntityBeforeSave + app.post '/api/codeTablesAdmin/:entityType', exports.saveCodeTablesEntity + app.put '/api/codeTablesAdmin/:codeType/:codeKind/:id', exports.updateCodeTablesEntity + app.delete '/api/codeTablesAdmin/:codeType/:codeKind/:id', exports.deleteCodeTablesEntity + +exports.setupRoutes = (app, loginRoutes) -> + app.get '/api/codeTablesAdmin/:codeType/:codeKind/search/:searchTerm', loginRoutes.ensureAuthenticated, exports.searchCodeTablesEntities + app.post '/api/codeTablesAdmin/:codeType/:codeKind/validateBeforeSave', loginRoutes.ensureAuthenticated, exports.validateCodeTablesEntityBeforeSave + app.post '/api/codeTablesAdmin/:codeType/:codeKind', loginRoutes.ensureAuthenticated, exports.saveCodeTablesEntity + app.put '/api/codeTablesAdmin/:codeType/:codeKind/:id', loginRoutes.ensureAuthenticated, exports.updateCodeTablesEntity + app.delete '/api/codeTablesAdmin/:id', loginRoutes.ensureAuthenticated, exports.deleteCodeTablesEntity + +exports.validateCodeTablesEntity = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + entityType = req.params.entityType + cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + "#{entityType}/validate?code=" + req.params.code + request( + method: 'GET' + url: cmpdRegCall + json: false + timeout: 6000000 + , (error, response, validEntity) => + if !error and !validEntity.startsWith('<') + resp.statusCode = response.statusCode + resp.setHeader('Content-Type', 'application/json') + resp.end validEntity + console.log "done validating the #{entityType} testString" + else + console.log 'got ajax error trying to do validate #{entityType}' + resp.statusCode = 500 + resp.end JSON.stringify {error: "something went wrong validating the #{entityType}"} + ) + +exports.validateCodeTablesEntityBeforeSave = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + codeTableServiceRoutes = require "./CodeTableServiceRoutes.js" + searchTerm = req.params.searchTerm + codeTableServiceRoutes.getCodeTableValuesInternal req.params.codeType, req.params.codeKind, (results) -> + for r in results + data = JSON.parse(req.body.data) + if (!data.id? || data.id!=r.id) && r.code == data.code + resp.end JSON.stringify([{"errorLevel": "ERROR", "message": "Code value already exists for #{req.params.codeType} #{req.params.codeKind}"}]) + return + resp.end req.body.data + +exports.getCodeTablesEntityById = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + entityType = req.params.entityType + cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + "#{entityType}/" + req.params.id + request( + method: 'GET' + url: cmpdRegCall + json: true + timeout: 6000000 + , (error, response, json) => + if !error && response.statusCode == 200 + console.log JSON.stringify json + resp.statusCode = response.statusCode + resp.setHeader('Content-Type', 'application/json') + resp.json json + else + resp.statusCode = 404 + console.error "got ajax error trying to do find #{entityType}" + console.log json + resp.end JSON.stringify {error: "something went wrong :("} + ) + +exports.getCodeTablesEntityByCode = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + entityType = req.params.entityType + cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + "#{entityType}/findByCodeEquals?code=" + req.params.code + request( + method: 'GET' + url: cmpdRegCall + json: true + timeout: 6000000 + , (error, response, json) => + if !error && response.statusCode == 200 + console.log JSON.stringify json + resp.statusCode = response.statusCode + resp.setHeader('Content-Type', 'application/json') + resp.json json + else + resp.statusCode = 404 + console.log "got ajax error trying to do find #{entityType}" + console.log json + resp.end JSON.stringify {error: "something went wrong :("} + ) + +exports.searchCodeTablesEntities = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + codeTableServiceRoutes = require "./CodeTableServiceRoutes.js" + searchTerm = req.params.searchTerm.toLowerCase().trim() + codeTableServiceRoutes.getCodeTableValuesInternal req.params.codeType, req.params.codeKind, (results) -> + searchResults = [] + if searchTerm == "*" || searchTerm == "" + searchResults = results + else + for r in results + if r.code.toLowerCase().indexOf(searchTerm) >= 0 || r.comments.toLowerCase().indexOf(searchTerm) >= 0 || r.description.toLowerCase().indexOf(searchTerm) >= 0 || r.name.toLowerCase().indexOf(searchTerm) >= 0 + searchResults.push(r) + resp.json searchResults + +exports.getCodeTablesEntities = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + entityType = req.params.entityType + cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + "#{entityType}" + request( + method: 'GET' + url: cmpdRegCall + json: true + timeout: 6000000 + , (error, response, json) => + if !error + console.log JSON.stringify json + resp.statusCode = response.statusCode + resp.setHeader('Content-Type', 'application/json') + resp.json json + else + console.log "got ajax error trying to do get #{entityType}" + console.log json + resp.statusCode = 404 + resp.end JSON.stringify {error: "something went wrong :("} + ) + +exports.saveCodeTablesEntity = (req, resp) -> + codeTableServiceRoutes = require "./CodeTableServiceRoutes.js" + codeEntry = { + comments: "registered with code tables admin module", + description: "registered with code tables admin module", + displayOrder: null, + ignored: req.body.ignored, + name: req.body.name, + codeOrigin: "ACAS" + codeKind: req.params.codeKind, + codeType: req.params.codeType, + code: req.body.code + } + codeTableServiceRoutes.postCodeTableInternal codeEntry, (statusCode, response) -> + resp.end response + +exports.updateCodeTablesEntity = (req, resp) -> + codeTableServiceRoutes = require "./CodeTableServiceRoutes.js" + codeEntry = { + comments: "registered with code tables admin module", + description: "registered with code tables admin module", + displayOrder: null, + ignored: req.body.ignored, + name: req.body.name, + codeOrigin: "ACAS" + codeKind: req.params.codeKind, + codeType: req.params.codeType, + code: req.body.code + id: req.body.id + } + codeTableServiceRoutes.putCodeTableInternal codeEntry, (statusCode, response) -> + resp.end response + + +exports.deleteCodeTablesEntity = (req, resp) -> + codeTableServiceRoutes = require "./CodeTableServiceRoutes.js" + + codeTableServiceRoutes.deleteCodeTableInternal {id: req.params.id}, (statusCode, response) -> + console.log(statusCode) + console.log(response) + resp.json response diff --git a/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee b/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee index 7e87f439e..1e30160b9 100644 --- a/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee +++ b/modules/Components/src/server/routes/CodeTableServiceRoutes.coffee @@ -72,9 +72,13 @@ exports.getCodeTableValuesInternal = (type, kind, cb) -> exports.postCodeTable = (req, resp) -> + exports.postCodeTableInternal req.body.codeEntry, (statusCode, response) => + resp.end JSON.stringify response + +exports.postCodeTableInternal = (codeTableEntry, callback) -> if global.specRunnerTestmode codeTablePostTestJSON = require '../public/javascripts/spec/Components/testFixtures/codeTablePostTestJSON.js' - resp.end JSON.stringify codeTablePostTestJSON.codeEntry + callback 201, JSON.stringify codeTablePostTestJSON.codeEntry else console.log "attempting to post new code table value" config = require '../conf/compiled/conf.js' @@ -83,48 +87,76 @@ exports.postCodeTable = (req, resp) -> request( method: 'POST' url: baseurl - body: req.body.codeEntry + body: codeTableEntry json: true , (error, response, json) => if !error && response.statusCode == 201 - resp.end JSON.stringify json + callback 201, JSON.stringify json else console.log 'got ajax error trying to save new code table' console.log error console.log json - console.log response + callback response.statusCode, response.json ) - exports.putCodeTable = (req, resp) -> - #console.log JSON.stringify req.body + exports.putCodeTableInternal req.body.codeEntry, (statusCode, response) => + resp.end JSON.stringify response + +exports.putCodeTableInternal = (codeTableEntry, callback) -> if global.specRunnerTestmode codeTablePostTestJSON = require '../public/javascripts/spec/Components/testFixtures/codeTablePutTestJSON.js' resp.end JSON.stringify codeTablePostTestJSON.codeEntry else config = require '../conf/compiled/conf.js' - putId = req.body.id + putId = codeTableEntry.id baseurl = "#{config.all.client.service.persistence.fullpath}ddictvalues/codetable/#{putId}" request = require 'request' request( method: 'PUT' url: baseurl - body: req.body + body: codeTableEntry json: true , (error, response, json) => console.log response.statusCode if !error && response.statusCode == 200 - resp.end JSON.stringify json + callback 200, JSON.stringify json else console.log 'got ajax error trying to update code table' console.log error console.log response + callback response.statusCode, response.json ) +exports.deleteCodeTable = (req, resp) -> + exports.deleteCodeTableInternal req.body.codeEntry, (statusCode, response) => + resp.end JSON.stringify response - - +exports.deleteCodeTableInternal = (codeTableEntry, callback) -> + config = require '../conf/compiled/conf.js' + console.log "#{config.all.client.service.persistence.fullpath}ddictvalues/#{codeTableEntry.id}" + if global.specRunnerTestmode + codeTablePostTestJSON = require '../public/javascripts/spec/Components/testFixtures/codeTablePostTestJSON.js' + callback 201, JSON.stringify codeTablePostTestJSON.codeEntry + else + console.log "attempting to delete code table value" + config = require '../conf/compiled/conf.js' + baseurl = "#{config.all.client.service.persistence.fullpath}ddictvalues/#{codeTableEntry.id}" + request = require 'request' + request( + method: 'DELETE' + url: baseurl + json: true + , (error, response, json) => + if !error && response.statusCode == 200 + callback 200, JSON.stringify json + else + console.log 'got ajax error trying to delete code table' + console.log error + console.log json + callback response.statusCode, response.json + ) diff --git a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee index a040b8d7c..d39a5b4ed 100644 --- a/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee +++ b/modules/ModuleMenus/src/client/ModuleMenusConfiguration.coffee @@ -78,6 +78,12 @@ window.ModuleMenusConfiguration = mainControllerClassName: "ACASLabelSequenceController" autoLaunchName: "acasLabelSequence" requireUserRoles: [window.conf.roles.acas.adminRole] + , + isHeader: false + menuName: "Additional Assay Scientists" + mainControllerClassName: "AssayScientistBrowserController" + autoLaunchName: "assay_scientist_browser" + requireUserRoles: [window.conf.roles.acas.adminRole] , isHeader: true menuName: "CmpdReg Admin" @@ -97,8 +103,8 @@ window.ModuleMenusConfiguration = requireUserRoles: [window.conf.roles.cmpdreg.adminRole] , isHeader: false - menuName: "CmpdReg Scientists" - mainControllerClassName: "ScientistBrowserController" - autoLaunchName: "scientist_browser" + menuName: "Additional CmpdReg Scientists" + mainControllerClassName: "CompoundScientistBrowserController" + autoLaunchName: "compound_scientist_browser" requireUserRoles: [window.conf.roles.cmpdreg.adminRole] ] From 6338412f00b201f8e94f1934c61d153565d5e2a7 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 1 Jul 2019 19:35:13 -0700 Subject: [PATCH 448/576] update acasclient to simplify user acls --- .../python/acas_ldclient/acasldclient.py | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index 466c76a77..280eb3f64 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -109,19 +109,28 @@ def get_users(client, ls_type = None, ls_kind = None, role_name = None): acas_users = map(ld_user_to_acas_user_code_table, ld_users) else: groups = client.list_groups() - roles = map(ld_group_to_acas_role, groups) - role = [r for r in roles if r["roleEntry"]["lsType"] == ls_type and r["roleEntry"]["lsKind"] == ls_kind and r["roleEntry"]["roleName"] == role_name] - if len(role) == 0: - return [] - users = [] + permissions = client.list_permissions() memberships = client.list_memberships() - userDict = {} - for u in ld_users: - userDict[u["id"]] = u - for m in memberships: - if m["group_id"] == role[0]["id"]: - users.append(userDict[m["user_id"]]) - acas_users = map(ld_user_to_acas_user_code_table, users) + projects = client.projects() + user_projects = {} + for p in permissions: + for g in groups: + if p["group_id"] == g["id"]: + for proj in projects: + if p["project_id"] == proj.id: + if proj.name in user_projects: + user_projects[proj.name]["granting_groups"].append(g["name"]) + else: + user_projects[proj.name] = {"id":proj.id, "granting_groups": [g["name"]], "ld_users": []} + for m in memberships: + if m["group_id"] == g["id"]: + for u in ld_users: + if u["id"] == m["user_id"]: + user_projects[proj.name]["ld_users"].append(u) + if ls_kind in user_projects: + acas_users = map(ld_user_to_acas_user_code_table, user_projects[ls_kind]["ld_users"]) + else: + acas_users = [] return acas_users def ld_user_to_acas_user(ld_user, roles): @@ -156,13 +165,35 @@ def get_user(client, username): user = client.get_user(username) permissions = client.list_permissions() memberships = client.list_memberships() + projects = client.projects() groups = client.list_groups() user_memberships = [m for m in memberships if m['user_id'] == user['id']] user_groups = [next(g for g in groups if g["id"]==m["group_id"]) for m in user_memberships] - roles = map(ld_group_to_acas_role, user_groups) + user_projects = {} + for p in permissions: + for g in user_groups: + if p["group_id"] == g["id"]: + for proj in projects: + if p["project_id"] == proj.id: + if proj.name in user_projects: + user_projects[proj.name]["granting_groups"].append(g["name"]) + else: + user_projects[proj.name] = {"id":proj.id, "granting_groups": [g["name"]]} + roles = [] + for proj, data in user_projects.iteritems(): + roles.append({ + "id": data["id"], + "roleEntry": { + 'id': data["id"], + 'lsType': 'Project', + 'lsKind': proj, + 'lsTypeAndKind': "Project_" + proj, + 'roleDescription': "Permission to Project granted by Live Design group(s): "+', '.join("'{0}'".format(g) for g in data["granting_groups"]), + 'roleName': "User", + 'version': 0 + } + }) acas_user = ld_user_to_acas_user(user, roles) - # user = {"id":1,"username":"bob","email":"bob@mcneilco.com","firstName":"Ham","lastName":"Cheese","roles":[{"id":2,"roleEntry":{"id":1,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-USERS autocreated by ACAS","roleName":"ROLE_ACAS-USERS","version":0},"version":0},{"id":1,"roleEntry":{"id":2,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-ADMINS autocreated by ACAS","roleName":"ROLE_ACAS-ADMINS","version":0},"version":0},{"id":4,"roleEntry":{"id":4,"lsKind":"CmpdReg","lsType":"System","lsTypeAndKind":"System_CmpdReg","roleDescription":"ROLE_CMPDREG-ADMINS autocreated by ACAS","roleName":"ROLE_CMPDREG-ADMINS","version":0},"version":0},{"id":3,"roleEntry":{"id":5,"lsKind":"ACAS","lsType":"System","lsTypeAndKind":"System_ACAS","roleDescription":"ROLE_ACAS-CROSS-PROJECT-LOADER autocreated by ACAS","roleName":"ROLE_ACAS-CROSS-PROJECT-LOADER","version":0},"version":0},{"id":5,"roleEntry":{"id":3,"lsKind":"CmpdReg","lsType":"System","lsTypeAndKind":"System_CmpdReg","roleDescription":"ROLE_CMPDREG-USERS autocreated by ACAS","roleName":"ROLE_CMPDREG-USERS","version":0},"version":0},{"id":6,"roleEntry":{"id":6,"lsKind":"PROJ-00000001","lsType":"Project","lsTypeAndKind":"Project_PROJ-00000001","roleDescription":"User autocreated by ACAS","roleName":"User","version":0},"version":0}]} - return acas_user def get_projects(client): From 34ef3a8fffa106adca19b61468dbdbf8582046d9 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 2 Jul 2019 10:12:43 -0700 Subject: [PATCH 449/576] roo sparesely fills null values on object so need to test for their existance during search --- .../src/server/routes/CodeTablesAdminRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee b/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee index 970fda4f8..764ad85a7 100644 --- a/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee +++ b/modules/CodeTablesAdmin/src/server/routes/CodeTablesAdminRoutes.coffee @@ -106,7 +106,7 @@ exports.searchCodeTablesEntities = (req, resp) -> searchResults = results else for r in results - if r.code.toLowerCase().indexOf(searchTerm) >= 0 || r.comments.toLowerCase().indexOf(searchTerm) >= 0 || r.description.toLowerCase().indexOf(searchTerm) >= 0 || r.name.toLowerCase().indexOf(searchTerm) >= 0 + if (r.code? && r.code.toLowerCase().indexOf(searchTerm) >= 0) || (r.comments? && r.comments.toLowerCase().indexOf(searchTerm) >= 0) || (r.description? && r.description.toLowerCase().indexOf(searchTerm) >= 0) || (r.name? && r.name.toLowerCase().indexOf(searchTerm) >= 0) searchResults.push(r) resp.json searchResults From ddd7d9b97e639fbe64ab0cc58411969818b4d18f Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 2 Jul 2019 11:08:49 -0700 Subject: [PATCH 450/576] add askterisk for required field in code table admin module --- .../CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html index ee252bc18..c0b4ba05e 100644 --- a/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html +++ b/modules/CodeTablesAdmin/src/client/AbstractCodeTablesAdminView.html @@ -1,7 +1,7 @@ + + diff --git a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee index d4ebda39b..23d28aca3 100644 --- a/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee +++ b/modules/CmpdRegBulkLoader/src/server/routes/CmpdRegBulkLoaderRoutes.coffee @@ -3,6 +3,7 @@ path = require 'path' exports.setupAPIRoutes = (app) -> app.post '/api/cmpdRegBulkLoader', exports.postAssignedProperties app.post '/api/cmpdRegBulkLoader/registerCmpds', exports.registerCmpds + app.post '/api/cmpdRegBulkLoader/validateCmpds', exports.validateCmpds app.post '/api/cmpdRegBulkLoader/validationProperties', exports.validationProperties app.get '/api/cmpdRegBulkLoader/getFilesToPurge', exports.getFilesToPurge app.post '/api/cmpdRegBulkLoader/purgeFile', exports.purgeFile @@ -15,6 +16,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/cmpdRegBulkLoader/readSDF', loginRoutes.ensureAuthenticated, exports.cmpdRegBulkLoaderReadSdf app.post '/api/cmpdRegBulkLoader/saveTemplate', loginRoutes.ensureAuthenticated, exports.saveTemplate app.post '/api/cmpdRegBulkLoader/registerCmpds', loginRoutes.ensureAuthenticated, exports.registerCmpds + app.post '/api/cmpdRegBulkLoader/validateCmpds', loginRoutes.ensureAuthenticated, exports.validateCmpds app.post '/api/cmpdRegBulkLoader', loginRoutes.ensureAuthenticated, exports.postAssignedProperties app.post '/api/cmpdRegBulkLoader/checkFileDependencies', loginRoutes.ensureAuthenticated, exports.checkFileDependencies app.post '/api/cmpdRegBulkLoader/purgeFile', loginRoutes.ensureAuthenticated, exports.purgeFile @@ -162,6 +164,10 @@ exports.getScientistsInternal = (callback) -> loginRoutes.getAuthorsInternal {additionalCodeType: 'compound', additionalCodeKind: 'scientist', roleName: roleName}, (statusCode, authors) => callback authors +exports.validateCmpds = (req, resp) -> + req.body.validate = true + exports.registerCmpds(req, resp) + exports.registerCmpds = (req, resp) -> req.connection.setTimeout 6000000 createSummaryZip = (fileName, json) -> @@ -267,7 +273,17 @@ exports.registerCmpds = (req, resp) -> console.log "Can't find or create bulkload folder: " + bulkLoadFolder callback "error", resp else - fs.rename oldPath, newPath, (err) -> + if req.body.validate + # new node has fs.copyFile but we don't currently:( + fsFuct = (oldPath, newPath, callback) -> + stream = fs.createReadStream(oldPath).pipe fs.createWriteStream(newPath) + stream.on 'error', (err) -> + callback err + stream.on 'close', -> + callback null + else + fsFuct = fs.rename + fsFuct oldPath, newPath, (err) -> if err? console.log err callback "error", resp From 9f00755bab0e96a84619e321f2a1e367c69abe15 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 8 Oct 2019 09:57:17 -0700 Subject: [PATCH 465/576] fixes #594 Add route for experiment metada by date start/date end or hours back --- .../src/server/r/GetExperimentalMetadata.R | 59 +++++++++++++++++++ .../routes/ExperimentServiceRoutes.coffee | 9 +++ 2 files changed, 68 insertions(+) create mode 100644 modules/ServerAPI/src/server/r/GetExperimentalMetadata.R diff --git a/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R b/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R new file mode 100644 index 000000000..002ef7ec6 --- /dev/null +++ b/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R @@ -0,0 +1,59 @@ +# The next line is used by PrepareConfigFiles to include this file as a route in rapache, do not modify unless you intend to modify rapache routes (it can be anywhere in the files though) +# ROUTE: /getExperimentalMetadata + +library(racas) + +# Sample usage: +# curl "http://localhost:1080/r-services-api/getExperimentalMetadata?startTime=2019-09-05%2020:00:00&endTime=2019-09-05%2021:00:00" +# curl "http://localhost:1080/r-services-api/getExperimentalMetadata?hoursBack=24" + +findExperiments <- function(GET) { + + startTime = NA + endTime = NA + hoursBack = NA + + if (("hoursBack" %in% names(GET)) && (!is.na(GET$hoursBack))) { + hoursBack = as.integer(URLdecode(GET$hoursBack)) + } else { + if (!is.na(GET$startTime)) { + startTime = URLdecode(GET$startTime) + } + if (("endTime" %in% names(GET)) && (!is.na(GET$endTime))) { + endTime = URLdecode(GET$endTime) + } + } + + queryString = paste0("select e.id, e.code_name as current_experiment, el.label_text as experiment_name, e.recorded_date, ev_prev.code_value as previous_experiment, ev_proj.code_value ", + " from experiment e ", + " join experiment_label el on e.id = el.experiment_id and el.ls_type = 'name' and el.ls_kind = 'experiment name' and el.ignored = '0' and el.deleted = '0' ", + " left join experiment_state es on es.experiment_id = e.id and es.ls_type = 'metadata' and es.ls_kind = 'experiment metadata' and es.ignored = '0' and es.deleted = '0' ", + " left join experiment_value ev_prev on ev_prev.experiment_state_id = es.id and ev_prev.ls_type = 'codeValue' and ev_prev.ls_kind = 'previous experiment code' and ev_prev.ignored = '0' and ev_prev.deleted = '0' ", + " left join experiment_value ev_proj on ev_proj.experiment_state_id = es.id and ev_proj.ls_type = 'codeValue' and ev_proj.ls_kind = 'project' and ev_proj.ignored = '0' and ev_proj.deleted = '0' " + ) + + if(!is.na(hoursBack)) { + currentTime <- Sys.time() + hoursBackTime <- currentTime - (hoursBack * 60 * 60) + attributes(hoursBackTime)$tzone <- "UTC" + queryString = paste0(queryString, " where e.recorded_date > '",hoursBackTime,"' "); + } else { + if (!is.na(startTime) && !is.na(endTime)) { + queryString = paste0(queryString, " where e.recorded_date between '",startTime,"' and '",endTime,"'"); + } else if (!is.na(startTime)) { + queryString = paste0(queryString, " where e.recorded_date > '",startTime,"' "); + } else if (!is.na(endTime)) { + queryString = paste0(queryString, " where e.recorded_date < '",endTime,"' "); + } + } + + results <- query(queryString) + names(results) <- tolower(names(results)) + return (jsonlite::toJSON(results)) +} + +resultList <- findExperiments(GET); + +cat(resultList); + +DONE \ No newline at end of file diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index 0ebc3af33..d1a44bd56 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -24,6 +24,7 @@ exports.setupAPIRoutes = (app) -> app.post '/api/experiments/getExperimentCodeByLabel/:exptType/:exptKind', exports.getExperimentCodeByLabel app.post '/api/bulkPostExperiments', exports.bulkPostExperiments app.put '/api/bulkPutExperiments', exports.bulkPutExperiments + app.get '/api/getExperimentalMetadata', exports.getExperimentalMetadata exports.setupRoutes = (app, loginRoutes) -> app.get '/api/experiments/codename/:code', loginRoutes.ensureAuthenticated, exports.experimentByCodename @@ -52,6 +53,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/experiments/getExperimentCodeByLabel/:exptType/:exptKind', loginRoutes.ensureAuthenticated, exports.getExperimentCodeByLabel app.post '/api/bulkPostExperiments', loginRoutes.ensureAuthenticated, exports.bulkPostExperiments app.put '/api/bulkPostExperiments', loginRoutes.ensureAuthenticated, exports.bulkPutExperiments + app.get '/api/getExperimentalMetadata', loginRoutes.ensureAuthenticated, exports.getExperimentalMetadata serverUtilityFunctions = require './ServerUtilityFunctions.js' csUtilities = require '../src/javascripts/ServerAPI/CustomerSpecificServerFunctions.js' @@ -1035,3 +1037,10 @@ exports.getExperimentCodeByLabel = (req, resp) -> resp.statusCode = 500 resp.json json.errorMessages ) + +exports.getExperimentalMetadata = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + redirectQuery = req._parsedUrl.query + rapacheCall = config.all.client.service.rapache.fullpath + '/getExperimentalMetadata?' + redirectQuery + req.pipe(request(rapacheCall)).pipe(resp) From 2f6739ca47fbcc8f4ee64b288006478cb89e4718 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 9 Oct 2019 13:15:56 -0700 Subject: [PATCH 466/576] #596 Add ignored field to project service --- .../ServerAPI/src/server/python/acas_ldclient/acasldclient.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index 6adb46330..6c3752f0b 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -218,6 +218,7 @@ def ld_project_to_acas(ld_project): 'code': ld_project.name, 'alias': ld_project.name, 'active': True if ld_project.active == "Y" else False, + 'ignored': False if ld_project.active == "Y" else True, 'isRestricted': ld_project.restricted, 'name': ld_project.name } From a4d8974e11750ce10e6a50b8786307baba03753d Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 14 Oct 2019 16:08:11 -0700 Subject: [PATCH 467/576] Expose LsThing advanced search route --- .../server/routes/ThingServiceRoutes.coffee | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee index 753b48db1..71b9527e6 100644 --- a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee @@ -11,6 +11,7 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.post '/api/validateName', exports.validateName app.get '/api/getAssembliesFromComponent/:lsType/:lsKind/:componentCode', exports.getAssemblies app.get '/api/genericSearch/things/:searchTerm', exports.genericThingSearch + app.post '/api/advancedSearch/things/:lsType/:lsKind', exports.advancedThingSearch app.get '/api/getThingThingItxsByFirstThing/:firstThingId', exports.getThingThingItxsByFirstThing app.get '/api/getThingThingItxsBySecondThing/:secondThingId', exports.getThingThingItxsBySecondThing app.get '/api/getThingThingItxsByFirstThing/:lsType/:lsKind/:firstThingId', exports.getThingThingItxsByFirstThingAndItxTypeKind @@ -38,6 +39,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/validateName', loginRoutes.ensureAuthenticated, exports.validateName app.get '/api/getAssembliesFromComponent/:lsType/:lsKind/:componentCode', loginRoutes.ensureAuthenticated, exports.getAssemblies app.get '/api/genericSearch/things/:searchTerm', loginRoutes.ensureAuthenticated, exports.genericThingSearch + app.post '/api/advancedSearch/things/:lsType/:lsKind', loginRoutes.ensureAuthenticated, exports.advancedThingSearch app.get '/api/getThingThingItxsByFirstThing/:firstThingId', loginRoutes.ensureAuthenticated, exports.getThingThingItxsByFirstThing app.get '/api/getThingThingItxsBySecondThing/:secondThingId', loginRoutes.ensureAuthenticated, exports.getThingThingItxsBySecondThing app.get '/api/getThingThingItxsByFirstThing/:lsType/:lsKind/:firstThingId', loginRoutes.ensureAuthenticated, exports.getThingThingItxsByFirstThingAndItxTypeKind @@ -607,6 +609,47 @@ exports.genericThingSearch = (req, resp) -> serverUtilityFunctions = require './ServerUtilityFunctions.js' serverUtilityFunctions.getFromACASServer(baseurl, resp) +exports.advancedThingSearch = (req, resp) -> + console.log "advanced thing search" + console.log req.query.testMode + console.log global.specRunnerTestmode + if req.query.testMode is true or global.specRunnerTestmode is true + resp.end JSON.stringify "Stubs mode not implemented yet" + else + console.log "search req body" + console.log req.body + exports.advancedThingSearchInternal req.body, req.query.format, (results) => + if typeof response is "string" and results.indexOf("error") > -1 + resp.statusCode = 500 + resp.end results + else + resp.json results + +exports.advancedThingSearchInternal = (input, format, callback) -> + config = require '../conf/compiled/conf.js' + baseurl = config.all.client.service.persistence.fullpath+"lsthings/genericBrowserSearch" + if format? + baseurl += "?with=#{format}" + console.log "advanced thing search baseurl" + console.log baseurl + requestOptions = + method: 'POST' + url: baseurl + body: input + json: true + request requestOptions, (error, response, object) -> + if !error + if response.statusCode == 500 + callback object, response + else + callback object, null + else + console.log 'got ajax error trying to run advancedThingSearch' + console.log error + console.log json + console.log response + callback object, error + exports.getProjectCodesFromNamesOrCodes = (codeRequest, callback) -> #TODO: real implementation console.log "got to getProjectCodesFromNamesOrCodes" From 8db1b4d68d6ccecd6515e5c2781dfe67dd5c746d Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 16 Oct 2019 12:33:35 -0700 Subject: [PATCH 468/576] adding protocol name field, check for no results, ignore deleted experiments, set is_reload field, order by most recent experiment first --- .../src/server/r/GetExperimentalMetadata.R | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R b/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R index 002ef7ec6..907542a26 100644 --- a/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R +++ b/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R @@ -2,7 +2,7 @@ # ROUTE: /getExperimentalMetadata library(racas) - +library(data.table) # Sample usage: # curl "http://localhost:1080/r-services-api/getExperimentalMetadata?startTime=2019-09-05%2020:00:00&endTime=2019-09-05%2021:00:00" # curl "http://localhost:1080/r-services-api/getExperimentalMetadata?hoursBack=24" @@ -24,8 +24,10 @@ findExperiments <- function(GET) { } } - queryString = paste0("select e.id, e.code_name as current_experiment, el.label_text as experiment_name, e.recorded_date, ev_prev.code_value as previous_experiment, ev_proj.code_value ", - " from experiment e ", + queryString = paste0("select e.id, e.code_name as current_experiment, el.label_text as experiment_name, e.recorded_date, ev_prev.code_value as previous_experiment, ev_proj.code_value project, pl.label_text as protocol_name ", + " from protocol p ", + " join protocol_label pl on p.deleted = '0' and p.ignored = '0' and p.id = pl.protocol_id and pl.ls_type = 'name' and pl.ls_kind = 'protocol name' and pl.ignored = '0' and pl.deleted = '0' ", + " join experiment e on p.id = e.protocol_id and e.deleted = '0' and e.ignored = '0' ", " join experiment_label el on e.id = el.experiment_id and el.ls_type = 'name' and el.ls_kind = 'experiment name' and el.ignored = '0' and el.deleted = '0' ", " left join experiment_state es on es.experiment_id = e.id and es.ls_type = 'metadata' and es.ls_kind = 'experiment metadata' and es.ignored = '0' and es.deleted = '0' ", " left join experiment_value ev_prev on ev_prev.experiment_state_id = es.id and ev_prev.ls_type = 'codeValue' and ev_prev.ls_kind = 'previous experiment code' and ev_prev.ignored = '0' and ev_prev.deleted = '0' ", @@ -47,9 +49,15 @@ findExperiments <- function(GET) { } } - results <- query(queryString) - names(results) <- tolower(names(results)) - return (jsonlite::toJSON(results)) + results <- as.data.table(query(queryString)) + if(nrow(results) > 0) { + setnames(results, tolower(names(results))) + results[ , is_reload := any(!is.na(previous_experiment)), by = id] + results[ , previous_experiment := NULL] + results <- unique(results) + setorder(results, -recorded_date) + } + return (jsonlite::toJSON(results, auto_unbox = TRUE)) } resultList <- findExperiments(GET); From 5c3cfe31ff25d3f5cb5e1b5191aad5366c205b34 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 16 Oct 2019 12:34:28 -0700 Subject: [PATCH 469/576] add mail service --- modules/ServerAPI/src/server/r/sendMail.R | 17 +++++++++++++++++ .../src/server/routes/EmailRoutes.coffee | 12 ++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 modules/ServerAPI/src/server/r/sendMail.R create mode 100644 modules/ServerAPI/src/server/routes/EmailRoutes.coffee diff --git a/modules/ServerAPI/src/server/r/sendMail.R b/modules/ServerAPI/src/server/r/sendMail.R new file mode 100644 index 000000000..cf5161d64 --- /dev/null +++ b/modules/ServerAPI/src/server/r/sendMail.R @@ -0,0 +1,17 @@ +# The next line is used by PrepareConfigFiles to include this file as a route in rapache, do not modify unless you intend to modify rapache routes (it can be anywhere in the files though) +# ROUTE: /sendMail + +library(racas) +library(mailR) + +sendMail <- function(request) { + request <- jsonlite::fromJSON(request) + request$send <- TRUE + request$debug <- TRUE + do.call(mailR::send.mail, request) + return(TRUE) +} + +postData <- rawToChar(receiveBin(-1)) +cat(jsonlite::toJSON(sendMail(postData), auto_unbox = TRUE)) +DONE \ No newline at end of file diff --git a/modules/ServerAPI/src/server/routes/EmailRoutes.coffee b/modules/ServerAPI/src/server/routes/EmailRoutes.coffee new file mode 100644 index 000000000..551d7f3c7 --- /dev/null +++ b/modules/ServerAPI/src/server/routes/EmailRoutes.coffee @@ -0,0 +1,12 @@ +exports.setupAPIRoutes = (app) -> + app.post '/api/sendMail', exports.sendMail + +exports.setupRoutes = (app, loginRoutes) -> + app.post '/api/sendMail', loginRoutes.ensureAuthenticated, exports.sendMail + +exports.sendMail = (req, resp) -> + request = require 'request' + config = require '../conf/compiled/conf.js' + redirectQuery = req._parsedUrl.query + rapacheCall = config.all.client.service.rapache.fullpath + '/sendMail?' + redirectQuery + req.pipe(request(rapacheCall)).pipe(resp) From 886e29620973d756d2a0823ee3779d386eb121f5 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 16 Oct 2019 16:08:19 -0700 Subject: [PATCH 470/576] re-arranging files --- modules/ServerAPI/src/server/{r => }/GetExperimentalMetadata.R | 0 modules/ServerAPI/src/server/{r => }/sendMail.R | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename modules/ServerAPI/src/server/{r => }/GetExperimentalMetadata.R (100%) rename modules/ServerAPI/src/server/{r => }/sendMail.R (100%) diff --git a/modules/ServerAPI/src/server/r/GetExperimentalMetadata.R b/modules/ServerAPI/src/server/GetExperimentalMetadata.R similarity index 100% rename from modules/ServerAPI/src/server/r/GetExperimentalMetadata.R rename to modules/ServerAPI/src/server/GetExperimentalMetadata.R diff --git a/modules/ServerAPI/src/server/r/sendMail.R b/modules/ServerAPI/src/server/sendMail.R similarity index 100% rename from modules/ServerAPI/src/server/r/sendMail.R rename to modules/ServerAPI/src/server/sendMail.R From 491e5756fe5f11d9d45ccb80d7c83494771ee1f2 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 17 Oct 2019 19:55:06 -0700 Subject: [PATCH 471/576] Switch Itx formfield to use regular pickBestLabel --- modules/Components/src/client/ACASFormFields.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index c968b14cb..61d096f3f 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -401,7 +401,7 @@ class window.ACASFormLSThingInteractionFieldController extends ACASFormAbstractF if @extendedLabel? and @extendedLabel labelText = labels.getExtendedNameText() else - labelText = labels.pickBestNonEmptyLabel().get('labelText') + labelText = labels.pickBestLabel().get('labelText') @thingSelectController.setSelectedCode code: @getModel().getItxThing().codeName label: labelText From 1263604e0a04330377031b9bcd4c4c69ad11eee6 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 22 Oct 2019 17:12:33 -0700 Subject: [PATCH 472/576] Add Multi Interaction form field widget --- .../client/ACASFormMultiInteraction.coffee | 97 +++++++++++++++++++ .../src/client/ACASFormMultiInteraction.css | 15 +++ .../client/ACASFormMultiInteractionView.html | 17 ++++ .../src/client/AbstractFormController.coffee | 13 ++- modules/ServerAPI/src/client/Thing.coffee | 59 ++++++++--- 5 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 modules/Components/src/client/ACASFormMultiInteraction.coffee create mode 100644 modules/Components/src/client/ACASFormMultiInteraction.css create mode 100644 modules/Components/src/client/ACASFormMultiInteractionView.html diff --git a/modules/Components/src/client/ACASFormMultiInteraction.coffee b/modules/Components/src/client/ACASFormMultiInteraction.coffee new file mode 100644 index 000000000..c28f75521 --- /dev/null +++ b/modules/Components/src/client/ACASFormMultiInteraction.coffee @@ -0,0 +1,97 @@ +class window.ACASFormMultiInteractionController extends Backbone.View + ### + Launched by ACASFormMultiInteractionListController to control one element/row in the list + ### + + template: _.template($("#ACASFormMultiInteractionView").html()) + + events: -> + "click .bv_removeInteractionButton": "handleRemoveInteractionButtonClicked" + + initialize: -> + options = + modelKey: @options.interactionKey + thingType: @options.thingType + thingKind: @options.thingKind + labelType: @options.labelType + queryUrl: @options.queryUrl + placeholder: @options.placeholder + extendedLabel: @options.extendedLabel + inputClass: @options.inputClass + formLabel: @options.formLabel + required: @options.required + thingRef: @options.thingRef + insertUnassigned: @options.insertUnassigned + @interactionController = new ACASFormLSThingInteractionFieldController options + + render: -> + $(@el).empty() + $(@el).html @template() + @$('.bv_multiInteraction').html @interactionController.render().el + @interactionController.renderModelContent() + @ + + handleRemoveInteractionButtonClicked: -> + @interactionController.setEmptyValue() + $(@el).hide() + + +class window.ACASFormMultiInteractionListController extends ACASFormAbstractFieldController + ### + Launching controller must instantiate with the full field conf including modelDefaults, not just the fieldDefinition. + Controls a flexible-length list of LsThingInteraction input fields within ACASFormLSThingInteractionFieldControllers with an add button. + ### + template: _.template($("#ACASFormMultiInteractionListView").html()) + + events: -> + "click .bv_addInteractionButton": "handleAddInteractionButtonClicked" + + initialize: -> + super() + @opts = @options + if @opts.firstItx + @itxClass = FirstThingItx + @thingItxRef = 'firstLsThings' + else + @itxClass = SecondThingItx + @thingItxRef = 'secondLsThings' + + render: => + $(@el).empty() + $(@el).html @template() + @ + + renderModelContent: -> + @render() + multiInteractions = @thingRef.get(@thingItxRef).getItxByTypeAndKind(@opts.modelDefaults.itxType, @opts.modelDefaults.itxKind) + _.each multiInteractions, (interaction) => + @addOneInteraction(interaction) + + handleAddInteractionButtonClicked: => + @addNewInteraction() + + addNewInteraction: (skipAmDirtyTrigger) => + keyBase = @modelKey + newModel = new @itxClass + lsType: @opts.modelDefaults.itxType + lsKind: @opts.modelDefaults.itxKind + currentMultiInteractions = @thingRef.get(@thingItxRef).filter (interaction) -> + interaction.has('key') and (interaction.get('key').indexOf(keyBase) > -1) + newKey = keyBase + currentMultiInteractions.length + newModel.set key: newKey + @thingRef.set newKey, newModel + @thingRef.get(@thingItxRef).add newModel + @addOneInteraction(newModel) + unless skipAmDirtyTrigger is true + newModel.trigger 'amDirty' + + addOneInteraction: (interaction) -> + interactionOpts = @opts + interactionOpts.interactionKey = interaction.get('key') + multiInteractionController = new ACASFormMultiInteractionController interactionOpts + @$('.bv_multiInteractions').append multiInteractionController.render().el + multiInteractionController.on 'updateState', => + @trigger 'updateState' + + + diff --git a/modules/Components/src/client/ACASFormMultiInteraction.css b/modules/Components/src/client/ACASFormMultiInteraction.css new file mode 100644 index 000000000..b629ec8d7 --- /dev/null +++ b/modules/Components/src/client/ACASFormMultiInteraction.css @@ -0,0 +1,15 @@ +.multiInteractionListWell { + margin-bottom: 0; +} + +.bv_removeInteractionButton { + margin-left: 5px; + padding-right: 8px; + padding-left: 8px; +} + +.bv_addInteractionButton { + padding-right: 8px; + padding-left: 8px; + margin-left: 388px; +} \ No newline at end of file diff --git a/modules/Components/src/client/ACASFormMultiInteractionView.html b/modules/Components/src/client/ACASFormMultiInteractionView.html new file mode 100644 index 000000000..9f8e0cfb3 --- /dev/null +++ b/modules/Components/src/client/ACASFormMultiInteractionView.html @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 3b4034b12..758a5e605 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -124,7 +124,12 @@ class window.AbstractThingFormController extends AbstractFormController fDefs = [] if fieldDefs.labels? then fDefs = fDefs.concat fieldDefs.labels if fieldDefs.values? then fDefs = fDefs.concat fieldDefs.values - if fieldDefs.firstLsThingItxs? then fDefs = fDefs.concat fieldDefs.firstLsThingItxs + if fieldDefs.firstLsThingItxs? + # Tag any firstItx fieldDefinitions with "firstItx" so the controllers know which direction of interaction to create + firstDefs = _.map(fieldDefs.firstLsThingItxs, (fdef) => + fdef.fieldSettings.firstItx = true + return fdef) + fDefs = fDefs.concat firstDefs if fieldDefs.secondLsThingItxs? then fDefs = fDefs.concat fieldDefs.secondLsThingItxs for field in fDefs @@ -166,7 +171,11 @@ class window.AbstractThingFormController extends AbstractFormController opts.thingKind = field.fieldSettings.thingKind opts.queryUrl = field.fieldSettings.queryUrl opts.labelType = field.fieldSettings.labelType - newField = new ACASFormLSThingInteractionFieldController opts + opts.firstItx = field.fieldSettings.firstItx + if field.multiple? and field.multiple + newField = new ACASFormMultiInteractionListController opts + else + newField = new ACASFormLSThingInteractionFieldController opts when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts diff --git a/modules/ServerAPI/src/client/Thing.coffee b/modules/ServerAPI/src/client/Thing.coffee index be6d204f8..a3052815d 100644 --- a/modules/ServerAPI/src/client/Thing.coffee +++ b/modules/ServerAPI/src/client/Thing.coffee @@ -92,21 +92,20 @@ class window.Thing extends Backbone.Model if attsToSave.secondLsThings.length == 0 delete attsToSave.secondLsThings - if @lsProperties.defaultLabels? + if @lsProperties.defaultLabels? and @lsProperties.defaultLabels.length > 0 for dLabel in @lsProperties.defaultLabels delete attsToSave[dLabel.key] - if @lsProperties.defaultFirstLsThingItx? + if @lsProperties.defaultFirstLsThingItx? and @lsProperties.defaultFirstLsThingItx.length > 0 for itx in @lsProperties.defaultFirstLsThingItx delete attsToSave[itx.key] - if @lsProperties.defaultSecondLsThingItx? + if @lsProperties.defaultSecondLsThingItx? and @lsProperties.defaultSecondLsThingItx.length > 0 console.log "deleting empty 2nd ls thing itxs" - for itx in @lsProperties.defaultSecondLsThingItx delete attsToSave[itx.key] - if @lsProperties.defaultValues? + if @lsProperties.defaultValues? and @lsProperties.defaultValues.length > 0 for dValue in @lsProperties.defaultValues if attsToSave[dValue.key]? val = attsToSave[dValue.key].get('value') @@ -277,18 +276,56 @@ class window.Thing extends Backbone.Model # add key as attribute of model if @lsProperties.defaultFirstLsThingItx? for itx in @lsProperties.defaultFirstLsThingItx - #TODO fix - broken when have more than one itx that has same type/kind - thingItx = @get('firstLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind - @set itx.key, thingItx + if itx.multiple? and itx.multiple + thingItxs = @get('firstLsThings').getItxByTypeAndKind itx.itxType, itx.itxKind + if thingItxs.length > 0 + counter = 0 + _.each thingItxs, (thingItx) => + if !thingItx.has('key') + thingItxKey = itx.key + counter + counter++ + @set thingItxKey, thingItx + thingItx.set key: thingItxKey + #@listenTo thingItx, 'createNewLabel', @createNewLabel + else + thingItx = @get('firstLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind + newKey = itx.key + 0 + @set newKey, thingItx + thingItx.set key: newKey + else + thingItx = @get('firstLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind + #@listenTo newLabel, 'createNewLabel', @createNewLabel + @set itx.key, thingItx + # if newLabel.get('preferred') is undefined + thingItx.set key: itx.key createDefaultSecondLsThingItx: => # loop over defaultSecondLsThingItx # add key as attribute of model if @lsProperties.defaultSecondLsThingItx? for itx in @lsProperties.defaultSecondLsThingItx - #TODO fix - broken when have more than one itx that has same type/kind - thingItx = @get('secondLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind - @set itx.key, thingItx + if itx.multiple? and itx.multiple + thingItxs = @get('secondLsThings').getItxByTypeAndKind itx.itxType, itx.itxKind + if thingItxs.length > 0 + counter = 0 + _.each thingItxs, (thingItx) => + if !thingItx.has('key') + thingItxKey = itx.key + counter + counter++ + @set thingItxKey, thingItx + thingItx.set key: thingItxKey + #@listenTo thingItx, 'createNewLabel', @createNewLabel + else + thingItx = @get('secondLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind + newKey = itx.key + 0 + @set newKey, thingItx + thingItx.set key: newKey + else + thingItx = @get('secondLsThings').getOrCreateItxByTypeAndKind itx.itxType, itx.itxKind + #@listenTo newLabel, 'createNewLabel', @createNewLabel + @set itx.key, thingItx + # if newLabel.get('preferred') is undefined + thingItx.set key: itx.key getAnalyticalFiles: (fileTypes) => #TODO: rename from analytical files to attachFiles or something more generic #get list of possible kinds of analytical files From 195325b1f2f40c09b2f20606a4e816bfe2320b0d Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 22 Oct 2019 17:13:06 -0700 Subject: [PATCH 473/576] Allow disabled grey out for ACASFormLSHTMLClobValueFieldController --- modules/Components/src/client/ACASFormFields.coffee | 4 +++- modules/Components/src/client/ACASFormFields.css | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 modules/Components/src/client/ACASFormFields.css diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 61d096f3f..346d1e980 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -489,13 +489,15 @@ class window.ACASFormLSHTMLClobValueFieldController extends ACASFormAbstractFiel if @contentToLoad? @editor.setContent @contentToLoad if @disableEditor? - @editor.getBody().setAttribute('contenteditable', @disableEditor) + @editor.getBody().setAttribute('contenteditable', !@disableEditor) + @editor.getBody().setAttribute('disabled', @disableEditor) editor.on 'change', (e) => @textChanged editor.getContent() disableInput: -> if @editor? @editor.getBody().setAttribute('contenteditable', false) + @editor.getBody().setAttribute('disabled', true) else @disableEditor = true diff --git a/modules/Components/src/client/ACASFormFields.css b/modules/Components/src/client/ACASFormFields.css new file mode 100644 index 000000000..e7ec46ce6 --- /dev/null +++ b/modules/Components/src/client/ACASFormFields.css @@ -0,0 +1,4 @@ +.mce-content-body[disabled] { + cursor: not-allowed; + background-color: #eeeeee; +} \ No newline at end of file From 70202713828ed55ccd1d1c779269ed179368e2fb Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 22 Oct 2019 17:13:56 -0700 Subject: [PATCH 474/576] Controversial: Sidestep load order issue with handsontable. Not an actual fix though. --- .../src/client/ACASFormStateTable.coffee | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 176aaeee2..4280da918 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -406,10 +406,11 @@ class window.ACASFormStateTableController extends Backbone.View return newValue disableInput: -> - @hot.updateSettings - readOnly: true - contextMenu: false - comments: false + if @hot? + @hot.updateSettings + readOnly: true + contextMenu: false + comments: false #Other options I decided not to use # disableVisualSelection: true # manualColumnResize: false @@ -420,10 +421,11 @@ class window.ACASFormStateTableController extends Backbone.View contextMenu = @tableDef.contextMenu else contextMenu = true - @hot.updateSettings - readOnly: false - contextMenu: contextMenu - comments: true + if @hot? + @hot.updateSettings + readOnly: false + contextMenu: contextMenu + comments: true #Other options I decided not to use # disableVisualSelection: false # manualColumnResize: true From 29b19abaaa2a4bd2bc068ac82dfd9fe67174b1ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2019 14:18:40 +0000 Subject: [PATCH 475/576] Bump csv-parse from 1.3.3 to 4.6.5 Bumps [csv-parse](https://github.com/wdavidw/node-csv-parse) from 1.3.3 to 4.6.5. - [Release notes](https://github.com/wdavidw/node-csv-parse/releases) - [Changelog](https://github.com/adaltas/node-csv-parse/blob/master/CHANGELOG.md) - [Commits](https://github.com/wdavidw/node-csv-parse/compare/v1.3.3...v4.6.5) Signed-off-by: dependabot[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80d6d619c..3ef94cd28 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "cookie-parser": "^1.4.3", "connect-flash": "~0.1.0", "cron": "1.3.0", - "csv-parse": "^1.1.1", + "csv-parse": "^4.6.5", "each": "0.6.1", "exports-loader": "0.6.4", "express": "3.0.2", From 63bdb9328574dbf8fc9b67949097b9dc2e76c3cc Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 30 Oct 2019 14:27:53 -0700 Subject: [PATCH 476/576] fixes #600 plot log y scale and log plot window --- modules/CurveAnalysis/src/server/renderCurve.R | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/CurveAnalysis/src/server/renderCurve.R b/modules/CurveAnalysis/src/server/renderCurve.R index b9a0a8224..c825732b3 100644 --- a/modules/CurveAnalysis/src/server/renderCurve.R +++ b/modules/CurveAnalysis/src/server/renderCurve.R @@ -33,15 +33,19 @@ renderCurve <- function(getParams) { data$parameters$fitted_slope <- -data$parameters$fitted_hillslope[fittedHillSlopes] } + logDose <- TRUE + if(fitData[1]$renderingHint %in% c("Michaelis-Menten", "Substrate Inhibition", "Scatter", "Scatter Log-y")) logDose <- FALSE + logResponse <- FALSE + if(fitData[1]$renderingHint %in% c("Scatter Log-y","Scatter Log-x,y")) logResponse <- TRUE #Get Protocol Curve Display Min and Max for first curve in list if(any(is.na(parsedParams$yMin),is.na(parsedParams$yMax))) { protocol_display_values <- racas::get_protocol_curve_display_min_and_max_by_curve_id(parsedParams$curveIds[[1]]) plotWindowPoints <- rbindlist(fitData[ , points])[!userFlagStatus == "knocked out" & !preprocessFlagStatus == "knocked out" & !algorithmFlagStatus == "knocked out",] if(nrow(plotWindowPoints) == 0) { - plotWindow <- racas::get_plot_window(fitData[1]$points[[1]]) + plotWindow <- racas::get_plot_window(fitData[1]$points[[1]], logDose = logDose, logResponse = logResponse) } else { - plotWindow <- racas::get_plot_window(plotWindowPoints) + plotWindow <- racas::get_plot_window(plotWindowPoints, logDose = logDose, logResponse = logResponse) } recommendedDisplayWindow <- list(ymax = max(protocol_display_values$ymax,plotWindow[2], na.rm = TRUE), ymin = min(protocol_display_values$ymin,plotWindow[4], na.rm = TRUE)) if(is.na(parsedParams$yMin)) parsedParams$yMin <- recommendedDisplayWindow$ymin @@ -56,10 +60,7 @@ renderCurve <- function(getParams) { if(!"connectPoints" %in% names(renderingOptions) || is.na(renderingOptions$connectPoints)) { renderingOptions$connectPoints <- FALSE } - logDose <- TRUE - if(fitData[1]$renderingHint %in% c("Michaelis-Menten", "Substrate Inhibition", "Scatter", "Scatter Log-y")) logDose <- FALSE - logResponse <- FALSE - if(fitData[1]$renderingHint %in% c("Scatter Log-y","Scatter Log-x,y")) logResponse <- FALSE + setContentType("image/png") setHeader("Content-Disposition", paste0("filename=\"",getParams$curveIds,"\"")) t <- tempfile() From 5d73835109c45f2fc931c73ce9a5e45908bf8795 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 1 Nov 2019 17:14:07 -0700 Subject: [PATCH 477/576] fixes #601 encode projects when searching --- .../ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee | 2 +- .../ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee index d3d1a1470..8f43c6d0a 100644 --- a/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ExperimentServiceRoutes.coffee @@ -494,7 +494,7 @@ exports.genericExperimentSearch = (req, res) -> authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> _ = require "underscore" allowedProjectCodes = _.pluck(allowedUserProjects, "code") - baseurl = config.all.client.service.persistence.fullpath+"experiments/search?q="+req.params.searchTerm+"&projects=#{allowedProjectCodes.join(',')}" + baseurl = config.all.client.service.persistence.fullpath+"experiments/search?q="+req.params.searchTerm+"&projects=#{encodeURIComponent(allowedProjectCodes.join(','))}" console.log "baseurl" console.log baseurl serverUtilityFunctions = require './ServerUtilityFunctions.js' diff --git a/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee index ca0927c54..6fb0d4ac3 100644 --- a/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ProtocolServiceRoutes.coffee @@ -495,7 +495,7 @@ exports.genericProtocolSearch = (req, res) -> _ = require "underscore" allowedProjectCodes = _.pluck(allowedUserProjects, "code") config = require '../conf/compiled/conf.js' - baseurl = config.all.client.service.persistence.fullpath+"protocols/search?q="+req.params.searchTerm+"&projects=#{allowedProjectCodes.join(',')}" + baseurl = config.all.client.service.persistence.fullpath+"protocols/search?q="+req.params.searchTerm+"&projects=#{encodeURIComponent(allowedProjectCodes.join(','))}" console.log "baseurl" console.log baseurl serverUtilityFunctions = require './ServerUtilityFunctions.js' From 763ceff4194393c7220194dd68fe56e53d6bca51 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 11 Nov 2019 14:33:09 -0800 Subject: [PATCH 478/576] Bugfix: CodeValue StateTable values were not getting codeType/codeKind set properly --- modules/Components/src/client/ACASFormStateTable.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 4280da918..177d2a764 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -219,6 +219,10 @@ class window.ACASFormStateTableController extends Backbone.View newValue.set unitType: valueDef.modelDefaults.unitType if valueDef.modelDefaults.unitKind? newValue.set unitKind: valueDef.modelDefaults.unitKind + if valueDef.modelDefaults.codeType? + newValue.set codeType: valueDef.modelDefaults.codeType + if valueDef.modelDefaults.codeKind? + newValue.set codeKind: valueDef.modelDefaults.codeKind return newState getCurrentStates: -> From 88107d1db13f7a70583204f27d9ba0972ba2f037 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 12 Nov 2019 12:56:51 -0800 Subject: [PATCH 479/576] Fix StateTable bug with empty/null dateValues --- modules/Components/src/client/ACASFormStateTable.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 177d2a764..da1c5589d 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -243,6 +243,8 @@ class window.ACASFormStateTableController extends Backbone.View else if valDef.modelDefaults.type == 'dateValue' if value.get('dateValue')? displayVal = new Date(value.get('dateValue')).toISOString().split('T')[0] + else: + displayVal = null else displayVal = value.get valDef.modelDefaults.type From 16fb91f735de990592ded0650c587daa769e9825 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 12 Nov 2019 15:43:52 -0800 Subject: [PATCH 480/576] Syntax error in last change --- modules/Components/src/client/ACASFormStateTable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index da1c5589d..fbc127463 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -243,7 +243,7 @@ class window.ACASFormStateTableController extends Backbone.View else if valDef.modelDefaults.type == 'dateValue' if value.get('dateValue')? displayVal = new Date(value.get('dateValue')).toISOString().split('T')[0] - else: + else displayVal = null else displayVal = value.get valDef.modelDefaults.type From 845568d23cec5190281ed68dd34f55892ed3a277 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 14 Nov 2019 14:12:21 -0800 Subject: [PATCH 481/576] bugfix: StateTable now persists values of type urlValue --- modules/Components/src/client/ACASFormStateTable.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index fbc127463..6a94e7c68 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -324,6 +324,8 @@ class window.ACASFormStateTableController extends Backbone.View value.set stringValue: if cellContent? then cellContent else "" when 'clobValue' value.set clobValue: if cellContent? then cellContent else "" + when 'urlValue' + value.set urlValue: if cellContent? then cellContent else "" when 'numericValue' numVal = parseFloat(cellContent) if isNaN(numVal) or isNaN(Number(numVal)) From 26e02b1bed2e644ecfd62066041b32be7bb2e39a Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 15 Nov 2019 09:36:19 -0800 Subject: [PATCH 482/576] Revert "Bump csv-parse from 1.3.3 to 4.6.5" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ef94cd28..80d6d619c 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "cookie-parser": "^1.4.3", "connect-flash": "~0.1.0", "cron": "1.3.0", - "csv-parse": "^4.6.5", + "csv-parse": "^1.1.1", "each": "0.6.1", "exports-loader": "0.6.4", "express": "3.0.2", From bd4b67706b20a6d0a0d46fc7230aba053c9e31af Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Wed, 20 Nov 2019 15:39:19 -0800 Subject: [PATCH 483/576] Make it so both table and edit value view show human-readable values for codeValues and dateValues --- .../client/ACASFormStateDisplayUpdate.coffee | 56 ++++++++++++++++--- .../src/client/UtilityFunctions.coffee | 9 +++ 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee index 59699ca6e..9c08adaa8 100644 --- a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee +++ b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee @@ -34,6 +34,8 @@ class window.ACASFormStateDisplayUpdateCellController extends Backbone.View content = @options.cellDef.displayOverride content if val.get('lsType') == 'dateValue' content = UtilityFunctions::convertMSToYMDDate val.get('dateValue') + else if val.get('lsType') == 'codeValue' + content = UtilityFunctions::getNameForCode val, content, @options.pickLists $(@el).html content $(@el).addClass if @collection.length > 1 then "valueWasEdited" else "" @@ -56,6 +58,7 @@ class window.ACASFormStateDisplayUpdateRowController extends Backbone.View cellController = new ACASFormStateDisplayUpdateCellController collection: new ValueList vals cellDef: valDef + pickLists: @options.pickLists $(@el).append cellController.render().el cellController.on 'cellClicked', (values) => @trigger 'cellClicked', values @@ -76,10 +79,44 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View $(@el).empty() $(@el).html @template() @applyOptions() - @tableSetupComplete = true - - @ - + @fetchPickLists => + @tableSetupComplete = true + @ + + fetchPickLists: (callback) => + @pickLists = {} + listCount = 0 + + for val in @tableDef.values + if val.modelDefaults.type == 'codeValue' and val.fieldSettings.fieldType == 'codeValue' + listCount++ + + doneYet = => + listCount-- + if listCount == 0 + callback() + + if listCount == 0 + callback() + + for val in @tableDef.values + if val.modelDefaults.type == 'codeValue' and val.fieldSettings.fieldType == "codeValue" + if val.fieldSettings.optionURL? + url = val.fieldSettings.optionURL + else + url = "/api/codetables/#{val.modelDefaults.codeType}/#{val.modelDefaults.codeKind}" + kind = val.modelDefaults.kind + + $.ajax + type: 'GET' + url: url + json: true + self: @ + kind: kind + success: (response) -> + this.self.pickLists[this.kind] = new PickListList response + doneYet() + renderModelContent: => if @tableSetupComplete @completeRenderModelContent() @@ -98,6 +135,7 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View rowController = new ACASFormStateDisplayUpdateRowController collection: state.get 'lsValues' tableDef: @options.tableDef + pickLists: @pickLists @$("tbody").append rowController.render().el rowController.on 'cellClicked', (values) => @@ -108,6 +146,7 @@ class window.ACASFormStateDisplayUpdateController extends Backbone.View el: @$('.bv_valueEditor') tableDef: @tableDef stateID: state.id + pickLists: @pickLists @currentCellEditor.render() @currentCellEditor.on 'saveNewValue', @handleCellUpdate @@ -185,7 +224,11 @@ class window.ACASFormStateDisplayOldValueController extends Backbone.View value = @options.valueDef.displayOverride value if @options.previousLsTransaction prevLsTransaction = @options.previousLsTransaction - + if @model.get('lsType') == 'dateValue' + value = UtilityFunctions::convertMSToYMDDate value + else if @model.get('lsType') == 'codeValue' + value = content = UtilityFunctions::getNameForCode @model, value, @options.pickLists + attrs = value: value attrs.valueClass = if @model.get 'ignored' then "valueWasEdited" else "" @@ -249,6 +292,7 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View valueDef: @valueDef initialVal: initialVal previousLsTransaction: previousLsTransaction + pickLists: @options.pickLists @$("tbody").append oldRow.render().el index++ @@ -325,5 +369,3 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View #TODO value editor: # - maybe show transaction creator as modified by and date? # - add new value where none existed? -# show human readable codevalue instead of code - diff --git a/modules/Components/src/client/UtilityFunctions.coffee b/modules/Components/src/client/UtilityFunctions.coffee index 2c70edd46..e95b365ee 100644 --- a/modules/Components/src/client/UtilityFunctions.coffee +++ b/modules/Components/src/client/UtilityFunctions.coffee @@ -77,3 +77,12 @@ class window.UtilityFunctions return 0 else return Math.round((num+0.000001)*1000)/1000 + + getNameForCode: (value, codeToLookup, pickLists) -> + if pickLists[value.get('lsKind')]? + code = pickLists[value.get('lsKind')].findWhere({code: codeToLookup}) + name = if code? then code.get 'name' else "not found" + return name + else + console.log "can't find entry in pickLists hash for: "+value.get('lsKind') + console.dir pickLists From cf6c966582bfa480900f4435fa67e688662097db Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sat, 23 Nov 2019 18:00:50 -0800 Subject: [PATCH 484/576] fixes #606 Dont install LD client on calls to create an LR if server.liveDesign.installClientOnStart is true --- .../server/routes/CreateLiveDesignLiveReportForACAS.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ServerAPI/src/server/routes/CreateLiveDesignLiveReportForACAS.coffee b/modules/ServerAPI/src/server/routes/CreateLiveDesignLiveReportForACAS.coffee index 33d855c59..f014409c1 100644 --- a/modules/ServerAPI/src/server/routes/CreateLiveDesignLiveReportForACAS.coffee +++ b/modules/ServerAPI/src/server/routes/CreateLiveDesignLiveReportForACAS.coffee @@ -50,8 +50,9 @@ exports.getUrlForNewLiveDesignLiveReportForExperimentInternal = (exptCode, usern exptInfo.username = username console.log @responseJSON #install fresh ldclient - exports.installLiveDesignPythonClientInternal (statusCode, output) -> - console.log 'Updated LDClient' + if !config.all.server.liveDesign.installClientOnStart? || !config.all.server.liveDesign.installClientOnStart + exports.installLiveDesignPythonClientInternal (statusCode, output) -> + console.log 'Updated LDClient' command = "python ./src/python/ServerAPI/createLiveDesignLiveReportForACAS/create_lr_for_acas.py -e " command += "'"+config.all.client.service.result.viewer.liveDesign.baseUrl+"' -u '"+config.all.client.service.result.viewer.liveDesign.username+"' -p '"+config.all.client.service.result.viewer.liveDesign.password+"' -d '"+config.all.client.service.result.viewer.liveDesign.database+"' -r '"+config.all.client.service.result.viewer.liveDesign.makeReportReadonly+"' -i '" # data = {"compounds":["V035000","CMPD-0000002"],"assays":[{"protocolName":"Target Y binding","resultType":"curve id"}]} From f4f1c6f0f891c7dc96a83a0b3bff0d4e0963af28 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sat, 23 Nov 2019 18:21:12 -0800 Subject: [PATCH 485/576] #608 Restrict the global project instead of hiding it when HIDE_GLOBAL_PROJECT is true --- .../src/server/python/acas_ldclient/acasldclient.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index 6c3752f0b..f119ae201 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -201,13 +201,18 @@ def get_projects(client): configs = client.config() # If HIDE_GLOBAL_PROJECT ld config is set to 'true' - # then we need to remove "Global" from the list of projects + # then we set Global to a restricted project. + # Hide Global project in LD is restricted to Admins so we will restrict the Global project in ACAS + # This allows us to give admins and users with ACLs to the Global project rather than just removing it from ACAS hide_global_project_config = next((config for config in configs if config['key'] == "HIDE_GLOBAL_PROJECT"), None) if hide_global_project_config != None: hide_global_project = hide_global_project_config['value'] if hide_global_project == 'true': - ld_projects = [ p for p in ld_projects if p.name != "Global"] - + def set_global_restricted(project): + if project.name == "Global": + project.restricted = True + return project + ld_projects = map(set_global_restricted, ld_projects) projects = map(ld_project_to_acas, ld_projects) return projects From 3c1c091a278520e97e707ef97b6b9723c1e5597c Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 25 Nov 2019 16:22:52 -0800 Subject: [PATCH 486/576] return "not found" if code cannot be looked up --- modules/Components/src/client/UtilityFunctions.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/Components/src/client/UtilityFunctions.coffee b/modules/Components/src/client/UtilityFunctions.coffee index e95b365ee..4c5c505f2 100644 --- a/modules/Components/src/client/UtilityFunctions.coffee +++ b/modules/Components/src/client/UtilityFunctions.coffee @@ -86,3 +86,4 @@ class window.UtilityFunctions else console.log "can't find entry in pickLists hash for: "+value.get('lsKind') console.dir pickLists + return "not found" From f5b62ef29f1ce0d4a223e5f4858644ae933324dd Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 25 Nov 2019 17:50:08 -0800 Subject: [PATCH 487/576] fixes #610 - Insert selected item into picklist if missing from picklist --- modules/CmpdReg/src/client/src/PickList.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modules/CmpdReg/src/client/src/PickList.js b/modules/CmpdReg/src/client/src/PickList.js index fada98e6a..2b3fd345e 100755 --- a/modules/CmpdReg/src/client/src/PickList.js +++ b/modules/CmpdReg/src/client/src/PickList.js @@ -79,6 +79,12 @@ $(function() { } else { this.showIgnored = false; } + + if (this.options.insertSelectedCode !=null) { + this.insertSelectedCode = this.options.insertSelectedCode; + } else { + this.insertSelectedCode = true; + } }, handleListReset: function() { @@ -90,8 +96,20 @@ $(function() { } if (this.insertFirstOption) { this.collection.add(this.insertFirstOption, {at: 0, silent: true}); - } - this.render(); + } + + //If insert selected code is set to true (it is by default) then + // check if the selectedCode is null or undefined and also if it is in the list + // if it is not in the list then we artificially insert the selected code as to not change + // the value stored in the db. + if (this.insertSelectedCode && this.selectedCode != null && typeof(this.selectedCode) != "undefined" && (typeof(this.collection.getModelWithCode(this.selectedCode)) == "undefined")) { + newOption = new PickList({ + code: this.selectedCode, + name: this.selectedCode + }); + this.collection.add(newOption, {at: 0, silent: true}); + } + this.render(); }, render: function() { From 8b6e60d9e8a6d4ba854323da3a07fd96bf6bf843 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 9 Dec 2019 10:43:02 -0800 Subject: [PATCH 488/576] fixes #613 add configuration for a list of project codes to filter from ACAS --- conf/config.properties.example | 4 ++++ .../python/acas_ldclient/acasldclient.py | 18 +----------------- .../src/server/routes/AuthorRoutes.coffee | 8 ++++++++ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 0665c3127..7a2dd56ef 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -565,6 +565,10 @@ client.projectEditor.isRestricted.default=false client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER +# List of project codes to filter from ACAS +# e.g. server.projects.filterList = ["SomeProject"] +server.projects.filterList = [] + # For whether protocols and experiments should have sequential user defined corpName labels client.entity.saveInitialsCorpName=false diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index f119ae201..0d29ba009 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -197,24 +197,8 @@ def get_user(client, username): return acas_user def get_projects(client): - ld_projects = client.projects() - configs = client.config() - - # If HIDE_GLOBAL_PROJECT ld config is set to 'true' - # then we set Global to a restricted project. - # Hide Global project in LD is restricted to Admins so we will restrict the Global project in ACAS - # This allows us to give admins and users with ACLs to the Global project rather than just removing it from ACAS - hide_global_project_config = next((config for config in configs if config['key'] == "HIDE_GLOBAL_PROJECT"), None) - if hide_global_project_config != None: - hide_global_project = hide_global_project_config['value'] - if hide_global_project == 'true': - def set_global_restricted(project): - if project.name == "Global": - project.restricted = True - return project - ld_projects = map(set_global_restricted, ld_projects) + ld_projects = client.projects() projects = map(ld_project_to_acas, ld_projects) - return projects def ld_project_to_acas(ld_project): diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 6354264a9..732e56bec 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -50,6 +50,14 @@ exports.allowedProjectsInternal = (user, callback) -> csUtilities.getProjectStubsInternal (statusCode, allProjects) -> config = require '../conf/compiled/conf.js' _ = require 'underscore' + + # Filter out any projects that exist in the config for filtering projects + projectFilterList = JSON.parse config.all.server.projects.filterList + allProjects = _.filter allProjects, (project, index) -> + console.log project + console.log projectFilterList[0] + ! _.contains projectFilterList, project.code + if (config.all.server.project.roles.enable) filteredProjects = [] isAdmin = false; From 789d1b000ae15f8ccae690eb3776fb267bb92ffd Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Mon, 9 Dec 2019 13:58:48 -0800 Subject: [PATCH 489/576] Add optional inline display of codeTable descriptions --- modules/Components/src/client/ACASFormFields.coffee | 12 ++++++++++++ modules/Components/src/client/ACASFormFields.css | 5 +++++ .../Components/src/client/ACASFormFieldsView.html | 1 + .../src/client/AbstractFormController.coffee | 1 + 4 files changed, 19 insertions(+) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 346d1e980..9f5874cf3 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -282,6 +282,11 @@ class window.ACASFormLSCodeValueFieldController extends ACASFormAbstractFieldCon @getModel().set value: value ignored: false + if @options.showDescription? and @options.showDescription + @clearDescription() + desc = @pickListController.getSelectedModel().get('description') + if desc? + @setDescription(desc) super() setEmptyValue: -> @@ -342,6 +347,13 @@ class window.ACASFormLSCodeValueFieldController extends ACASFormAbstractFieldCon enableInput: -> @$('select').removeAttr 'disabled' + + setDescription: (message) -> + @$('.desc-inline').removeClass 'hide' + @$('.desc-inline').html message + + clearDescription: -> + @$('.desc-inline').addClass 'hide' class window.ACASFormLSThingInteractionFieldController extends ACASFormAbstractFieldController ### diff --git a/modules/Components/src/client/ACASFormFields.css b/modules/Components/src/client/ACASFormFields.css index e7ec46ce6..cddce8a91 100644 --- a/modules/Components/src/client/ACASFormFields.css +++ b/modules/Components/src/client/ACASFormFields.css @@ -1,4 +1,9 @@ .mce-content-body[disabled] { cursor: not-allowed; background-color: #eeeeee; +} + +.desc-inline { + font-style: italic; + color: #aaa; } \ No newline at end of file diff --git a/modules/Components/src/client/ACASFormFieldsView.html b/modules/Components/src/client/ACASFormFieldsView.html index cc68f457c..582842d97 100644 --- a/modules/Components/src/client/ACASFormFieldsView.html +++ b/modules/Components/src/client/ACASFormFieldsView.html @@ -20,6 +20,7 @@
    +
    diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 758a5e605..ba4284f86 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -154,6 +154,7 @@ class window.AbstractThingFormController extends AbstractFormController tabIndex: field.fieldSettings.tabIndex toFixed: field.fieldSettings.toFixed pickList: field.fieldSettings.pickList + showDescription: field.fieldSettings.showDescription switch field.fieldSettings.fieldType when 'label' From 05be79142128903d1590898e3fed80f1e04a8df7 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 12 Dec 2019 14:00:06 -0800 Subject: [PATCH 490/576] Add formfield label tooltip option. needs styling help --- modules/Components/src/client/ACASFormFields.coffee | 9 +++++++++ modules/Components/src/client/ACASFormFieldsView.html | 8 ++++++++ .../Components/src/client/AbstractFormController.coffee | 1 + 3 files changed, 18 insertions(+) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 9f5874cf3..cddfacfff 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -97,6 +97,9 @@ class window.ACASFormAbstractFieldController extends Backbone.View @addFormLabelClass @options.formLabelClass if @options.formLabelOrientation? @setupFormLabelOrientation @options.formLabelOrientation + if @options.formLabelTooltip? + @setFormLabelTooltip @options.formLabelTooltip + @showFormLabelTooltip() if @options.inputClass? @addInputClass @options.inputClass if @options.controlGroupClass? @@ -119,6 +122,12 @@ class window.ACASFormAbstractFieldController extends Backbone.View if value is "top" @$('label').removeClass 'control-label' # else set label to left, this is already the default + + setFormLabelTooltip: (value) -> + @$('.label-tooltip').attr 'title', value + + showFormLabelTooltip: -> + @$('.label-tooltip').removeClass 'hide' addInputClass: (value) -> @$('input').addClass value diff --git a/modules/Components/src/client/ACASFormFieldsView.html b/modules/Components/src/client/ACASFormFieldsView.html index 582842d97..446e344c2 100644 --- a/modules/Components/src/client/ACASFormFieldsView.html +++ b/modules/Components/src/client/ACASFormFieldsView.html @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 758a5e605..97da2d577 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -179,6 +179,7 @@ class window.AbstractThingFormController extends AbstractFormController when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts + when 'booleanValue' then newField = new ACASFormLSBooleanFieldController opts when 'locationTree' opts.tubeCode = @model.get('tubeCode') newField = new ACASFormLocationTreeController opts diff --git a/modules/ServerAPI/conf/ExampleThingConfJSON.coffee b/modules/ServerAPI/conf/ExampleThingConfJSON.coffee index ba62d9484..97830848a 100644 --- a/modules/ServerAPI/conf/ExampleThingConfJSON.coffee +++ b/modules/ServerAPI/conf/ExampleThingConfJSON.coffee @@ -89,6 +89,8 @@ ddicttypes: [ typeName: "metadata" + , + typeName: "boolean" ] ddictkinds: @@ -98,6 +100,9 @@ , typeName: "metadata" kindName: "color" + , + typeName: "boolean" + kindName: "boolean" ] codetables: @@ -129,6 +134,20 @@ code: "purple" name: "Purple" ignored: false + , + codeType: "boolean" + codeKind: "boolean" + codeOrigin: "ACAS DDict" + code: "true" + name: "true" + ignored: false + , + codeType: "boolean" + codeKind: "boolean" + codeOrigin: "ACAS DDict" + code: "false" + name: "false" + ignored: false ] labelsequences: From 93a17d0676d030141a9696175f2ab26a7a74ce73 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 13 Dec 2019 08:35:09 -0800 Subject: [PATCH 492/576] Adding state table form controller for state fields that should be shown in a form --- .../src/client/ACASFormStateTable.coffee | 182 +++++++++++++++++- .../src/client/ACASFormStateTableView.html | 5 +- .../src/client/AbstractFormController.coffee | 6 + modules/Components/src/client/style.css | 7 + 4 files changed, 193 insertions(+), 7 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 6a94e7c68..905f29783 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -19,8 +19,10 @@ class window.ACASFormStateTableController extends Backbone.View initialize: -> @thingRef = @options.thingRef @tableDef = @options.tableDef + @formWrapper = @options.formWrapper @tableSetupComplete = false @callWhenSetupComplete = null + @stateTableFormControllersCollection = [] getCollection: -> #TODO get states by type and kind @@ -49,6 +51,10 @@ class window.ACASFormStateTableController extends Backbone.View @setTableLabel @tableDef.tableLabel if @tableDef?.tableLabelClass? @addFormLabelClass @options.tableDef.tableLabelClass + anyValuesHaveFieldWrapper = _.some @tableDef.values, (v) -> + _.has v.fieldSettings, 'fieldWrapper' + if @formWrapper? and anyValuesHaveFieldWrapper + @hasFormWrapper = true @tableReadOnly = if @tableDef.tableReadOnly? then @tableDef.tableReadOnly else false @@ -63,6 +69,9 @@ class window.ACASFormStateTableController extends Backbone.View removeTableLabelClass: (value) -> @$('.bv_tableLabel').removeClass value + addFormWrapper: (formEl) -> + @$('.bv_formWrapper').html formEl + defineColumnsAndSetupHOT: -> @fetchPickLists => @defineColumns() @@ -111,19 +120,23 @@ class window.ACASFormStateTableController extends Backbone.View @colHeaders = [] unless @colDefs? @colDefs = [] + unless @formValueDefs? + @formValueDefs = [] unless @unitKeyValueMap? #keeps track of which values unit keys are for @unitKeyValueMap = {} for val in @tableDef.values + isTableValue = !val.fieldSettings.fieldWrapper? displayName = val.fieldSettings.formLabel if @showUnits if val.modelDefaults.unitKind? displayName += "
    (#{val.modelDefaults.unitKind})" - @colHeaders.push - displayName: displayName - keyName: val.modelDefaults.kind - width: if val.fieldSettings.width? then val.fieldSettings.width else 75 + if isTableValue + @colHeaders.push + displayName: displayName + keyName: val.modelDefaults.kind + width: if val.fieldSettings.width? then val.fieldSettings.width else 75 colOpts = data: val.modelDefaults.kind colOpts.readOnly = if val.fieldSettings.readOnly? then val.fieldSettings.readOnly else false @@ -146,7 +159,10 @@ class window.ACASFormStateTableController extends Backbone.View if val.validator? colOpts.validator = val.validator - @colDefs.push colOpts + if isTableValue + @colDefs.push colOpts + else + @formValueDefs.push val if val.fieldSettings.unitColumnKey? @colHeaders.push @@ -176,6 +192,7 @@ class window.ACASFormStateTableController extends Backbone.View afterChange: @handleCellChanged afterValidate: @handleAfterValidate afterCreateRow: @handleRowCreated + afterSelection: @handleSelection minSpareRows: 1, allowInsertRow: true contextMenu: contextMenu @@ -189,11 +206,17 @@ class window.ACASFormStateTableController extends Backbone.View afterRemoveRow: @handleRowRemoved columns: @colDefs search: @tableDef.search + currentRowClassName: 'bv_stateDisplayCurrentRow', + currentColClassName: 'bv_stateDisplayCurrentColumn' cells: (row, col, prop) => cellProperties = {} if @tableReadOnly cellProperties.readOnly = true return cellProperties; + + # Select the first row on start + @hot.selectCell(0,0,0,0) + @hot.addHook 'afterChange', @validateRequiredAndUniqueness readOnlyRenderer: (instance, td, row, col, prop, value, cellProperties) => @@ -223,11 +246,29 @@ class window.ACASFormStateTableController extends Backbone.View newValue.set codeType: valueDef.modelDefaults.codeType if valueDef.modelDefaults.codeKind? newValue.set codeKind: valueDef.modelDefaults.codeKind + + if @hasFormWrapper + @setupFormForNewState newState + @hot.selectCell(row,0,row,0) return newState getCurrentStates: -> @thingRef.get('lsStates').getStatesByTypeAndKind @tableDef.stateType, @tableDef.stateKind + setupFormForNewState: (state) -> + fDiv = @formWrapper.clone() + @$('.bv_formWrapper').append fDiv + formController = new ACASFormStateTableFormController + el: fDiv + thingRef: @thingRef + valueDefs: @formValueDefs + stateType: @tableDef.stateType + stateKind: @tableDef.stateKind + rowNumber: @getRowNumberForState(state) + rowNumberKind: @rowNumberKind + @stateTableFormControllersCollection.push formController + formController.hide() + renderState: (state) -> rowNum = @getRowNumberForState(state) if rowNum? #should always be true @@ -263,6 +304,7 @@ class window.ACASFormStateTableController extends Backbone.View @hot.setDataAtRowProp cols, "autofill" + getRowNumberForState: (state) -> rowValues = state.getValuesByTypeAndKind 'numericValue', @rowNumberKind if rowValues.length == 1 @@ -369,6 +411,28 @@ class window.ACASFormStateTableController extends Backbone.View else state.set ignored: true @trigger 'removedRow', state + + # This happens before the table is rendered and the row really removed + if @hasFormWrapper + # First destroy the form controller we are removing + controllerToRemove = @stateTableFormControllersCollection[rowNum] + controllerToRemove.remove() + controllerToRemove.unbind() + + # Remove the controller from the list + @stateTableFormControllersCollection.splice rowNum, 1 + + # After controller removal we want to select the row that took its place if it exists + # which should be the conroller below it if it exists + if @stateTableFormControllersCollection[rowNum]? + @hot.selectCell(rowNum,0,rowNum,0) + # If it doesn't exist then the row below it doesn't have a controller yet + # This means that we just deleted the last row in the table with a controller + # so we select the row above it if it exists + else if rowNum-1 > -1 + @hot.selectCell(rowNum-1,0,rowNum-1,0) + + nextRow = index+amount nRows = @hot.countRows() + amount #to get number of rows before removing if nextRow < nRows-1 @@ -378,6 +442,17 @@ class window.ACASFormStateTableController extends Backbone.View if rowValues.length == 1 rowValues[0].set numericValue: rowNum - amount + handleSelection: (row, column, row2, column2, preventScrolling, selectionLayerLevel) => + if @hasFormWrapper? + $(@el).find(".bv_moreDetails").show() + for cont, index in @stateTableFormControllersCollection + if index == row + $(@el).find(".bv_moreDetails").hide() + cont.show() + else + cont.hide() + + setCodeForName: (value, nameToLookup) -> if @pickLists[value.get('lsKind')]? code = @pickLists[value.get('lsKind')].findWhere({name: nameToLookup}) @@ -497,4 +572,101 @@ class window.ACASFormStateTableController extends Backbone.View @hot.render() +class window.ACASFormStateTableFormController extends Backbone.View + + initialize: -> + @hide() + @valueDefs = @options.valueDefs + @stateType = @options.stateType + @stateKind = @options.stateKind + @thingRef = @options.thingRef + @rowNumber = @options.rowNumber + @rowNumberKind = @options.rowNumberKind + @setupForm() + @show() + + render: => + @setupForm() + + show: -> + $(@el).show() + + hide: -> + $(@el).hide() + + setupForm: -> + state = @getStateForRow() + + for field in @valueDefs + value = state.getOrCreateValueByTypeAndKind field.modelDefaults.type, field.modelDefaults.kind + keyBase = field.key + newKey = keyBase + value.cid + value.set key: newKey + @thingRef.set newKey, value + + opts = + modelKey: newKey + inputClass: field.fieldSettings.inputClass + formLabel: field.fieldSettings.formLabel + formLabelOrientation: field.fieldSettings.formLabelOrientation + placeholder: field.fieldSettings.placeholder + required: field.fieldSettings.required + url: field.fieldSettings.url + thingRef: @thingRef + insertUnassigned: field.fieldSettings.insertUnassigned + firstSelectText: field.fieldSettings.firstSelectText + modelDefaults: field.modelDefaults + allowedFileTypes: field.fieldSettings.allowedFileTypes + extendedLabel: field.fieldSettings.extendedLabel + tabIndex: field.fieldSettings.tabIndex + toFixed: field.fieldSettings.toFixed + pickList: field.fieldSettings.pickList + rowNumber: @rowNumber + rowNumberKind: @rowNumberKind + stateType: @stateType + stateKind: @stateKind + + #Should refactor this into a function for other places to use when selcting a controller + switch field.fieldSettings.fieldType + when 'label' + if field.multiple? and field.multiple + newField = new ACASFormMultiLabelListController opts + else + newField = new ACASFormLSLabelFieldController opts + when 'numericValue' then newField = new ACASFormLSNumericValueFieldController opts + when 'codeValue' then newField = new ACASFormLSCodeValueFieldController opts + when 'htmlClobValue' + opts.rows = field.fieldSettings?.rows + newField = new ACASFormLSHTMLClobValueFieldController opts + when 'thingInteractionSelect' + opts.thingType = field.fieldSettings.thingType + opts.thingKind = field.fieldSettings.thingKind + opts.queryUrl = field.fieldSettings.queryUrl + opts.labelType = field.fieldSettings.labelType + newField = new ACASFormLSThingInteractionFieldController opts + when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts + when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts + when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts + when 'locationTree' + opts.tubeCode = @model.get('tubeCode') + newField = new ACASFormLocationTreeController opts + + valueDiv = $(@el).find("."+field.fieldSettings.fieldWrapper) + valueDiv[0].append newField.render().el + + getStateForRow: () -> + currentStates = @getCurrentStates() + for state in currentStates + if @getRowNumberForState(state) == @rowNumber + return state + + getCurrentStates: -> + @thingRef.get('lsStates').getStatesByTypeAndKind @stateType, @stateKind + + getRowNumberForState: (state) -> + rowValues = state.getValuesByTypeAndKind 'numericValue', @rowNumberKind + if rowValues.length == 1 + return rowValues[0].get('numericValue') + else + return null diff --git a/modules/Components/src/client/ACASFormStateTableView.html b/modules/Components/src/client/ACASFormStateTableView.html index c19458a33..02121c348 100644 --- a/modules/Components/src/client/ACASFormStateTableView.html +++ b/modules/Components/src/client/ACASFormStateTableView.html @@ -1,6 +1,7 @@ diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 758a5e605..04e5197a9 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -204,9 +204,15 @@ class window.AbstractThingFormController extends AbstractFormController @formTables = {} for tDef in tableDefs tdiv = $("
    ") + @$("."+tDef.tableWrapper).append tdiv + + formWrapper = null + if tDef.formWrapper? + formWrapper = @$("."+tDef.formWrapper) fTable = new ACASFormStateTableController el: tdiv + formWrapper: formWrapper tableDef: tDef thingRef: @model fTable.render() diff --git a/modules/Components/src/client/style.css b/modules/Components/src/client/style.css index 878aa87c8..c3b845277 100644 --- a/modules/Components/src/client/style.css +++ b/modules/Components/src/client/style.css @@ -47,4 +47,11 @@ .bv_stateDisplayUpdateTable .valueEditable { cursor: pointer; +} + +.bv_stateDisplayCurrentRow { + background-color: rgb(182, 182, 211) !important; +} +.bv_stateDisplayCurrentColumn { + background-color: rgb(182, 182, 211) !important; } \ No newline at end of file From a6280b77729f1918317c023b363aee2cb28b76e0 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sat, 14 Dec 2019 07:03:45 -0800 Subject: [PATCH 493/576] better configuration description and removing console.log statements --- conf/config.properties.example | 2 +- modules/ServerAPI/src/server/routes/AuthorRoutes.coffee | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/conf/config.properties.example b/conf/config.properties.example index 7a2dd56ef..d8686e3e0 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -565,7 +565,7 @@ client.projectEditor.isRestricted.default=false client.roles.crossProjectLoaderRole=ROLE_ACAS-CROSS-PROJECT-LOADER -# List of project codes to filter from ACAS +# List of project codes to filter out and hide from ACAS # e.g. server.projects.filterList = ["SomeProject"] server.projects.filterList = [] diff --git a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee index 732e56bec..dfac5160c 100644 --- a/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/AuthorRoutes.coffee @@ -54,8 +54,6 @@ exports.allowedProjectsInternal = (user, callback) -> # Filter out any projects that exist in the config for filtering projects projectFilterList = JSON.parse config.all.server.projects.filterList allProjects = _.filter allProjects, (project, index) -> - console.log project - console.log projectFilterList[0] ! _.contains projectFilterList, project.code if (config.all.server.project.roles.enable) From e024121ab6bd6cf79f44caad99b9006495d7b592 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Sun, 15 Dec 2019 14:33:09 -0800 Subject: [PATCH 494/576] Initial MultiCodeValueCheckbox implementation --- .../ACASFormMultiCodeValueCheckbox.coffee | 153 ++++++++++++++++++ .../ACASFormMultiCodeValueCheckbox.html | 11 ++ .../src/client/ACASFormStateTable.coffee | 6 +- .../src/client/AbstractFormController.coffee | 6 +- 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee create mode 100644 modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee new file mode 100644 index 000000000..e4e90df28 --- /dev/null +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee @@ -0,0 +1,153 @@ +class window.ACASFormCodeValueCheckboxController extends ACASFormAbstractFieldController + ### + Launched by ACASFormMultiCodeValueCheckboxController to control one checkbox in the list + ### + + template: _.template($("#ACASFormCodeValueCheckbox").html()) + + events: -> + "change input": "handleInputChanged" + + initialize: -> + @stateRef = @options.stateRef + @thingRef = @options.thingRef + @keyBase = @options.keyBase + @code = @options.codeTable.get 'code' + @codeType = @options.codeTable.get 'codeType' + @codeKind = @options.codeTable.get 'codeKind' + @stateType = @options.stateType + @stateKind = @options.stateKind + @valueType = @options.valueType + @valueKind = @options.valueKind + @rowNumber = @options.rowNumber + @rowNumberKind = @options.rowNumberKind + @ + + render: -> + super() + @ + + handleInputChanged: -> + @clearError() + @userInputEvent = true + isChecked = @$('input').is(":checked") + if isChecked + value = @getState().createValueByTypeAndKind @valueType, @valueKind + value.set + codeValue: @code + codeType: @codeType + codeKind: @codeKind + codeOrigin: @codeOrigin + ignored: false + newKey = @keyBase + value.cid + value.set key: newKey + @thingRef.set newKey, value + @getState().get('lsValues').add value + else + # Find existing value with codeValue matching this controller's code, and mark as ignored. + value = @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code}) + value.set + ignored: true + super() + + renderModelContent: => + # Check for value matching this controller's code, and mark as checked if found + if @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code})? + @$('input').attr 'checked', 'checked' + else + @$('input').removeAttr 'checked' + super() + + getState: () -> + if @rowNumber? and @rowNumberKind? + # If rowNumber is specified, assume operating within a stateTable / stateTableForm and pick the right state + return @getStateForRow() + else + # Otherwise assume operating with only one state of given type/kind on the Thing + return @thingRef.get('lsStates').getOrCreateStateByTypeAndKind @stateType, @stateKind + + getStateForRow: () -> + currentStates = @getCurrentStates() + for state in currentStates + if @getRowNumberForState(state) == @rowNumber + return state + #if we get to here without returning, we need a new state + newState = @thingRef.get('lsStates').createStateByTypeAndKind @stateType, @stateKind + return newState + + getCurrentStates: -> + @thingRef.get('lsStates').getStatesByTypeAndKind @stateType, @stateKind + + getRowNumberForState: (state) -> + rowValues = state.getValuesByTypeAndKind 'numericValue', @rowNumberKind + if rowValues.length == 1 + return rowValues[0].get('numericValue') + else + return null + + +class window.ACASFormMultiCodeValueCheckboxController extends ACASFormAbstractFieldController + ### + Launching controller must instantiate with the full field conf including modelDefaults, not just the fieldDefinition. + Specifying rowNumber and rowNumberKind are optional, and will make this controller act on a specific state. + Controls a list of + ### + template: _.template($("#ACASFormMultiCodeValueCheckbox").html()) + + initialize: -> + @opts = @options + @thingRef = @opts.thingRef + mdl = @opts.modelDefaults + if @opts.stateType? + @stateType = @opts.stateType + else + @stateType = mdl.stateType + if @opts.stateKind? + @stateKind = @opts.stateKind + else + @stateKind = mdl.stateKind + @keyBase = @opts.modelKey + @codeTableCollection = new PickListList() + if @url? + @codeTableCollection.url = @url + else + @codeTableCollection.url = "/api/codetables/#{mdl.codeType}/#{mdl.codeKind}" + @checkboxControllerList = [] + super() + @ + + + + render: -> + $(@el).empty() + $(@el).html @template() + @codeTableCollection.fetch + success: @finishRender + @ + + renderModelContent: -> + @checkboxControllerList.forEach (controller) -> + controller.renderModelContent() + + finishRender: (collection) => + # setup single controllers for each fetched codeTable + collection.each (codeTable) => + @addOneCodeValueSelect codeTable + + addOneCodeValueSelect: (codeTable) -> + # setup single controllers for each fetched codeTable, call renderModelContent on each + opts = + codeTable: codeTable + stateType: @stateType + stateKind: @stateKind + valueType: @opts.modelDefaults.type + valueKind: @opts.modelDefaults.kind + formLabel: codeTable.get 'name' + keyBase: @keyBase + rowNumber: @opts.rowNumber + rowNumberKind: @opts.rowNumberKind + thingRef: @thingRef + checkboxController = new ACASFormCodeValueCheckboxController opts + @checkboxControllerList.push checkboxController + @$('.bv_multiCodeValueCheckboxWrapper').append checkboxController.render().el + diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html new file mode 100644 index 000000000..e64dfb12f --- /dev/null +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 905f29783..f4df6e01e 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -635,7 +635,11 @@ class window.ACASFormStateTableFormController extends Backbone.View else newField = new ACASFormLSLabelFieldController opts when 'numericValue' then newField = new ACASFormLSNumericValueFieldController opts - when 'codeValue' then newField = new ACASFormLSCodeValueFieldController opts + when 'codeValue' + if field.multiple? and field.multiple + newField = new ACASFormMultiCodeValueCheckboxController opts + else + newField = new ACASFormLSCodeValueFieldController opts when 'htmlClobValue' opts.rows = field.fieldSettings?.rows newField = new ACASFormLSHTMLClobValueFieldController opts diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 57dd4b03f..0b3766acc 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -164,7 +164,11 @@ class window.AbstractThingFormController extends AbstractFormController else newField = new ACASFormLSLabelFieldController opts when 'numericValue' then newField = new ACASFormLSNumericValueFieldController opts - when 'codeValue' then newField = new ACASFormLSCodeValueFieldController opts + when 'codeValue' + if field.multiple? and field.multiple + newField = new ACASFormMultiCodeValueCheckboxController opts + else + newField = new ACASFormLSCodeValueFieldController opts when 'htmlClobValue' opts.rows = field.fieldSettings?.rows newField = new ACASFormLSHTMLClobValueFieldController opts From f6bc4c4b82002450b59606e269e22242999ffedb Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Sun, 15 Dec 2019 16:57:16 -0800 Subject: [PATCH 495/576] show description on initial load --- .../Components/src/client/ACASFormFields.coffee | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 5e9b55862..2d9c0ee43 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -291,11 +291,7 @@ class window.ACASFormLSCodeValueFieldController extends ACASFormAbstractFieldCon @getModel().set value: value ignored: false - if @options.showDescription? and @options.showDescription - @clearDescription() - desc = @pickListController.getSelectedModel().get('description') - if desc? - @setDescription(desc) + @showDescription() super() setEmptyValue: -> @@ -305,6 +301,7 @@ class window.ACASFormLSCodeValueFieldController extends ACASFormAbstractFieldCon renderModelContent: => @pickListController.setSelectedCode @getModel().get('value') + @showDescription() super() setupSelect: -> @@ -357,6 +354,14 @@ class window.ACASFormLSCodeValueFieldController extends ACASFormAbstractFieldCon enableInput: -> @$('select').removeAttr 'disabled' + showDescription: -> + if @options.showDescription? and @options.showDescription + @clearDescription() + if @pickListController.getSelectedModel()? + desc = @pickListController.getSelectedModel().get('description') + if desc? + @setDescription(desc) + setDescription: (message) -> @$('.desc-inline').removeClass 'hide' @$('.desc-inline').html message From 25ea9609d28115a0206b9cfd6a9c148e5ca03b54 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Sun, 15 Dec 2019 16:58:09 -0800 Subject: [PATCH 496/576] renderModelContent for formFields within stateTableForm --- .../src/client/ACASFormStateTable.coffee | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 905f29783..b693bd8dc 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -258,15 +258,16 @@ class window.ACASFormStateTableController extends Backbone.View setupFormForNewState: (state) -> fDiv = @formWrapper.clone() @$('.bv_formWrapper').append fDiv + rowNumber = @getRowNumberForState(state) formController = new ACASFormStateTableFormController el: fDiv thingRef: @thingRef valueDefs: @formValueDefs stateType: @tableDef.stateType stateKind: @tableDef.stateKind - rowNumber: @getRowNumberForState(state) + rowNumber: rowNumber rowNumberKind: @rowNumberKind - @stateTableFormControllersCollection.push formController + @stateTableFormControllersCollection[rowNumber] = formController formController.hide() renderState: (state) -> @@ -303,6 +304,7 @@ class window.ACASFormStateTableController extends Backbone.View cols.push unitCellInfo @hot.setDataAtRowProp cols, "autofill" + @setupFormForNewState state getRowNumberForState: (state) -> @@ -583,6 +585,7 @@ class window.ACASFormStateTableFormController extends Backbone.View @thingRef = @options.thingRef @rowNumber = @options.rowNumber @rowNumberKind = @options.rowNumberKind + @formFields = {} @setupForm() @show() @@ -590,6 +593,8 @@ class window.ACASFormStateTableFormController extends Backbone.View @setupForm() show: -> + for modelKey, formField of @formFields + formField.renderModelContent() $(@el).show() hide: -> @@ -600,6 +605,13 @@ class window.ACASFormStateTableFormController extends Backbone.View for field in @valueDefs value = state.getOrCreateValueByTypeAndKind field.modelDefaults.type, field.modelDefaults.kind + if field.modelDefaults.codeType? + value.set 'codeType', field.modelDefaults.codeType + if field.modelDefaults.codeKind? + value.set 'codeKind', field.modelDefaults.codeKind + if field.modelDefaults.codeOrigin? + value.set 'codeOrigin', field.modelDefaults.codeOrigin + value.set 'value', value.get(value.get('lsType')) keyBase = field.key newKey = keyBase + value.cid value.set key: newKey @@ -610,6 +622,7 @@ class window.ACASFormStateTableFormController extends Backbone.View inputClass: field.fieldSettings.inputClass formLabel: field.fieldSettings.formLabel formLabelOrientation: field.fieldSettings.formLabelOrientation + formLabelTooltip: field.fieldSettings.formLabelTooltip placeholder: field.fieldSettings.placeholder required: field.fieldSettings.required url: field.fieldSettings.url @@ -622,6 +635,7 @@ class window.ACASFormStateTableFormController extends Backbone.View tabIndex: field.fieldSettings.tabIndex toFixed: field.fieldSettings.toFixed pickList: field.fieldSettings.pickList + showDescription: field.fieldSettings.showDescription rowNumber: @rowNumber rowNumberKind: @rowNumberKind stateType: @stateType @@ -653,7 +667,8 @@ class window.ACASFormStateTableFormController extends Backbone.View newField = new ACASFormLocationTreeController opts valueDiv = $(@el).find("."+field.fieldSettings.fieldWrapper) - valueDiv[0].append newField.render().el + valueDiv.append newField.render().el + @formFields[newKey] = newField getStateForRow: () -> currentStates = @getCurrentStates() From c905a4dbe08aa687bb62b504df38105dcc194bb0 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Sun, 15 Dec 2019 19:26:14 -0800 Subject: [PATCH 497/576] bugfix: properly wrap setupFormForNewState --- modules/Components/src/client/ACASFormStateTable.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index ccd567b51..eae2b9876 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -304,7 +304,8 @@ class window.ACASFormStateTableController extends Backbone.View cols.push unitCellInfo @hot.setDataAtRowProp cols, "autofill" - @setupFormForNewState state + if @hasFormWrapper + @setupFormForNewState state getRowNumberForState: (state) -> From cbd96614eb5b827bfe1cb1f32faf254493303267 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 17 Dec 2019 13:14:26 -0800 Subject: [PATCH 498/576] Fix styling of checkboxes --- .../src/client/ACASFormMultiCodeValueCheckbox.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee index e4e90df28..5982b6ee0 100644 --- a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee @@ -149,5 +149,7 @@ class window.ACASFormMultiCodeValueCheckboxController extends ACASFormAbstractFi thingRef: @thingRef checkboxController = new ACASFormCodeValueCheckboxController opts @checkboxControllerList.push checkboxController - @$('.bv_multiCodeValueCheckboxWrapper').append checkboxController.render().el + checkBoxEl = checkboxController.render().el + checkBoxEl.style.float = "left" + @$('.bv_multiCodeValueCheckboxWrapper').append checkBoxEl From 3e48c6719d12130c1124cf433b269491cb299b55 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 20 Dec 2019 22:44:49 -0800 Subject: [PATCH 499/576] 619 add row to ACASFormStateTable programatically and hide and show table controller --- .../src/client/ACASFormStateTable.coffee | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index eae2b9876..e8d1729a8 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -215,16 +215,62 @@ class window.ACASFormStateTableController extends Backbone.View return cellProperties; # Select the first row on start - @hot.selectCell(0,0,0,0) + if @$('.bv_tableWrapper').is ":visible" + @hot.selectCell(0,0,0,0) @hot.addHook 'afterChange', @validateRequiredAndUniqueness + addRow: (values, callback) -> + # when we update the handsontable cell data directly we don't know when the "afterChange" function + # will complete, so we lock the table, count the number of triggers to the number of completions of the + # afterChange function, when it equals the number of changes we expect, we reset the table back to its + # original settings, and trigger the callback function + @hotPseudoTransaction(callback, values.length) + lastRow = @getLastRow() + for value, index in values + @hot.setDataAtCell(lastRow, index, value) + + hotPseudoTransaction: (callback, changeCount) -> + # Get the settings related th elo + originalSettings = @getCurrentLockSettings() + @lockTable() + @changeCount = 0 + @.on "cellChangeComplete", => + @changeCount = @changeCount + 1 + if @changeCount == changeCount + @hot.updateSettings(originalSettings) + callback changeCount + + getCurrentLockSettings: -> + settings = @hot.getSettings() + currentLockSettings = + readOnly: settings.readOnly, # make table cells read-only + contextMenu: settings.contextMenu, # disable context menu to change things + disableVisualSelection: settings.disableVisualSelection, # prevent user from visually selecting + manualColumnResize: settings.manualColumnResize, # prevent dragging to resize columns + manualRowResize: settings.manualRowResize, # prevent dragging to resize rows + comments: settings.comments #3 prevent editing of comments + + lockTable: -> + @hot.updateSettings({ + readOnly: true, # make table cells read-only + contextMenu: false, # disable context menu to change things + disableVisualSelection: true, # prevent user from visually selecting + manualColumnResize: false, # prevent dragging to resize columns + manualRowResize: false, # prevent dragging to resize rows + comments: false #3 prevent editing of comments + }) + readOnlyRenderer: (instance, td, row, col, prop, value, cellProperties) => Handsontable.renderers.TextRenderer.apply(this, arguments) td.style.background = '#EEE' td.style.color = 'black'; cellProperties.readOnly = true; + getLastRow: -> + lastRow = @getCurrentStates().length + return lastRow + getStateForRow: (row, forceNew) -> currentStates = @getCurrentStates() for state in currentStates @@ -252,6 +298,13 @@ class window.ACASFormStateTableController extends Backbone.View @hot.selectCell(row,0,row,0) return newState + show: => + $(@el).show() + @hot.render() + + hide: -> + $(@el).hide() + getCurrentStates: -> @thingRef.get('lsStates').getStatesByTypeAndKind @tableDef.stateType, @tableDef.stateKind @@ -392,6 +445,8 @@ class window.ACASFormStateTableController extends Backbone.View @setCodeForName(value, cellContent) rowNumValue = state.getOrCreateValueByTypeAndKind 'numericValue', @rowNumberKind rowNumValue.set numericValue: changeRow + @trigger 'cellChangeComplete', state + handleRowCreated: (index, amount) => #TODO: if update handsontable version, can add source input to only do something when row created via context menu From c2dc9e417ae90f8f57bfd8bd940ae42d9e16b92e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 20 Dec 2019 22:46:49 -0800 Subject: [PATCH 500/576] 619 add row to state form controller programatically by supplying values and also hide/show functionality --- .../src/client/ACASFormStateTable.coffee | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index eae2b9876..e8d1729a8 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -215,16 +215,62 @@ class window.ACASFormStateTableController extends Backbone.View return cellProperties; # Select the first row on start - @hot.selectCell(0,0,0,0) + if @$('.bv_tableWrapper').is ":visible" + @hot.selectCell(0,0,0,0) @hot.addHook 'afterChange', @validateRequiredAndUniqueness + addRow: (values, callback) -> + # when we update the handsontable cell data directly we don't know when the "afterChange" function + # will complete, so we lock the table, count the number of triggers to the number of completions of the + # afterChange function, when it equals the number of changes we expect, we reset the table back to its + # original settings, and trigger the callback function + @hotPseudoTransaction(callback, values.length) + lastRow = @getLastRow() + for value, index in values + @hot.setDataAtCell(lastRow, index, value) + + hotPseudoTransaction: (callback, changeCount) -> + # Get the settings related th elo + originalSettings = @getCurrentLockSettings() + @lockTable() + @changeCount = 0 + @.on "cellChangeComplete", => + @changeCount = @changeCount + 1 + if @changeCount == changeCount + @hot.updateSettings(originalSettings) + callback changeCount + + getCurrentLockSettings: -> + settings = @hot.getSettings() + currentLockSettings = + readOnly: settings.readOnly, # make table cells read-only + contextMenu: settings.contextMenu, # disable context menu to change things + disableVisualSelection: settings.disableVisualSelection, # prevent user from visually selecting + manualColumnResize: settings.manualColumnResize, # prevent dragging to resize columns + manualRowResize: settings.manualRowResize, # prevent dragging to resize rows + comments: settings.comments #3 prevent editing of comments + + lockTable: -> + @hot.updateSettings({ + readOnly: true, # make table cells read-only + contextMenu: false, # disable context menu to change things + disableVisualSelection: true, # prevent user from visually selecting + manualColumnResize: false, # prevent dragging to resize columns + manualRowResize: false, # prevent dragging to resize rows + comments: false #3 prevent editing of comments + }) + readOnlyRenderer: (instance, td, row, col, prop, value, cellProperties) => Handsontable.renderers.TextRenderer.apply(this, arguments) td.style.background = '#EEE' td.style.color = 'black'; cellProperties.readOnly = true; + getLastRow: -> + lastRow = @getCurrentStates().length + return lastRow + getStateForRow: (row, forceNew) -> currentStates = @getCurrentStates() for state in currentStates @@ -252,6 +298,13 @@ class window.ACASFormStateTableController extends Backbone.View @hot.selectCell(row,0,row,0) return newState + show: => + $(@el).show() + @hot.render() + + hide: -> + $(@el).hide() + getCurrentStates: -> @thingRef.get('lsStates').getStatesByTypeAndKind @tableDef.stateType, @tableDef.stateKind @@ -392,6 +445,8 @@ class window.ACASFormStateTableController extends Backbone.View @setCodeForName(value, cellContent) rowNumValue = state.getOrCreateValueByTypeAndKind 'numericValue', @rowNumberKind rowNumValue.set numericValue: changeRow + @trigger 'cellChangeComplete', state + handleRowCreated: (index, amount) => #TODO: if update handsontable version, can add source input to only do something when row created via context menu From eef820e96510afed95313295a29d546f439edaeb Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Thu, 2 Jan 2020 09:57:46 -0800 Subject: [PATCH 501/576] fix typo in comment --- modules/Components/src/client/ACASFormStateTable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index e8d1729a8..838715072 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -231,7 +231,7 @@ class window.ACASFormStateTableController extends Backbone.View @hot.setDataAtCell(lastRow, index, value) hotPseudoTransaction: (callback, changeCount) -> - # Get the settings related th elo + # Get the current settings related the lock originalSettings = @getCurrentLockSettings() @lockTable() @changeCount = 0 From 2d12e50d93c60ddaec0a11c735069edbf34bac47 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 2 Jan 2020 16:40:53 -0800 Subject: [PATCH 502/576] #619 if re-rendering after a save, we need to destroy the existing state form controller and re-create it with the new state --- modules/Components/src/client/ACASFormStateTable.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 838715072..9006f46ee 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -309,6 +309,10 @@ class window.ACASFormStateTableController extends Backbone.View @thingRef.get('lsStates').getStatesByTypeAndKind @tableDef.stateType, @tableDef.stateKind setupFormForNewState: (state) -> + if @stateTableFormControllersCollection[rowNumber]? + @stateTableFormControllersCollection[rowNumber].remove() + @stateTableFormControllersCollection[rowNumber].unbind() + fDiv = @formWrapper.clone() @$('.bv_formWrapper').append fDiv rowNumber = @getRowNumberForState(state) From f1651ec648ebf67eff6a09e05fe96ebc9fc7fe08 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 3 Jan 2020 11:06:43 -0800 Subject: [PATCH 503/576] #619 remove listener before starting transaction otherwise the count will be off during subsequent additions and the call back will return to early --- modules/Components/src/client/ACASFormStateTable.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 9006f46ee..a01295aea 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -235,6 +235,7 @@ class window.ACASFormStateTableController extends Backbone.View originalSettings = @getCurrentLockSettings() @lockTable() @changeCount = 0 + @.off "cellChangeComplete" @.on "cellChangeComplete", => @changeCount = @changeCount + 1 if @changeCount == changeCount From 905cc98963dbd5b262d6feb0e41256d928aca11e Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 3 Jan 2020 13:08:03 -0800 Subject: [PATCH 504/576] Fix rendering and updates to only act on non-ignored values --- .../src/client/ACASFormMultiCodeValueCheckbox.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee index 5982b6ee0..86d54a4d3 100644 --- a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee @@ -32,7 +32,7 @@ class window.ACASFormCodeValueCheckboxController extends ACASFormAbstractFieldCo @userInputEvent = true isChecked = @$('input').is(":checked") if isChecked - value = @getState().createValueByTypeAndKind @valueType, @valueKind + value = @getState().getOrCreateValueByTypeAndKind @valueType, @valueKind value.set codeValue: @code codeType: @codeType @@ -45,14 +45,14 @@ class window.ACASFormCodeValueCheckboxController extends ACASFormAbstractFieldCo @getState().get('lsValues').add value else # Find existing value with codeValue matching this controller's code, and mark as ignored. - value = @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code}) + value = @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code, ignored: false}) value.set ignored: true super() renderModelContent: => # Check for value matching this controller's code, and mark as checked if found - if @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code})? + if @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code, ignored: false})? @$('input').attr 'checked', 'checked' else @$('input').removeAttr 'checked' From a5cf0bcb695ceabc16797fe0bf055f199415947c Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Fri, 3 Jan 2020 15:53:11 -0800 Subject: [PATCH 505/576] Add a proper listener to createNewValue in the right state when StateTableForm values get updated --- .../src/client/ACASFormStateTable.coffee | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 1b2b15374..6c85021ed 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -676,6 +676,14 @@ class window.ACASFormStateTableFormController extends Backbone.View newKey = keyBase + value.cid value.set key: newKey @thingRef.set newKey, value + # Deep copy the field modelDefaults and change the key to the newKey + newField = $.extend( true, {}, field) + newField.modelDefaults.key = newKey + # Save the modelDefaults to the Thing's defaultValues with the new key + # This allows @createNewValue to find the correct modelDefaults when creating a new value should this value be edited + @thingRef.lsProperties.defaultValues.push newField.modelDefaults + # Add a new listener to value changes to create a new value in the proper state + @listenTo value, 'createNewValue', @createNewValue opts = modelKey: newKey @@ -749,3 +757,22 @@ class window.ACASFormStateTableFormController extends Backbone.View return rowValues[0].get('numericValue') else return null + + createNewValue: (vKind, newVal, key) => + state = @getStateForRow() + # Get the modelDefaults for this key that was populated above during setupForm + valInfo = _.where(@thingRef.lsProperties.defaultValues, {key: key})[0] + @thingRef.unset(key) + newValue = state.createValueByTypeAndKind valInfo['type'], valInfo['kind'] + newValue.set valInfo['type'], newVal + newValue.set + unitKind: valInfo['unitKind'] + unitType: valInfo['unitType'] + codeKind: valInfo['codeKind'] + codeType: valInfo['codeType'] + codeOrigin: valInfo['codeOrigin'] + value: newVal + # Replace the Thing's reference to the old ignored value with a reference to the newValue + @thingRef.set key, newValue + # Add a listener in case this new value is changed again + @listenTo newValue, 'createNewValue', @createNewValue From e2c11650f3731da744cb99d5376748147fa03c1e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 6 Jan 2020 09:19:25 -0800 Subject: [PATCH 506/576] fixes #630 Placeholder text in form table cells, default value in thing definition --- .../src/client/ACASFormStateTable.coffee | 15 ++++++++----- modules/ServerAPI/src/client/Label.coffee | 22 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index 1b2b15374..e17f121ec 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -141,6 +141,11 @@ class window.ACASFormStateTableController extends Backbone.View colOpts = data: val.modelDefaults.kind colOpts.readOnly = if val.fieldSettings.readOnly? then val.fieldSettings.readOnly else false colOpts.wordWrap = true + + # put a value or placeholder in place + if val.fieldSettings.placeholder? + colOpts.placeholder = if _.isFunction(val.fieldSettings.placeholder) then val.fieldSettings.placeholder() else val.fieldSettings.placeholder + if val.modelDefaults.type == 'numericValue' colOpts.type = 'numeric' if val.fieldSettings.fieldFormat? @@ -283,7 +288,7 @@ class window.ACASFormStateTableController extends Backbone.View rowValue = newState.createValueByTypeAndKind 'numericValue', @rowNumberKind rowValue.set numericValue: row for valueDef in @tableDef.values - newValue = newState.createValueByTypeAndKind valueDef.modelDefaults.type, valueDef.modelDefaults.kind + newValue = newState.createValueByTypeAndKind valueDef.modelDefaults.type, valueDef.modelDefaults.kind, valueDef.modelDefaults.value if valueDef.modelDefaults.unitType? newValue.set unitType: valueDef.modelDefaults.unitType if valueDef.modelDefaults.unitKind? @@ -333,7 +338,7 @@ class window.ACASFormStateTableController extends Backbone.View cols = [] for valDef in @tableDef.values cellInfo = [] - value = state.getOrCreateValueByTypeAndKind valDef.modelDefaults.type, valDef.modelDefaults.kind + value = state.getOrCreateValueByTypeAndKind valDef.modelDefaults.type, valDef.modelDefaults.kind, valDef.modelDefaults.value if valDef.modelDefaults.type == 'codeValue' if valDef.fieldSettings.fieldType == 'stringValue' displayVal = value.get 'codeValue' @@ -408,7 +413,7 @@ class window.ACASFormStateTableController extends Backbone.View valueDefs = _.filter @tableDef.values, (def) -> def.modelDefaults.kind == attr valueDef = valueDefs[0] - value = state.getOrCreateValueByTypeAndKind valueDef.modelDefaults.type, valueDef.modelDefaults.kind + value = state.getOrCreateValueByTypeAndKind valueDef.modelDefaults.type, valueDef.modelDefaults.kind, valueDef.modelDefaults.value unless value.isNew() oldValueAttr = value.get(valueDef.modelDefaults.type) value = @cloneValueAndIgnoreOld state, value @@ -664,7 +669,7 @@ class window.ACASFormStateTableFormController extends Backbone.View state = @getStateForRow() for field in @valueDefs - value = state.getOrCreateValueByTypeAndKind field.modelDefaults.type, field.modelDefaults.kind + value = state.getOrCreateValueByTypeAndKind field.modelDefaults.type, field.modelDefaults.kind, field.modelDefaults.value if field.modelDefaults.codeType? value.set 'codeType', field.modelDefaults.codeType if field.modelDefaults.codeKind? @@ -683,7 +688,7 @@ class window.ACASFormStateTableFormController extends Backbone.View formLabel: field.fieldSettings.formLabel formLabelOrientation: field.fieldSettings.formLabelOrientation formLabelTooltip: field.fieldSettings.formLabelTooltip - placeholder: field.fieldSettings.placeholder + placeholder: if _.isFunction(field.fieldSettings.placeholder) then field.fieldSettings.placeholder() else field.fieldSettings.placeholder required: field.fieldSettings.required url: field.fieldSettings.url thingRef: @thingRef diff --git a/modules/ServerAPI/src/client/Label.coffee b/modules/ServerAPI/src/client/Label.coffee index 2f50c94a8..2009f3d4b 100644 --- a/modules/ServerAPI/src/client/Label.coffee +++ b/modules/ServerAPI/src/client/Label.coffee @@ -245,17 +245,22 @@ class window.State extends Backbone.Model getFirstValueOfKind: (kind) -> @get('lsValues').findWhere lsKind: kind - getOrCreateValueByTypeAndKind: (vType, vKind) -> + getOrCreateValueByTypeAndKind: (vType, vKind, vValue) -> descVals = @getValuesByTypeAndKind vType, vKind descVal = descVals[0] #TODO should do something smart if there are more than one unless descVal? - descVal = @createValueByTypeAndKind(vType, vKind) + descVal = @createValueByTypeAndKind(vType, vKind, vValue) return descVal - createValueByTypeAndKind: (vType, vKind) -> + createValueByTypeAndKind: (vType, vKind, vValue) -> descVal = new Value lsType: vType lsKind: vKind + if vValue? + if !_.isFunction(vValue) + descVal.set 'value', vValue + else + descVal.set 'value', vValue() @get('lsValues').add descVal descVal.on 'change', => @trigger('change') @@ -285,12 +290,12 @@ class window.StateList extends Backbone.Collection mState = @createStateByTypeAndKind sType, sKind return mState - getOrCreateValueByTypeAndKind: (sType, sKind, vType, vKind) -> + getOrCreateValueByTypeAndKind: (sType, sKind, vType, vKind, vValue) -> metaState = @getOrCreateStateByTypeAndKind sType, sKind descVals = metaState.getValuesByTypeAndKind vType, vKind descVal = descVals[0] #TODO should do something smart if there are more than one unless descVal? - descVal = @createValueByTypeAndKind(sType, sKind, vType, vKind) + descVal = @createValueByTypeAndKind(sType, sKind, vType, vKind, vValue) return descVal createStateByTypeAndKind: (sType, sKind) -> @@ -303,10 +308,15 @@ class window.StateList extends Backbone.Collection return mState - createValueByTypeAndKind: (sType, sKind, vType, vKind) -> + createValueByTypeAndKind: (sType, sKind, vType, vKind, vValue) -> descVal = new Value lsType: vType lsKind: vKind + if vValue? + if !_.isFunction(vValue) + descVal.set 'value', vValue + else + descVal.set 'value', vValue() metaState = @getOrCreateStateByTypeAndKind sType, sKind metaState.get('lsValues').add descVal descVal.on 'change', => From 9588a87827c7ffa65042f45f73c2c77e01f934dc Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 6 Jan 2020 17:20:48 -0800 Subject: [PATCH 507/576] fixes #619 if the table is rerendered then we should destroy all of its current form controllers --- modules/Components/src/client/ACASFormStateTable.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index a01295aea..3ade24b22 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -43,6 +43,12 @@ class window.ACASFormStateTableController extends Backbone.View @callWhenSetupComplete = @completeRenderModelContent completeRenderModelContent: -> + # Destroy all current form controllers as they will be recreated when rendering the state + if @stateTableFormControllersCollection? + @stateTableFormControllersCollection.forEach (controller) -> + controller.remove() + controller.unbind() + for state in @getCurrentStates() @renderState state From ce9ecfdffd9e8ef006e3ed70f4001a94e58404b4 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 7 Jan 2020 07:12:48 -0800 Subject: [PATCH 508/576] #634 set cells to readonly when disabling all inputs --- modules/Components/src/client/ACASFormStateTable.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index d3d478d22..ce5da242e 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -568,6 +568,10 @@ class window.ACASFormStateTableController extends Backbone.View readOnly: true contextMenu: false comments: false + cells: (row, col, prop) => + cellProperties = {readOnly: true} + return cellProperties; + #Other options I decided not to use # disableVisualSelection: true # manualColumnResize: false @@ -583,6 +587,10 @@ class window.ACASFormStateTableController extends Backbone.View readOnly: false contextMenu: contextMenu comments: true + cells: (row, col, prop) => + cellProperties = {} + return cellProperties; + #Other options I decided not to use # disableVisualSelection: false # manualColumnResize: true From 14b6acd9f77bb404d0e299ba115e1b18424191b5 Mon Sep 17 00:00:00 2001 From: Brian Frost Date: Tue, 7 Jan 2020 14:49:43 -0800 Subject: [PATCH 509/576] fix new checkbox value creation to find the correct value and also tolerate multiple toggle events and only create one value --- .../ACASFormMultiCodeValueCheckbox.coffee | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee index 86d54a4d3..d9113247e 100644 --- a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.coffee @@ -32,17 +32,25 @@ class window.ACASFormCodeValueCheckboxController extends ACASFormAbstractFieldCo @userInputEvent = true isChecked = @$('input').is(":checked") if isChecked - value = @getState().getOrCreateValueByTypeAndKind @valueType, @valueKind - value.set - codeValue: @code - codeType: @codeType - codeKind: @codeKind - codeOrigin: @codeOrigin - ignored: false - newKey = @keyBase + value.cid - value.set key: newKey - @thingRef.set newKey, value - @getState().get('lsValues').add value + # Checkbox was just checked. Check for a new / unsaved ignored value with matching type/kind/code + value = @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code, ignored: true, id: undefined}) + if value? + # This means the box was toggled on and off multiple times, so we just flip the value to non-ignored + value.set + ignored: false + else + # Create a new value + value = @getState().createValueByTypeAndKind @valueType, @valueKind + value.set + codeValue: @code + codeType: @codeType + codeKind: @codeKind + codeOrigin: @codeOrigin + ignored: false + newKey = @keyBase + value.cid + value.set key: newKey + @thingRef.set newKey, value + @getState().get('lsValues').add value else # Find existing value with codeValue matching this controller's code, and mark as ignored. value = @getState().get('lsValues').findWhere({lsType: @valueType, lsKind: @valueKind, codeValue: @code, ignored: false}) From 12cead63a84831ac0d80684229b00570e155b8b3 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 7 Jan 2020 15:08:16 -0800 Subject: [PATCH 510/576] styling improvments for acas form multi code value checkboxes --- .../src/client/ACASFormFields.coffee | 5 ++++- .../client/ACASFormMultiCodeValueCheckbox.html | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 2d9c0ee43..37e5a0226 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -113,7 +113,10 @@ class window.ACASFormAbstractFieldController extends Backbone.View @required = if @options.required? then @options.required else false setFormLabel: (value) -> - @$('label').html value + if @$('label .label-text').length + @$('label .label-text').html value + else + @$('label').html value addFormLabelClass: (value) -> @$('label').addClass value diff --git a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html index e64dfb12f..f34ea9412 100644 --- a/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html +++ b/modules/Components/src/client/ACASFormMultiCodeValueCheckbox.html @@ -1,9 +1,17 @@ + + \ No newline at end of file diff --git a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee index 9c08adaa8..e97be7080 100644 --- a/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee +++ b/modules/Components/src/client/ACASFormStateDisplayUpdate.coffee @@ -357,6 +357,7 @@ class window.ACASFormStateDisplayValueEditController extends Backbone.View when 'numericValue' then @newField = new ACASFormLSNumericValueFieldController opts when 'codeValue' then @newField = new ACASFormLSCodeValueFieldController opts when 'stringValue' then @newField = new ACASFormLSStringValueFieldController opts + when 'urlValue' then @newField = new ACASFormLSURLValueFieldController opts when 'dateValue' then @newField = new ACASFormLSDateValueFieldController opts @$('.bv_valueField').append @newField.render().el diff --git a/modules/Components/src/client/ACASFormStateTable.coffee b/modules/Components/src/client/ACASFormStateTable.coffee index ce5da242e..882d40f2e 100644 --- a/modules/Components/src/client/ACASFormStateTable.coffee +++ b/modules/Components/src/client/ACASFormStateTable.coffee @@ -752,6 +752,7 @@ class window.ACASFormStateTableFormController extends Backbone.View opts.labelType = field.fieldSettings.labelType newField = new ACASFormLSThingInteractionFieldController opts when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts + when 'urlValue' then newField = new ACASFormLSURLValueFieldController opts when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts when 'locationTree' diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 0b3766acc..31109f869 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -183,6 +183,7 @@ class window.AbstractThingFormController extends AbstractFormController else newField = new ACASFormLSThingInteractionFieldController opts when 'stringValue' then newField = new ACASFormLSStringValueFieldController opts + when 'urlValue' then newField = new ACASFormLSURLValueFieldController opts when 'dateValue' then newField = new ACASFormLSDateValueFieldController opts when 'fileValue' then newField = new ACASFormLSFileValueFieldController opts when 'booleanValue' then newField = new ACASFormLSBooleanFieldController opts From 941c3e0a31d903bb41022d8eb0a3eed245537de5 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 27 Apr 2020 17:02:44 -0700 Subject: [PATCH 549/576] fixes #685 option to display files inline for image files --- modules/Components/src/client/ACASFormFields.coffee | 11 +++++++++-- .../src/client/AbstractFormController.coffee | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index e7f448584..6d815d7b6 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -697,7 +697,11 @@ class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldCon @allowedFileTypes = @options.allowedFileTypes else @allowedFileTypes = ['csv','xlsx','xls','png','jpeg'] - + if @options.displayInline? + @displayInline = @options.displayInline + else + @displayInline = false + render: -> super() @setupFileController() @@ -722,7 +726,10 @@ class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldCon displayText = @getModel().get('comments') if !displayText? displayText = fileValue - @$('.bv_file').html ''+displayText+'' + if @displayInline + @$('.bv_file').html ''+ displayText+'' + else + @$('.bv_file').html ''+displayText+'' @$('.bv_deleteSavedFile').show() createNewFileChooser: -> diff --git a/modules/Components/src/client/AbstractFormController.coffee b/modules/Components/src/client/AbstractFormController.coffee index 0b3766acc..a57a7d465 100644 --- a/modules/Components/src/client/AbstractFormController.coffee +++ b/modules/Components/src/client/AbstractFormController.coffee @@ -151,6 +151,7 @@ class window.AbstractThingFormController extends AbstractFormController firstSelectText: field.fieldSettings.firstSelectText modelDefaults: field.modelDefaults allowedFileTypes: field.fieldSettings.allowedFileTypes + displayInline: field.fieldSettings.displayInline extendedLabel: field.fieldSettings.extendedLabel tabIndex: field.fieldSettings.tabIndex toFixed: field.fieldSettings.toFixed From 62a1aed830d901bb82ebf2bc65e1f635c52f7042 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 27 Apr 2020 17:04:15 -0700 Subject: [PATCH 550/576] fixes #684 adding ACASFormLSURLValueFieldController --- .../src/client/ACASFormFields.coffee | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index e7f448584..234e4323a 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -795,4 +795,47 @@ class window.ACASFormLSBooleanFieldController extends ACASFormAbstractFieldContr @$('input').removeAttr 'checked' else @$('input').attr 'checked', 'checked' - super() \ No newline at end of file + super() + + + +class window.ACASFormLSURLValueFieldController extends ACASFormAbstractFieldController + ### + Launching controller must: + - Initialize the model with an LSValue + Do whatever else is required or optional in ACASFormAbstractFieldController + ### + + template: _.template($("#ACASFormLSURLValueFieldView").html()) + + events: -> + "click .bv_linkBtn": "handleLinkBtnClicked" + + handleInputChanged: => + @clearError() + @userInputEvent = true + value = UtilityFunctions::getTrimmedInput(@$('input')) + if value == "" + @setEmptyValue() + else + @getModel().set + value: value + ignored: false + super() + + setEmptyValue: -> + @getModel().set + value: null + ignored: true + + setInputValue: (inputValue) -> + @$('input').val inputValue + + handleLinkBtnClicked: => + url = UtilityFunctions::getTrimmedInput(@$('input')) + window.open(url); + + + renderModelContent: => + @$('input').val @getModel().get('value') + super() From dd258cd117ffb0d90ff7d5bf9fb122ff9b105063 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 27 Apr 2020 17:07:15 -0700 Subject: [PATCH 551/576] fixes #686 fix for ls kinds with multiple spaces breaking the html clob viewer (replaces all spaces not just the first one) --- modules/Components/src/client/ACASFormFields.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index e7f448584..e03a13207 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -565,7 +565,7 @@ class window.ACASFormLSHTMLClobValueFieldController extends ACASFormAbstractFiel setupTinyMCE: -> mdl = @getModel() - cname = mdl.get('lsKind').replace(" ","")+"_"+mdl.cid + cname = mdl.get('lsKind').split(' ').join('')+"_"+mdl.cid selector = "."+cname @$('.bv_wysiwygEditor').addClass cname @wysiwygEditor = tinymce.init From 84dc52b0e80e62ff16cfc17a186717ca234fd632 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 27 Apr 2020 17:10:22 -0700 Subject: [PATCH 552/576] fixes #690 disable file value method for acas forms --- modules/Components/src/client/ACASFormFields.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index e7f448584..5fb64823f 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -750,6 +750,9 @@ class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldCon value: nameOnServer ignored: false + disableInput: -> + @$('.bv_deleteSavedFile').hide() + handleFileRemoved: => @setEmptyValue() @checkEmptyAndRequired() From dd3b67887518e27ffa2b44dfdd09bd8791406c1c Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 29 Apr 2020 16:34:09 -0700 Subject: [PATCH 553/576] curve curator fixes --- modules/CurveAnalysis/src/server/CurveDetail.R | 2 +- .../CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/CurveAnalysis/src/server/CurveDetail.R b/modules/CurveAnalysis/src/server/CurveDetail.R index e3156fafb..3d5946daf 100644 --- a/modules/CurveAnalysis/src/server/CurveDetail.R +++ b/modules/CurveAnalysis/src/server/CurveDetail.R @@ -20,7 +20,7 @@ handle_response <- function(http_response_code, response) { get_curve_detail <- function() { postData <- rawToChar(receiveBin()) - if(is.null(postData)) { + if(is.null(postData) || postData == "") { myMessenger$logger$debug(paste0('getting curve detail with get json: ', GET)) myMessenger$capture_output("detail <- racas::api_doseResponse_get_curve_detail(GET)", userError = paste0("There was an error retrieving detail for '", GET, "'")) if(myMessenger$hasErrors()) { diff --git a/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee b/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee index 44a4e94ba..ec4a96f34 100644 --- a/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee +++ b/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee @@ -7,6 +7,7 @@ exports.setupAPIRoutes = (app, loginRoutes) -> exports.setupRoutes = (app, loginRoutes) -> app.get '/api/curves/stubs/:exptCode', loginRoutes.ensureAuthenticated, exports.getCurveStubs + app.get '/api/curve/detail/:id', loginRoutes.ensureAuthenticated, exports.getCurveDetail app.post '/api/curves/detail', loginRoutes.ensureAuthenticated, exports.getCurveDetailCurveIds app.put '/api/curve/detail/:id', loginRoutes.ensureAuthenticated, exports.updateCurveDetail app.post '/api/curve/stub/:id', loginRoutes.ensureAuthenticated, exports.updateCurveStub From 68bb1c3ccbd508040461dd02ea62db1e6ceacb5e Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Sat, 2 May 2020 19:29:41 -0700 Subject: [PATCH 554/576] longer timeout on dose response fits --- .../src/server/routes/DoseResponseFitRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CurveAnalysis/src/server/routes/DoseResponseFitRoutes.coffee b/modules/CurveAnalysis/src/server/routes/DoseResponseFitRoutes.coffee index 49286ec7e..96d499377 100644 --- a/modules/CurveAnalysis/src/server/routes/DoseResponseFitRoutes.coffee +++ b/modules/CurveAnalysis/src/server/routes/DoseResponseFitRoutes.coffee @@ -3,7 +3,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/doseResponseCurveFit', loginRoutes.ensureAuthenticated, exports.fitDoseResponse exports.fitDoseResponse = (request, response) -> - request.connection.setTimeout 600000 + request.connection.setTimeout 86400000 serverUtilityFunctions = require './ServerUtilityFunctions.js' response.writeHead(200, {'Content-Type': 'application/json'}); From 6015de685c31ad77d6b240d979833afed05047ce Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 6 May 2020 13:06:32 -0700 Subject: [PATCH 555/576] Fix for authorized label sequences --- .../src/server/routes/ACASLabelSequencesRoutes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee b/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee index 1701aeed2..9333eab00 100644 --- a/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee +++ b/modules/ACASLabelSequences/src/server/routes/ACASLabelSequencesRoutes.coffee @@ -50,7 +50,7 @@ exports.getAuthorizedLabelSequencesInternal = (req, callback) -> callback labelSequenceTestJSON.labelSequenceArray else roles = req.session.passport.user.roles - baseurl = config.all.client.service.persistence.fullpath+"labelsequences/getAuthorizedLabelSequences" + baseurl = config.all.client.service.persistence.fullpath+"labelsequences/getAuthorizedLabelSequences?" if req.query?.thingTypeAndKind? baseurl += "&thingTypeAndKind=#{req.query.thingTypeAndKind}" if req.query?.labelTypeAndKind? From 411279f3692af5b5787a29f511bb4cf6bf9f7624 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 12 May 2020 13:20:03 -0700 Subject: [PATCH 556/576] fixes #694 add route to fetch all authorized lots --- .../src/server/routes/CmpdRegRoutes.coffee | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee index 7cdb8d3ee..e00c913b2 100644 --- a/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee +++ b/modules/CmpdReg/src/server/routes/CmpdRegRoutes.coffee @@ -3,6 +3,7 @@ exports.setupAPIRoutes = (app) -> app.get '/cmpdReg/scientists', exports.getScientists app.get '/cmpdReg/metalots/corpName/[\\S]*', exports.getMetaLot app.post '/cmpdReg/metalots', exports.metaLots + app.get '/cmpdReg/parentLot/getAllAuthorizedLots', exports.getAllAuthorizedLots exports.setupRoutes = (app, loginRoutes) -> app.get '/cmpdReg', loginRoutes.ensureAuthenticated, exports.cmpdRegIndex @@ -50,6 +51,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/cmpdReg/ketcher/calculate_cip', loginRoutes.ensureAuthenticated, exports.ketcherCalculateCip app.get '/cmpdReg/labelPrefixes', loginRoutes.ensureAuthenticated, exports.getAuthorizedPrefixes app.get '/cmpdReg/parentLot/getLotsByParent', loginRoutes.ensureAuthenticated, exports.getAPICmpdReg + app.get '/cmpdReg/parentLot/getAllAuthorizedLots', loginRoutes.ensureAuthenticated, exports.getAllAuthorizedLots _ = require 'underscore' request = require 'request' @@ -266,6 +268,37 @@ exports.searchCmpds = (req, resp) -> resp.end JSON.stringify {error: "something went wrong :("} ) + +exports.getAllAuthorizedLots = (req, resp) -> + req.setTimeout 86400000 + authorRoutes = require './AuthorRoutes.js' + authorRoutes.allowedProjectsInternal req.user, (statusCode, allowedUserProjects) -> + _ = require "underscore" + allowedProjectCodes = _.pluck(allowedUserProjects, "code") + requestJSON = allowedProjectCodes + console.log requestJSON + cmpdRegCall = config.all.client.service.cmpdReg.persistence.fullpath + '/parentLot/getLotsByProjectsList' + request( + method: 'POST' + url: cmpdRegCall + body: requestJSON + json: true + timeout: 6000000 + , (error, response, json) => + if !error && response.statusCode == 200 + resp.setHeader('Content-Type', 'application/json') + resp.end JSON.stringify json + else + console.log resp.stat + console.log 'got ajax error trying to get all lots' + console.log error + console.log json + console.log response + resp.statusCode = 500 + console.log response.statusCode + resp.end JSON.stringify {error: "something went wrong :("} + ) + exports.getStructureImage = (req, resp) -> imagePath = (req.originalUrl).replace /\/cmpdreg\/structureimage/, "" cmpdRegCall = config.all.client.service.cmpdReg.persistence.basepath + '/structureimage' + imagePath From 02466274bf26d28bad904335ec01a79a70dd45e4 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 13 May 2020 15:25:39 -0700 Subject: [PATCH 557/576] make sure validation summary page is hiddent when showing purge files --- modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee index 4823ddd3a..eb924c3ed 100644 --- a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee +++ b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee @@ -1118,6 +1118,7 @@ class window.CmpdRegBulkLoaderAppController extends Backbone.View unless @$('.bv_purgeFiles').is(':visible') @$('.bv_bulkReg').hide() @$('.bv_bulkRegSummary').hide() + @$('.bv_bulkValSummary').hide() @$('.bv_purgeFiles').show() @setupPurgeFilesController() @$('.bv_adminDropdown').dropdown('toggle') From 9e343e18c30a666a28d33484c21f575b0d4ad5aa Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 13 May 2020 16:11:49 -0700 Subject: [PATCH 558/576] Fixing issue with saving templates and unable to register compounds --- modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee index eb924c3ed..16400082f 100644 --- a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee +++ b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee @@ -689,7 +689,7 @@ class window.AssignSdfPropertiesController extends Backbone.View data: dataToPost success: (response) => if response.id? - @validateCompounds() + @registerCompounds() else @handleSaveTemplateError() error: (err) => From 293b5b75b4628a26d770d9bc9714b26ff19e4836 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 22 May 2020 13:01:20 -0700 Subject: [PATCH 559/576] fixes #696 optional with parameter to fetch additonal thing depth or formats from generic search route --- modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee index d420439e8..24398e5fe 100644 --- a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee @@ -664,6 +664,8 @@ exports.genericThingSearch = (req, resp) -> typeFilter = "lsType=" + req.query.lsType if req.query.lsKind? kindFilter = "lsKind=" + req.query.lsKind + if req.query.with? + format = "with=#{req.query.with}" searchTerm = "q=" + req.params.searchTerm searchParams = "" @@ -671,6 +673,8 @@ exports.genericThingSearch = (req, resp) -> searchParams += typeFilter + "&" if kindFilter? searchParams += kindFilter + "&" + if format? + searchParams += format + "&" searchParams += searchTerm baseurl = config.all.client.service.persistence.fullpath+"lsthings/search?"+searchParams From 8e488bad801f8a42262698302062d4903b2c6f24 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 27 May 2020 08:53:51 -0700 Subject: [PATCH 560/576] Fixing issue with project mapping. It should only allow you to work in 1 mode (project mapped in - project dropdown off, OR project not mapped in - project dropdown on) --- .../src/client/CmpdRegBulkLoader.coffee | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee index 16400082f..b52ea69f4 100644 --- a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee +++ b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee @@ -704,18 +704,17 @@ class window.AssignSdfPropertiesController extends Backbone.View @$('.bv_errorMessage').html "An error occurred while trying to save the template. The compounds have not been registered yet.
    Please try again or contact an administrator." addProjectToMappingsPayLoad: -> - if @project != false - @assignedPropertiesList.remove(@assignedPropertiesListController.collection.findWhere({dbProperty: "Project"})) - if @project? - dbProjectProperty = new AssignedProperty - dbProperty: "Project" - required: true - sdfProperty: null - defaultVal: @project - @assignedPropertiesListController.collection.add dbProjectProperty + @assignedPropertiesList.remove(@assignedPropertiesListController.collection.findWhere({dbProperty: "Project"})) + dbProjectProperty = new AssignedProperty + dbProperty: "Project" + required: true + sdfProperty: null + defaultVal: @project + @assignedPropertiesListController.collection.add dbProjectProperty registerCompounds: -> - @addProjectToMappingsPayLoad() + if window.conf.cmpdReg.showProjectSelect + @addProjectToMappingsPayLoad() dataToPost = fileName: @fileName mappings: JSON.parse(JSON.stringify(@assignedPropertiesListController.collection.models)) @@ -741,7 +740,8 @@ class window.AssignSdfPropertiesController extends Backbone.View dataType: 'json' validateCompounds: -> - @addProjectToMappingsPayLoad() + if window.conf.cmpdReg.showProjectSelect + @addProjectToMappingsPayLoad() dataToPost = fileName: @fileName mappings: JSON.parse(JSON.stringify(@assignedPropertiesListController.collection.models)) From 824a583e139ad2deb8591aabb1889aa27e116d0b Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 4 Jun 2020 10:33:54 -0700 Subject: [PATCH 561/576] sort before applying colors --- modules/CurveAnalysis/src/server/renderCurve.R | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/CurveAnalysis/src/server/renderCurve.R b/modules/CurveAnalysis/src/server/renderCurve.R index 0c85af3b3..ffb0ad0bf 100644 --- a/modules/CurveAnalysis/src/server/renderCurve.R +++ b/modules/CurveAnalysis/src/server/renderCurve.R @@ -23,16 +23,14 @@ renderCurve <- function(getParams) { # "#149BEDFF", "#A1C720FF", "#FEC10BFF", "#16A08CFF", "#9A703EFF") plotColors <- trimws(strsplit(racas::applicationSettings$server.curveRender.plotColors,",")[[1]]) if(!is.na(parsedParams$colorBy)) { - if(parsedParams$colorBy == "protocol") { - colorCategories <- suppressWarnings(unique(fitData[ , c("protocol_label"), with = FALSE])[, color:=plotColors][ , name:=protocol_label]) - fitData <- merge(fitData, colorCategories, by = "protocol_label") - } else if(parsedParams$colorBy == "experiment") { - colorCategories <- suppressWarnings(unique(fitData[ , c("experiment_label"), with = FALSE])[, color:=plotColors][ , name:=experiment_label]) - fitData <- merge(fitData, colorCategories, by = "experiment_label") - } else if(parsedParams$colorBy == "batch") { - colorCategories <- suppressWarnings(unique(fitData[ , c("batch_code"), with = FALSE])[, color:= plotColors][ , name:=batch_code]) - fitData <- merge(fitData, colorCategories, by = "batch_code") - } + key <- switch(parsedParams$colorBy, + "protocol" = "protocol_label", + "experiment" = "experiment_label", + "batch" = "batch_code" + ) + uniqueKeys <- setkeyv(unique(fitData[ , key, with = FALSE]), key) + colorCategories <- suppressWarnings(uniqueKeys[, color:=plotColors][ , name:=get(key)]) + fitData <- merge(fitData, colorCategories, by = key) } fitData <- fitData[exists("category") && (!is.null(category) & category %in% c("inactive","potent")), c("fittedMax", "fittedMin") := { From 108b29afca46697381452359043e25fc491d283b Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 9 Jun 2020 14:55:50 -0700 Subject: [PATCH 562/576] fixing width issue with select2 --- modules/Components/src/client/PickList.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/Components/src/client/PickList.coffee b/modules/Components/src/client/PickList.coffee index d42394447..c69de068f 100644 --- a/modules/Components/src/client/PickList.coffee +++ b/modules/Components/src/client/PickList.coffee @@ -618,7 +618,6 @@ class window.ThingLabelComboBoxController extends PickListSelect2Controller placeholder: @placeholder openOnEnter: false allowClear: true - width: "100%" ajax: url: (params) => if !params.term? From ea7512f5d01a587006f749e5b1b8a3d03718a9f6 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 9 Jun 2020 15:26:22 -0700 Subject: [PATCH 563/576] fixes #698 date icon clickable in readonly mode, add disableInput method which is called on readonly fields --- modules/Components/src/client/ACASFormFields.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 6ddfdc5ba..8bc705776 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -677,6 +677,8 @@ class window.ACASFormLSDateValueFieldController extends ACASFormAbstractFieldCon handleDateIconClicked: => @$('input').datepicker( "show" ) + disableInput: => + $(@el).off('click', '.bv_dateIcon'); class window.ACASFormLSFileValueFieldController extends ACASFormAbstractFieldController ### From 9d5bed793b1d2033247b264ad82be3d5fc1f7607 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 10 Jun 2020 08:06:19 -0700 Subject: [PATCH 564/576] fixes #700 properly remove the disable attribute when enabling the html clob fields in acas forms --- modules/Components/src/client/ACASFormFields.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 6ddfdc5ba..3c2e966fa 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -578,7 +578,10 @@ class window.ACASFormLSHTMLClobValueFieldController extends ACASFormAbstractFiel @editor.setContent @contentToLoad if @disableEditor? @editor.getBody().setAttribute('contenteditable', !@disableEditor) - @editor.getBody().setAttribute('disabled', @disableEditor) + if @disableEditor + @editor.getBody().setAttribute('disabled', true) + else + @editor.getBody().removeAttribute('disabled') editor.on 'change', (e) => @textChanged editor.getContent() From 560ad56d8cb58d5f4c6fbf87a6af4f5f997f1daf Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 19 Jun 2020 14:48:31 -0700 Subject: [PATCH 565/576] fixes #708 put things in bulk and update their file values --- .../server/routes/ThingServiceRoutes.coffee | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee index 24398e5fe..98427177b 100644 --- a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee @@ -25,6 +25,7 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.post '/api/bulkPostThings', exports.bulkPostThings app.put '/api/bulkPutThings', exports.bulkPutThings app.post '/api/bulkPostThingsSaveFile', exports.bulkPostThingsSaveFile + app.put '/api/bulkPutThingsSaveFile', exports.bulkPutThingsSaveFile exports.setupRoutes = (app, loginRoutes) -> @@ -55,6 +56,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.post '/api/bulkPostThings', loginRoutes.ensureAuthenticated, exports.bulkPostThings app.put '/api/bulkPutThings', loginRoutes.ensureAuthenticated, exports.bulkPutThings app.post '/api/bulkPostThingsSaveFile', loginRoutes.ensureAuthenticated, exports.bulkPostThingsSaveFile + app.put '/api/bulkPutThingsSaveFile', loginRoutes.ensureAuthenticated, exports.bulkPutThingsSaveFile request = require 'request' config = require '../conf/compiled/conf.js' @@ -433,6 +435,69 @@ exports.postThingParent = (req, resp) -> exports.postThingBatch = (req, resp) -> postThing true, req, resp +exports.bulkPutThingsSaveFile = (req, resp) -> + + # First save all the ls things that come in + exports.bulkPutThingsInternal req.body, (response) => + + # Local function checkFilesAndUpdate + checkFilesAndUpdate = (thing, callback) -> + + # Check if there are any files to save + fileVals = serverUtilityFunctions.getFileValuesFromEntity thing, false + filesToSave = fileVals.length + console.log("got #{filesToSave} file values to check for updates") + + # Function called after final file is saved + completeThingUpdate = (thingToUpdate)-> + updateThing thingToUpdate, false, (updatedThing) -> + callback updatedThing, 200 + + # Function to call after a file is saved + fileSaveCompleted = (passed) -> + if !passed + callback "file move failed", 500 + # Decrement one from the filesToSave and if this is the final file that was saved call + # completeThingUpdate + if --filesToSave == 0 then completeThingUpdate(thing) + + # If there are any files to save, call the customer specific server function which should handle + # saving the file to the correct location and update the thing file value with the correct path + if filesToSave > 0 + prefix = serverUtilityFunctions.getPrefixFromEntityCode thing.codeName + for fv in fileVals + + # Only update new files + if !fv.id? + console.log("file value was updated #{JSON.stringify(fv)}") + # Send just the file value "fv" to the relocateEntityFile function + # relocate entity file is responsible for moving the file and updating the + # file value of thing in memory and later completeThingUpdate will handle persisting + # the change to the db. + csUtilities.relocateEntityFile fv, prefix, thing.codeName, fileSaveCompleted + else + fileSaveCompleted(true) + else + callback thing, 200 + + # If we failed to bulk save the things then just respond + if response.indexOf("saveFailed") > -1 + resp.json response + else + # Loop through the saved ls things and call the checkFilesAndUpdate function + # which should handle doing the correct thing with the files. + lengthThingsToCheck = response.length + i = 0 + resps = [] + for t in response + console.log("running check on files for thing #{JSON.stringify(t)}") + checkFilesAndUpdate t, (response, statusCode) -> + console.log("got response from check files #{statusCode} #{JSON.stringify(response)}") + resps.push response + i++ + if i == lengthThingsToCheck + resp.json resps + exports.putThingInternal = (thing, lsType, lsKind, testMode, callback) -> thingToSave = thing fileVals = serverUtilityFunctions.getFileValuesFromEntity thingToSave, true @@ -921,7 +986,7 @@ exports.bulkPutThingsInternal = (thingArray, callback) -> body: thingArray json: true , (error, response, json) => - console.log "bulkPutThingsInternal" + console.log "bulkPutThingsInternal complete" console.log response.statusCode if !error && response.statusCode == 200 callback json From ce3e7cb58bacf12125bc2d5a7b0a53d7aedcbb38 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 19 Jun 2020 14:54:23 -0700 Subject: [PATCH 566/576] fixes #706 default ls thing bollean form value should be unchecked if not true --- modules/Components/src/client/ACASFormFields.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/Components/src/client/ACASFormFields.coffee b/modules/Components/src/client/ACASFormFields.coffee index 6ddfdc5ba..0b5a1fa00 100644 --- a/modules/Components/src/client/ACASFormFields.coffee +++ b/modules/Components/src/client/ACASFormFields.coffee @@ -801,10 +801,11 @@ class window.ACASFormLSBooleanFieldController extends ACASFormAbstractFieldContr ignored: true renderModelContent: => - if @getModel().get('value') is "false" - @$('input').removeAttr 'checked' - else + # If value is anything other than true (i.e. null), then default to unchecked + if @getModel().get('value') is "true" @$('input').attr 'checked', 'checked' + else + @$('input').removeAttr 'checked' super() From 4ee73d9abfe181440ee9578bb7a2ebd1f2e5b69d Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 19 Jun 2020 14:59:18 -0700 Subject: [PATCH 567/576] fixes #708 add POST request option for rendering curves --- modules/CurveAnalysis/src/server/renderCurve.R | 18 ++++++++++++++---- .../server/routes/CurveCuratorRoutes.coffee | 9 ++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/CurveAnalysis/src/server/renderCurve.R b/modules/CurveAnalysis/src/server/renderCurve.R index ffb0ad0bf..d854a58cd 100644 --- a/modules/CurveAnalysis/src/server/renderCurve.R +++ b/modules/CurveAnalysis/src/server/renderCurve.R @@ -3,7 +3,9 @@ # MEMORY_LIMIT_EXEMPT library(data.table) -renderCurve <- function(getParams) { + +renderCurve <- function(getParams, postData) { + # Redirect to Curator if applicable redirectInfo <- racas::api_get_curve_curator_url(getParams$curveIds, getParams$inTable, globalConnect = TRUE) if(redirectInfo$shouldRedirect == TRUE) { @@ -11,8 +13,15 @@ renderCurve <- function(getParams) { return(HTTP_MOVED_TEMPORARILY) DONE } + + postParams <- NA + saveSession('/tmp/ham') + if(!is.null(postData) && !is.na(postData) && postData != "") { + postParams <- jsonlite::fromJSON(postData) + } # Parse GET Parameters - parsedParams <- racas::parse_params_curve_render_dr(getParams) + parsedParams <- racas::parse_params_curve_render_dr(getParams, postParams) + # GET FIT DATA #fitData <- racas::get_fit_data_curve_id(parsedParams$curveIds) @@ -88,7 +97,7 @@ renderCurve <- function(getParams) { } setContentType("image/png") - setHeader("Content-Disposition", paste0("filename=\"",getParams$curveIds,".png\"")) + setHeader("Content-Disposition", paste0("filename=\"",strtrim(getParams$curveIds,200),".png\"")) t <- tempfile() racas::plotCurve(curveData = data$points, drawIntercept = renderingOptions$drawIntercept, params = data$parameters, fitFunction = renderingOptions$fct, paramNames = renderingOptions$paramNames, drawCurve = TRUE, logDose = logDose, logResponse = logResponse, outFile = t, ymin=parsedParams$yMin, ymax=parsedParams$yMax, xmin=parsedParams$xMin, xmax=parsedParams$xMax, height=parsedParams$height, width=parsedParams$width, showGrid = parsedParams$showGrid, showAxes = parsedParams$showAxes, labelAxes = parsedParams$labelAxes, showLegend=parsedParams$legend, mostRecentCurveColor = parsedParams$mostRecentCurveColor, axes = parsedParams$axes, plotColors = parsedParams$plotColors, curveLwd=parsedParams$curveLwd, plotPoints=parsedParams$plotPoints, connectPoints = renderingOptions$connectPoints, xlabel = parsedParams$xLab, ylabel = parsedParams$yLab) @@ -97,7 +106,8 @@ renderCurve <- function(getParams) { DONE } -renderCurve(getParams = GET) +postData <- rawToChar(receiveBin(-1)) +renderCurve(getParams = GET, postData) diff --git a/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee b/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee index ec4a96f34..0f292b3ad 100644 --- a/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee +++ b/modules/CurveAnalysis/src/server/routes/CurveCuratorRoutes.coffee @@ -4,6 +4,7 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.post '/api/curves/detail', exports.getCurveDetailCurveIds app.get '/api/curve/detail/:id', exports.getCurveDetail app.get '/api/curve/render/*', exports.renderCurve + app.post '/api/curve/render/*', exports.renderCurve exports.setupRoutes = (app, loginRoutes) -> app.get '/api/curves/stubs/:exptCode', loginRoutes.ensureAuthenticated, exports.getCurveStubs @@ -12,6 +13,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.put '/api/curve/detail/:id', loginRoutes.ensureAuthenticated, exports.updateCurveDetail app.post '/api/curve/stub/:id', loginRoutes.ensureAuthenticated, exports.updateCurveStub app.get '/api/curve/render/*', loginRoutes.ensureAuthenticated, exports.renderCurve + app.post '/api/curve/render/*', loginRoutes.ensureAuthenticated, exports.renderCurve app.get '/curveCurator/*', loginRoutes.ensureAuthenticated, exports.curveCuratorIndex exports.getCurveStubs = (req, resp) -> @@ -216,4 +218,9 @@ exports.renderCurve = (req, resp) -> config = require '../conf/compiled/conf.js' redirectQuery = req._parsedUrl.query rapacheCall = config.all.client.service.rapache.fullpath + '/curve/render/dr/?' + redirectQuery - req.pipe(request(rapacheCall)).pipe(resp) + if req.method == 'GET' + req.pipe(request(rapacheCall)).pipe resp + else + req.pipe(request[req.method.toLowerCase()]( + url: rapacheCall + json: req.body)).pipe resp From 23fbf3dc4f67b6f17076ca2a80fec38d4c00c586 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 19 Jun 2020 16:15:16 -0700 Subject: [PATCH 568/576] 708 remove debug line --- modules/CurveAnalysis/src/server/renderCurve.R | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/CurveAnalysis/src/server/renderCurve.R b/modules/CurveAnalysis/src/server/renderCurve.R index d854a58cd..b1c2a734e 100644 --- a/modules/CurveAnalysis/src/server/renderCurve.R +++ b/modules/CurveAnalysis/src/server/renderCurve.R @@ -15,7 +15,6 @@ renderCurve <- function(getParams, postData) { } postParams <- NA - saveSession('/tmp/ham') if(!is.null(postData) && !is.na(postData) && postData != "") { postParams <- jsonlite::fromJSON(postData) } From df36cf5193a52d1327e4dda0eb06badc6dac27d5 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Mon, 22 Jun 2020 13:50:03 -0700 Subject: [PATCH 569/576] add delete thing route --- .../server/routes/ThingServiceRoutes.coffee | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee index 98427177b..b00f31adb 100644 --- a/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee +++ b/modules/ServerAPI/src/server/routes/ThingServiceRoutes.coffee @@ -26,6 +26,7 @@ exports.setupAPIRoutes = (app, loginRoutes) -> app.put '/api/bulkPutThings', exports.bulkPutThings app.post '/api/bulkPostThingsSaveFile', exports.bulkPostThingsSaveFile app.put '/api/bulkPutThingsSaveFile', exports.bulkPutThingsSaveFile + app.delete '/api/things/:lsType/:lsKind/:idOrCodeName', exports.deleteThing exports.setupRoutes = (app, loginRoutes) -> @@ -57,6 +58,7 @@ exports.setupRoutes = (app, loginRoutes) -> app.put '/api/bulkPutThings', loginRoutes.ensureAuthenticated, exports.bulkPutThings app.post '/api/bulkPostThingsSaveFile', loginRoutes.ensureAuthenticated, exports.bulkPostThingsSaveFile app.put '/api/bulkPutThingsSaveFile', loginRoutes.ensureAuthenticated, exports.bulkPutThingsSaveFile + app.delete '/api/things/:lsType/:lsKind/:idOrCodeName', loginRoutes.ensureAuthenticated, exports.deleteThing request = require 'request' config = require '../conf/compiled/conf.js' @@ -994,4 +996,28 @@ exports.bulkPutThingsInternal = (thingArray, callback) -> console.log "got error bulk updating things" console.log error callback JSON.stringify "bulk update things saveFailed: " + JSON.stringify error - ) \ No newline at end of file + ) + +exports.deleteThing = (req, resp) -> + exports.deleteThingInternal req.params.lsType, req.params.lsKind, req.params.idOrCodeName, (status, response) => + resp.statusCode = status + resp.json response + +exports.deleteThingInternal = (lsType, lsKind, idOrCodeName, callback) -> + console.log "deleteThing #{idOrCodeName}" + baseurl = config.all.client.service.persistence.fullpath+"lsthings/#{lsType}/#{lsKind}/#{idOrCodeName}" + console.log baseurl + request( + method: 'DELETE' + url: baseurl + json: true + , (error, response, json) => + console.log "deleteThingInternal complete" + console.log response.statusCode + if !error && response.statusCode == 200 + callback 200, json + else + console.log "got error deleting thing" + console.log error + callback 500, JSON.stringify "delete thing: " + JSON.stringify error + ) From 845e15f8a9cfae97da6be5a58551ff12ea55180d Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 25 Jun 2020 08:14:19 -0700 Subject: [PATCH 570/576] Adding default from email address for email service --- conf/config.properties.example | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/config.properties.example b/conf/config.properties.example index 84191757c..c9ddfb53b 100644 --- a/conf/config.properties.example +++ b/conf/config.properties.example @@ -531,6 +531,7 @@ server.support.smtp.password=null server.support.smtp.auth=false server.support.smtp.tls=false server.support.smtp.ssl=false +server.support.smtp.from= client.basePath=/ From 9d49e17e86dfcb1f141523af7afe0e9da86176b9 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Wed, 8 Jul 2020 16:27:33 -0400 Subject: [PATCH 571/576] fixes #713 ignore db properties when they are marked with ignored: true --- .../src/client/custom/configuration.json | 108 +++++++++--------- .../src/client/CmpdRegBulkLoader.coffee | 26 +++-- 2 files changed, 69 insertions(+), 65 deletions(-) diff --git a/modules/CmpdReg/src/client/custom/configuration.json b/modules/CmpdReg/src/client/custom/configuration.json index 012bfeb9d..9dd58893c 100755 --- a/modules/CmpdReg/src/client/custom/configuration.json +++ b/modules/CmpdReg/src/client/custom/configuration.json @@ -82,60 +82,60 @@ "bulkLoadSettings": { "useProjectRoles": false, "dbProperties": [ - {"name": "CAS Number", "dataType": "string", "required": false}, - {"name": "Lot Absorbance", "dataType": "numeric", "required": false}, - {"name": "Lot Alias", "dateType": "string", "required": false}, - {"name": "Lot Amount Units", "dataType": "string", "required": false}, - {"name": "Lot Amount", "dataType": "numeric", "required": false}, - {"name": "Lot Barcode", "dataType": "string", "required": false}, - {"name": "Lot Boiling Point", "dataType": "numeric", "required": false}, - {"name": "Lot Chemist", "dataType": "string", "required": true}, - {"name": "Lot Color", "dataType": "string", "required": false}, - {"name": "Lot Comments", "dataType": "string", "required": false}, - {"name": "Lot Corp Name", "dataType": "string", "required": false}, - {"name": "Lot Is Virtual", "dataType": "boolean", "required": false}, - {"name": "Lot Lambda", "dataType": "numeric", "required": false}, - {"name": "Lot Melting Point", "dataType": "numeric", "required": false}, - {"name": "Lot Notebook Page", "dataType": "string", "required": false}, - {"name": "Lot Number", "dataType": "numeric", "required": false}, - {"name": "Lot Observed Mass #1", "dataType": "numeric", "required": false}, - {"name": "Lot Observed Mass #2", "dataType": "numeric", "required": false}, - {"name": "Lot Percent ee", "dataType": "numeric", "required": false}, - {"name": "Lot Physical State", "dataType": "string", "required": false}, - {"name": "Lot Purity Measured By", "dataType": "string", "required": false}, - {"name": "Lot Purity Operator", "dataType": "string", "required": false}, - {"name": "Lot Purity", "dataType": "numeric", "required": false}, - {"name": "Lot Retain Location", "dataType": "string", "required": false}, - {"name": "Lot Retain Units", "dataType": "string", "required": false}, - {"name": "Lot Retain", "dataType": "numeric", "required": false}, - {"name": "Lot Salt Abbrev", "dataType": "string", "required": false}, - {"name": "Lot Salt Equivalents", "dataType": "string", "required": false}, - {"name": "Lot Solution Amount Units", "dataType": "string", "required": false}, - {"name": "Lot Solution Amount", "dataType": "numeric", "required": false}, - {"name": "Lot Stock Location", "dataType": "string", "required": false}, - {"name": "Lot Stock Solvent", "dataType": "string", "required": false}, - {"name": "Lot Storage Location", "dataType": "string", "required": false}, - {"name": "Lot Supplier ID", "dataType": "string", "required": false}, - {"name": "Lot Supplier Lot", "dataType": "string", "required": false}, - {"name": "Lot Supplier", "dataType": "string", "required": false}, - {"name": "Lot Synthesis Date", "dataType": "date", "required": false}, - {"name": "Lot Tare Weight Units", "dataType": "string", "required": false}, - {"name": "Lot Tare Weight", "dataType": "numeric", "required": false}, - {"name": "Lot Total Amount Stored Units", "dataType": "string", "required": false}, - {"name": "Lot Total Amount Stored", "dataType": "numeric", "required": false}, - {"name": "Lot Vendor", "dataType": "string", "required": false}, - {"name": "Lot Vendor ID", "dataType": "string", "required": false}, - {"name": "Parent Alias", "dateType": "string", "required": false}, - {"name": "Parent Annotation", "dateType": "string", "required": false}, - {"name": "Parent Comment", "dataType": "string", "required": false}, - {"name": "Parent Common Name", "dataType": "string", "required": false}, - {"name": "Parent Compound Type", "dateType": "string", "required": false}, - {"name": "Parent Corp Name", "dataType": "string", "required": false}, - {"name": "Parent Is Mixture", "dataType": "boolean", "required": false}, - {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false}, - {"name": "Parent Stereo Category", "dataType": "string", "required": true}, - {"name": "Parent Stereo Comment", "dataType": "string", "required": false}, - {"name": "Project", "dataType": "string", "required": false} + {"name": "CAS Number", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Absorbance", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Alias", "dateType": "string", "required": false, "ignored": false}, + {"name": "Lot Amount Units", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Amount", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Barcode", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Boiling Point", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Chemist", "dataType": "string", "required": true, "ignored": false}, + {"name": "Lot Color", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Comments", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Corp Name", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Is Virtual", "dataType": "boolean", "required": false, "ignored": false}, + {"name": "Lot Lambda", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Melting Point", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Notebook Page", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Number", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Observed Mass #1", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Observed Mass #2", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Percent ee", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Physical State", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Purity Measured By", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Purity Operator", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Purity", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Retain Location", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Retain Units", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Retain", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Salt Abbrev", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Salt Equivalents", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Solution Amount Units", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Solution Amount", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Stock Location", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Stock Solvent", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Storage Location", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Supplier ID", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Supplier Lot", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Supplier", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Synthesis Date", "dataType": "date", "required": false, "ignored": false}, + {"name": "Lot Tare Weight Units", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Tare Weight", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Total Amount Stored Units", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Total Amount Stored", "dataType": "numeric", "required": false, "ignored": false}, + {"name": "Lot Vendor", "dataType": "string", "required": false, "ignored": false}, + {"name": "Lot Vendor ID", "dataType": "string", "required": false, "ignored": false}, + {"name": "Parent Alias", "dateType": "string", "required": false, "ignored": false}, + {"name": "Parent Annotation", "dateType": "string", "required": false, "ignored": false}, + {"name": "Parent Comment", "dataType": "string", "required": false, "ignored": false}, + {"name": "Parent Common Name", "dataType": "string", "required": false, "ignored": false}, + {"name": "Parent Compound Type", "dateType": "string", "required": false, "ignored": false}, + {"name": "Parent Corp Name", "dataType": "string", "required": false, "ignored": false}, + {"name": "Parent Is Mixture", "dataType": "boolean", "required": false, "ignored": false}, + {"name": "Parent LiveDesign Corp Name", "dataType": "string", "required": false, "ignored": false}, + {"name": "Parent Stereo Category", "dataType": "string", "required": true, "ignored": false}, + {"name": "Parent Stereo Comment", "dataType": "string", "required": false, "ignored": false}, + {"name": "Project", "dataType": "string", "required": false, "ignored": false} ] } } diff --git a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee index afb2d3f9c..2d6afa6b6 100644 --- a/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee +++ b/modules/CmpdRegBulkLoader/src/client/CmpdRegBulkLoader.coffee @@ -18,6 +18,7 @@ class window.AssignedProperty extends Backbone.Model dbProperty: "none" defaultVal: "" required: false + ignored: false # validate: (attrs) => # errors = [] @@ -275,17 +276,18 @@ class window.AssignedPropertyController extends AbstractFormController formatDbSelectOptions: -> formattedOptions = new PickListList() @dbPropertiesList.each (dbProp) -> - code = dbProp.get('name') - if dbProp.get('required') - name = code+"*" - else - name = code - if code.toLowerCase().indexOf("date") > -1 - name += " (YYYY-MM-DD or MM-DD-YYYY)" - newOption = new PickList - code: code - name: name - formattedOptions.add newOption + if !dbProp.get("ignored")? || dbProp.get("ignored") != true + code = dbProp.get('name') + if dbProp.get('required') + name = code+"*" + else + name = code + if code.toLowerCase().indexOf("date") > -1 + name += " (YYYY-MM-DD or MM-DD-YYYY)" + newOption = new PickList + code: code + name: name + formattedOptions.add newOption formattedOptions handleDbPropertyChanged: -> @@ -473,6 +475,7 @@ class window.AssignSdfPropertiesController extends Backbone.View dbProperty: "none" required: false sdfProperty: sdfProperty + ignored: false @assignedPropertiesList.add newAssignedProp handleFileChanged: (newFileName) -> @@ -703,6 +706,7 @@ class window.AssignSdfPropertiesController extends Backbone.View required: true sdfProperty: null defaultVal: @project + ignored: false @assignedPropertiesListController.collection.add dbProjectProperty registerCompounds: -> From 4bdffa5728a4bcc628265f45f2aec36fcd165b2f Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Tue, 14 Jul 2020 12:34:21 -0700 Subject: [PATCH 572/576] fixes #715 add utility function to set width of text fields to the value length --- modules/Components/src/client/UtilityFunctions.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/Components/src/client/UtilityFunctions.coffee b/modules/Components/src/client/UtilityFunctions.coffee index 4c5c505f2..d84e27a2f 100644 --- a/modules/Components/src/client/UtilityFunctions.coffee +++ b/modules/Components/src/client/UtilityFunctions.coffee @@ -52,6 +52,14 @@ class window.UtilityFunctions text = $(textarea).val().replace(/\r?\n/g,'
    ') $(textarea).after '
    '+text+'
    ' $(textarea).hide() + + setInputsWidthToValue: (controller) => + # increase size of text boxes to fit data + for node in controller.$('input[type="text"]') + minWidth = parseInt(getComputedStyle(node).minWidth) or node.clientWidth + node.style.overflowX = 'auto' + node.style.width = minWidth + 'px' + node.style.width = node.scrollWidth + 'px' showInactiveTabsInfoToPrint: (controller) => for tab in controller.$('.tab-pane') From aa247383eedc31f2c7eab26484edec99fb4cf65c Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 17 Jul 2020 07:22:23 -0700 Subject: [PATCH 573/576] fixes #717 upgrade to python3 --- Dockerfile | 4 +- modules/ServerAPI/src/server/Bootstrap.coffee | 2 +- .../src/server/RunPythonFunctionTestStub.py | 2 +- .../python/acas_ldclient/acasldclient.py | 20 +++--- .../src/server/python/compare_single_csv.py | 6 +- .../create_lr_for_acas.py | 70 +++++++++---------- .../src/server/python/export_live_report.py | 2 +- .../syncProjectsUsers/ld_entitlements.py | 26 +++---- .../python/syncProjectsUsers/sync_projects.py | 18 ++--- 9 files changed, 75 insertions(+), 75 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2a03bb2a3..6b25a836b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -78,8 +78,8 @@ ENV ACAS_HOME=$BUILD_PATH USER root RUN curl -SLO dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm && rpm -ivh epel-release-6-8.noarch.rpm && rm epel-release-6-8.noarch.rpm RUN yum install -y centos-release-SCL -RUN yum install -y python-pip python-psycopg2 python27 -RUN source /opt/rh/python27/enable && pip install argparse requests psycopg2-binary +RUN yum install -y rh-python36-python-pip python-psycopg2 rh-python36 +RUN source /opt/rh/rh-python36/enable && pip install argparse requests psycopg2-binary USER runner EXPOSE 3000 diff --git a/modules/ServerAPI/src/server/Bootstrap.coffee b/modules/ServerAPI/src/server/Bootstrap.coffee index b20fd5a11..fcc242324 100644 --- a/modules/ServerAPI/src/server/Bootstrap.coffee +++ b/modules/ServerAPI/src/server/Bootstrap.coffee @@ -6,7 +6,7 @@ exports.main = (callback) -> config = require "#{ACAS_HOME}/conf/compiled/conf.js" if config.all.server.liveDesign.installClientOnStart? && config.all.server.liveDesign.installClientOnStart exec = require('child_process').exec - command = "pip2.7 install --upgrade --force-reinstall --user #{config.all.client.service.result.viewer.liveDesign.baseUrl}/ldclient.tar.gz" + command = "pip3.6 install --upgrade --force-reinstall --user #{config.all.client.service.result.viewer.liveDesign.baseUrl}/ldclient.tar.gz" console.log "About to call python using command: "+command child = exec command, (error, stdout, stderr) -> console.log stdout diff --git a/modules/ServerAPI/src/server/RunPythonFunctionTestStub.py b/modules/ServerAPI/src/server/RunPythonFunctionTestStub.py index 4e51d8961..a3a7bad55 100644 --- a/modules/ServerAPI/src/server/RunPythonFunctionTestStub.py +++ b/modules/ServerAPI/src/server/RunPythonFunctionTestStub.py @@ -18,7 +18,7 @@ def main(): request=args['input'] response = {"results":request, "hasError":False,"hasWarning":False,"errorMessages":[]} - print json.dumps(response) + print(json.dumps(response)) if __name__ == '__main__': main() diff --git a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py index d38c163b4..fb5c87b16 100644 --- a/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py +++ b/modules/ServerAPI/src/server/python/acas_ldclient/acasldclient.py @@ -6,6 +6,7 @@ import json import os, sys import locale +from functools import cmp_to_key def get_parser(): """ @@ -61,7 +62,7 @@ def get_parser(): return parser def list_groups(client, test): - print test + print(test) return client.list_groups() def auth_check(endpoint, username, password): @@ -107,7 +108,7 @@ def get_users(client, ls_type = None, ls_kind = None, role_name = None): # ld_users = client.list_users() ld_users = client.client.get("/users?include_permissions=false", '') if ls_type == None and ls_kind == None and role_name == None: - acas_users = map(ld_user_to_acas_user_code_table, ld_users) + acas_users = list(map(ld_user_to_acas_user_code_table, ld_users)) else: groups = client.list_groups() permissions = client.list_permissions() @@ -129,7 +130,7 @@ def get_users(client, ls_type = None, ls_kind = None, role_name = None): if u["id"] == m["user_id"]: user_projects[proj.name]["ld_users"].append(u) if ls_kind in user_projects: - acas_users = map(ld_user_to_acas_user_code_table, user_projects[ls_kind]["ld_users"]) + acas_users = list(map(ld_user_to_acas_user_code_table, user_projects[ls_kind]["ld_users"])) else: acas_users = [] return acas_users @@ -150,7 +151,7 @@ def get_users_roles(client, users): for u in users: usersDict[u["id"]] = u groups = client.list_groups() - roles = map(ld_group_to_acas_role, groups) + roles = list(map(ld_group_to_acas_role, groups)) roleDict = {} for r in roles: roleDict[r["id"]] = r @@ -158,7 +159,7 @@ def get_users_roles(client, users): for m in memberships: usersDict[m["user_id"]].append(roleDict[m["group_id"]]) users = [] - for user in usersDict.iteritems(): + for user in usersDict.items(): users.append(user) return users @@ -181,7 +182,7 @@ def get_user(client, username): else: user_projects[proj.name] = {"id":proj.id, "granting_groups": [g["name"]]} roles = [] - for proj, data in user_projects.iteritems(): + for proj, data in user_projects.items(): roles.append({ "id": data["id"], "roleEntry": { @@ -200,12 +201,11 @@ def get_user(client, username): def get_projects(client): ld_projects = client.projects() - projects = map(ld_project_to_acas, ld_projects) + projects = list(map(ld_project_to_acas, ld_projects)) # Sort by name locale.setlocale(locale.LC_ALL, '') - projects = sorted(projects, key=lambda k: k['name'], cmp=locale.strcoll) - + projects = sorted(projects, key=lambda x: locale.strxfrm(x['name'])) return projects def ld_project_to_acas(ld_project): @@ -231,7 +231,7 @@ def main(): else: result = auth_check(endpoint, *args.args) - print json.dumps(result) + print(json.dumps(result)) if __name__ == "__main__": main() diff --git a/modules/ServerAPI/src/server/python/compare_single_csv.py b/modules/ServerAPI/src/server/python/compare_single_csv.py index 9f89b23a6..452ccf210 100644 --- a/modules/ServerAPI/src/server/python/compare_single_csv.py +++ b/modules/ServerAPI/src/server/python/compare_single_csv.py @@ -32,8 +32,8 @@ def clean_diff(diffObject): from_list = from_value.split('\n') to_list = to_value.split('\n') - from_list = map(lambda x: clean_units(x), from_list) - to_list = map(lambda x: clean_units(x), to_list) + from_list = [clean_units(x) for x in from_list] + to_list = [clean_units(x) for x in to_list] if compare(from_list, to_list): changed['fields'][fieldkey] = {} if 'onschrodinger.com' in from_value and 'onschrodinger.com' in to_value: @@ -81,7 +81,7 @@ def main(): if lr_key not in diffResult: diffResult[lr_key]={} if 'removed' not in diffResult[lr_key]: diffResult[lr_key]['removed']={} diffResult[lr_key]['removed'][row_key] = row - print(json.dumps(diffResult, sort_keys=True, indent=4)) + print((json.dumps(diffResult, sort_keys=True, indent=4))) if __name__ == '__main__': main() diff --git a/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/create_lr_for_acas.py b/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/create_lr_for_acas.py index d4f61a431..dfd524a5e 100755 --- a/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/create_lr_for_acas.py +++ b/modules/ServerAPI/src/server/python/createLiveDesignLiveReportForACAS/create_lr_for_acas.py @@ -16,7 +16,7 @@ import http.client as http_client except ImportError: # Python 2 - import httplib as http_client + import http.client as http_client http_client.HTTPConnection.debuglevel = 0 import ldclient @@ -34,7 +34,7 @@ requests.packages.urllib3.disable_warnings(SNIMissingWarning) except ImportError: #ignore error, allow warnings - print 'ignoring ImportError' + print('ignoring ImportError') def str2bool(v): if isinstance(v, bool): @@ -52,8 +52,8 @@ def make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, log for folder in api.list_folders([projectId]): if int(folder.project_id) == int(projectId): folder_names[folder.name] = folder.id - if 'Autogenerated ACAS Reports' not in folder_names.keys(): - print 'Autogenerated ACAS Reports folder does not exist in this project. Creating it' + if 'Autogenerated ACAS Reports' not in list(folder_names.keys()): + print('Autogenerated ACAS Reports folder does not exist in this project. Creating it') folder = api.create_folder('Autogenerated ACAS Reports', projectId) folder_id = folder.id else: @@ -75,7 +75,7 @@ def make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, log lr_id = int(lr.id) - print "Live Report ID is:" + str(lr_id) + print("Live Report ID is:" + str(lr_id)) #get the list of assay addable columns if ldClientVersion >= 7.6: assay_column_ids = [] @@ -97,7 +97,7 @@ def make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, log assay_hash[assay.name][assay_type.name] = assay_type.addable_column_id assay_column_ids = [] for assay_to_add in assays_to_add: - assay_column_ids.append(assay_hash[assay_to_add['protocolName']][assay_to_add['resultType']]) + assay_column_ids.append(assay_hash[assay_to_add['protocolName']][assay_to_add['resultType']]) #assay_column_id1 = assay_hash["Peroxisome proliferator-activated receptor delta"]["EC50"] #assay_column_id2 = assay_hash["DRC TEST ASSAY"]["IC50%"] @@ -117,13 +117,13 @@ def make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, log #compound search by id search_results = [] - if isinstance(compound_ids, (str,unicode)): - search_results.extend(api.compound_search_by_id(compound_ids, database_names=[database], project_id = projectId)) + if isinstance(compound_ids, str): + search_results.extend(api.compound_search_by_id(compound_ids, database_names=[database], project_id = projectId)) else: - search_string = "" - for compound_id in compound_ids: - search_string += compound_id +"\n" - search_results.extend(api.compound_search_by_id(search_string, database_names=[database], project_id = projectId)) + search_string = "" + for compound_id in compound_ids: + search_string += compound_id +"\n" + search_results.extend(api.compound_search_by_id(search_string, database_names=[database], project_id = projectId)) # Now add the rows for the compound ids for which we want data #compound_ids = ["V51411","V51412","V51413","V51414"] api.add_rows(lr_id, search_results) @@ -131,20 +131,20 @@ def make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, log return lr_id def findassay(assay_tree, assay_name): - if 'name' in assay_tree and assay_tree['name'] == assay_name: return assay_tree - elif 'name' in assay_tree and assay_name in assay_tree['name'] and 'column_folder_node_type' in assay_tree and assay_tree['column_folder_node_type']=='ROLLUP': return assay_tree - for sub_tree in assay_tree['children']: - item = findassay(sub_tree, assay_name) - if item is not None: - return item - + if 'name' in assay_tree and assay_tree['name'] == assay_name: return assay_tree + elif 'name' in assay_tree and assay_name in assay_tree['name'] and 'column_folder_node_type' in assay_tree and assay_tree['column_folder_node_type']=='ROLLUP': return assay_tree + for sub_tree in assay_tree['children']: + item = findassay(sub_tree, assay_name) + if item is not None: + return item + def extract_endpoints(assay, endpoints): - #print json.dumps(assay) - if 'addable_column_ids' in assay and len(assay['addable_column_ids']) > 0: - endpoints.extend(assay['addable_column_ids']) - for sub_assay in assay['children']: - extract_endpoints(sub_assay, endpoints) - return endpoints + #print json.dumps(assay) + if 'addable_column_ids' in assay and len(assay['addable_column_ids']) > 0: + endpoints.extend(assay['addable_column_ids']) + for sub_assay in assay['children']: + extract_endpoints(sub_assay, endpoints) + return endpoints def main(): #if len(sys.argv) is not 4: @@ -172,13 +172,13 @@ def main(): assays_to_add=args['input']['assays'] experiment_code=args['input']['experimentCode'] try: - project=args['input']['project'] + project=args['input']['project'] except: - project="Global" + project="Global" try: - logged_in_user=args['input']['username'] + logged_in_user=args['input']['username'] except: - logged_in_user=username + logged_in_user=username apiSuffix = "/api" apiEndpoint = endpoint + apiSuffix; api = Api(apiEndpoint, username, password) @@ -190,20 +190,20 @@ def main(): ld_client_version=float(ldclient.api.requester.SUPPORTED_SERVER_VERSION) except: ld_client_version=float(7.3) - print "LDClient version is:"+str(ld_client_version) + print("LDClient version is:"+str(ld_client_version)) try: # projectId = api.get_project_id_by_name(project) - matching_projects = filter(lambda p: p.name == project, api.projects()) + matching_projects = [p for p in api.projects() if p.name == project] projectId = int(matching_projects[0].id.encode('ascii')) - print "Project " + project + " found with id: " + str(projectId) + print("Project " + project + " found with id: " + str(projectId)) except: - projectId = 0 + projectId = 0 if type(projectId) is not int: - projectId = 0 + projectId = 0 lr_id = make_acas_live_report(api, compound_ids, assays_to_add, experiment_code, logged_in_user, database, projectId, ld_client_version, args["readonly"]) liveReportSuffix = "/#/projects/"+str(projectId)+"/livereports/"; - print endpoint + liveReportSuffix + str(lr_id) + print(endpoint + liveReportSuffix + str(lr_id)) #return endpoint + liveReportSuffix + str(lr_id) if __name__ == '__main__': diff --git a/modules/ServerAPI/src/server/python/export_live_report.py b/modules/ServerAPI/src/server/python/export_live_report.py index 2fe1e5645..14fdbd534 100644 --- a/modules/ServerAPI/src/server/python/export_live_report.py +++ b/modules/ServerAPI/src/server/python/export_live_report.py @@ -33,7 +33,7 @@ def main(): # except: # projectId = 0 csv_dump = api.export_live_report(live_report_id, 'csv') - print csv_dump + print(csv_dump) if __name__ == '__main__': main() \ No newline at end of file diff --git a/modules/ServerAPI/src/server/python/syncProjectsUsers/ld_entitlements.py b/modules/ServerAPI/src/server/python/syncProjectsUsers/ld_entitlements.py index b6e2366fe..38d01c519 100755 --- a/modules/ServerAPI/src/server/python/syncProjectsUsers/ld_entitlements.py +++ b/modules/ServerAPI/src/server/python/syncProjectsUsers/ld_entitlements.py @@ -92,7 +92,7 @@ def reverse_mapping(m): """ Reverse a mapping (ignoring duplicate keys) """ - return dict((v, k) for k, v in m.iteritems()) + return dict((v, k) for k, v in m.items()) def retrieve_raw_model(config, session): @@ -108,7 +108,7 @@ def retrieve_raw_model(config, session): } ret = {} - for endpoint, dest in get_targets.iteritems(): + for endpoint, dest in get_targets.items(): url = config['ld_url'] + API + endpoint response = session.get(url) response.raise_for_status() @@ -167,7 +167,7 @@ def resolve_project_selector(model, model_projects_by_name, import_project): if predicate: ret = [] # pattern has a beginning or end wildcard - for pid, name in model['projects'].iteritems(): + for pid, name in model['projects'].items(): if predicate(name, pattern): ret.append(pid) if len(ret) > 0: @@ -183,7 +183,7 @@ def compute_changes(model, import_data): # Have to treat this specially since multiple projects might have same name projects_by_name = {} - for id, name in model['projects'].iteritems(): + for id, name in model['projects'].items(): projects_by_name[name] = projects_by_name.get(name, []) projects_by_name[name].append(id) @@ -199,7 +199,7 @@ def compute_changes(model, import_data): permissions_to_create_by_name = set() # set of (pid, group name) tuples permissions_to_remove = set() # set of (pid, gid) tuples - for group, members in import_data['groups'].iteritems(): + for group, members in import_data['groups'].items(): # Check if the group already exists in the model. if group not in groups_by_name: assert group not in groups_to_create @@ -305,7 +305,7 @@ def apply_changes(config, session, model, changes): projects_by_name = reverse_mapping(model['projects']) url = config['ld_url'] + API + 'groups' - for group, members in changes['create_groups'].iteritems(): + for group, members in changes['create_groups'].items(): data = { 'name': group } response = session.post(url, data=json.dumps(data)) response.raise_for_status() @@ -380,27 +380,27 @@ def main(argv): raw_model = retrieve_raw_model(config, session) model = crunch_model(raw_model) - print 'Current user model:' + print('Current user model:') pprint.pprint(model) - print + print() changes = compute_changes(model, import_data) - print 'Changes to make:' + print('Changes to make:') pprint.pprint(changes) if opts.dry_run: - print '\nExiting, as this is a dry run.' + print('\nExiting, as this is a dry run.') return 0 # Check to see if the current run is a no-op. has_changes = False - for obj in changes.values(): + for obj in list(changes.values()): if len(obj) > 0: has_changes = True break if not has_changes: - print '\nExiting, as no changes need to be made.' + print('\nExiting, as no changes need to be made.') return 0 # Back the current model, import data and proposed changes (for forensics) @@ -429,7 +429,7 @@ def serialize_sets(obj): json.dump(import_data, fp) apply_changes(config, session, model, changes) - print '\nAll changes applied successfully' + print('\nAll changes applied successfully') return 0 diff --git a/modules/ServerAPI/src/server/python/syncProjectsUsers/sync_projects.py b/modules/ServerAPI/src/server/python/syncProjectsUsers/sync_projects.py index 652ef4dec..1e162a000 100644 --- a/modules/ServerAPI/src/server/python/syncProjectsUsers/sync_projects.py +++ b/modules/ServerAPI/src/server/python/syncProjectsUsers/sync_projects.py @@ -2,7 +2,7 @@ import pprint import psycopg2 import threading -import ConfigParser +import configparser class SyncProjectsController: def __init__(self, config_json, projects_json): @@ -22,7 +22,7 @@ def __init__(self, config_json, projects_json): self.projects = json.loads(projects_json) def check_projects_exist(self): - print "Checking for projects:" + print("Checking for projects:") check_project_exists_sql = "SELECT * FROM syn_project WHERE alternate_id = %s;" self.new_projects=[] self.projects_to_update=[] @@ -34,18 +34,18 @@ def check_projects_exist(self): project_check_results=cur.fetchall() self.lock.release() if len(project_check_results)>0: - print "Project "+project['name']+" exists." + print("Project "+project['name']+" exists.") found_project = project_check_results[0] if project['active'] != found_project[1] or project['is_restricted'] != found_project[6] or project['name'] != found_project[4]: self.projects_to_update.append(project) else: - print "Project "+project['name']+" does not exist." + print("Project "+project['name']+" does not exist.") self.new_projects.append(project) return def add_new_projects(self): if len(self.new_projects) > 0: - print "Adding projects:" + print("Adding projects:") add_project_sql = "INSERT INTO syn_project (project_id, active, alternate_id, is_restricted, project_desc, project_name) VALUES(nextval('syn_project_project_id_seq'), %s, %s, %s, %s, %s) returning *;" for project in self.new_projects: self.lock.acquire() @@ -56,12 +56,12 @@ def add_new_projects(self): self.livedesign_db_conn.commit() self.lock.release() cur.close() - print add_project_results + print(add_project_results) return def update_projects(self): if len(self.projects_to_update) > 0: - print "Updating projects:" + print("Updating projects:") update_project_sql = "UPDATE syn_project SET active = %s , is_restricted = %s, project_name = %s where alternate_id = %s returning *;" for project in self.projects_to_update: self.lock.acquire() @@ -72,7 +72,7 @@ def update_projects(self): self.livedesign_db_conn.commit() self.lock.release() cur.close() - print update_project_results + print(update_project_results) return if __name__ == '__main__': @@ -81,7 +81,7 @@ def update_projects(self): sync_projects_controller.check_projects_exist() sync_projects_controller.add_new_projects() sync_projects_controller.update_projects() - print "successfully updated projects" + print("successfully updated projects") #reset_projects_sequence_sql = "psql -h 127.0.0.1 -p 3247 -U postgres -d synaptic -c \"select setval('syn_project_id_seq', (select max(project_id)+1 from syn_project));\"" #psql -h 127.0.0.1 -p 3247 -U postgres -d synaptic -c "INSERT INTO syn_project (active, alternate_id, is_restricted, project_desc, project_name) VALUES ('Y', 'Project Alias', 0, 'Description (optional)','Project name') returning *" From da74811c2c9f9b3cf5a6e634240767403b6f6990 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 17 Jul 2020 08:14:06 -0700 Subject: [PATCH 574/576] fixes #713 lock down mkdirp to older version. Newer version broke jquery middleware. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 80d6d619c..1af94f56b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,8 @@ "webshot": "0.18.0", "winston": "2.3.1", "winston-mongodb": "2.0.10", - "yamljs": "0.3.0" + "yamljs": "0.3.0", + "mkdirp": "0.5.1" }, "devDependencies": { "coffee-script": "^1.12.4", From 2eaa7c2ce6dd9baf98a6fad2e708d9b0d0995d18 Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Fri, 17 Jul 2020 11:53:56 -0700 Subject: [PATCH 575/576] fixes #717 missed checkin of setenv.sh file --- bin/setenv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/setenv.sh b/bin/setenv.sh index ee229d54b..231deb25b 100644 --- a/bin/setenv.sh +++ b/bin/setenv.sh @@ -1,3 +1,3 @@ # Example: #export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/client64/lib:$LD_LIBRARY_PATH -if [ -e "/opt/rh/python27/enable" ]; then source /opt/rh/python27/enable; fi; \ No newline at end of file +if [ -e "/opt/rh/rh-python36/enable" ]; then source /opt/rh/rh-python36/enable; fi; \ No newline at end of file From 6662c7810aec75507f489377046da84af693ce4d Mon Sep 17 00:00:00 2001 From: Brian Bolt Date: Thu, 3 Dec 2020 12:34:20 -0800 Subject: [PATCH 576/576] update to centos8 in base image --- Dockerfile | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6b25a836b..7cd1892da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,13 @@ -FROM centos:centos6 +FROM centos:centos8 # Update RUN \ - yum update -y && \ - yum upgrade -y && \ + dnf update -y && \ + dnf upgrade -y && \ # tar for pulling down node # git required for some npm packages - yum install -y tar git && \ - yum install -y fontconfig urw-fonts && \ - yum clean all + dnf install -y tar git && \ + dnf install -y fontconfig urw-fonts && \ + dnf clean all # node RUN set -ex \ @@ -56,7 +56,7 @@ USER runner WORKDIR $ACAS_BASE # This installs the modules but not acas, doing this makes subsequent builds much faster so that the container isn't invalidated on a small code change RUN npm install --ignore-scripts --loglevel warn -COPY . $ACAS_BASE +COPY --chown=runner:runner . $ACAS_BASE USER root RUN chown -R runner:runner $ACAS_BASE USER runner @@ -73,15 +73,17 @@ ENV PREPARE_MODULE_CONF_JSON=true ENV PREPARE_CONFIG_FILES=true ENV RUN_SYSTEM_TEST=true ENV ACAS_HOME=$BUILD_PATH +RUN gulp execute:prepare_config_files #Install python dependencies USER root -RUN curl -SLO dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm && rpm -ivh epel-release-6-8.noarch.rpm && rm epel-release-6-8.noarch.rpm -RUN yum install -y centos-release-SCL -RUN yum install -y rh-python36-python-pip python-psycopg2 rh-python36 -RUN source /opt/rh/rh-python36/enable && pip install argparse requests psycopg2-binary +RUN dnf install -y python36 python3-pip +RUN alternatives --set python /usr/bin/python3 +RUN alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 +RUN pip install argparse requests psycopg2-binary +RUN dnf install -y initscripts + USER runner EXPOSE 3000 - CMD ["/bin/sh","bin/acas.sh", "run"]