1 /** 2 * @fileOverview 3 * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>) 4 * @license Copyright (c) 2008-2018 LabKey Corporation 5 * <p/> 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * <p/> 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * <p/> 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * <p/> 18 */ 19 20 /** 21 * @namespace The Experiment static class allows you to create hidden run groups and other experiment-related functionality. 22 * <p>Additional Documentation: 23 * <ul> 24 * <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li> 25 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 26 * </ul> 27 * </p> 28 */ 29 LABKEY.Experiment = new function() 30 { 31 function getSuccessCallbackWrapper(createExpFn, fn, scope) 32 { 33 return function(response, options) 34 { 35 //ensure response is JSON before trying to decode 36 var json = null; 37 var experiment = null; 38 if (response && response.getResponseHeader && response.getResponseHeader('Content-Type') 39 && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0) 40 { 41 json = LABKEY.Utils.decode(response.responseText); 42 experiment = createExpFn(json); 43 } 44 45 if(fn) 46 fn.call(scope || this, experiment, response); 47 }; 48 } 49 50 function _saveBatches(config, createExps) 51 { 52 LABKEY.Ajax.request({ 53 url: LABKEY.ActionURL.buildURL("assay", "saveAssayBatch", LABKEY.ActionURL.getContainer()), 54 method: 'POST', 55 jsonData: { 56 assayId: config.assayId, 57 assayName: config.assayName, 58 providerName: config.providerName, 59 protocolName : config.protocolName, 60 batches: config.batches 61 }, 62 success: getSuccessCallbackWrapper(createExps, config.success, config.scope), 63 failure: LABKEY.Utils.getCallbackWrapper(config.failure, config.scope, true), 64 scope: config.scope, 65 headers: { 66 'Content-Type' : 'application/json' 67 } 68 }); 69 } 70 71 // normalize the different config object passed in for saveBatch and saveBatches into one config 72 // appropriate for _saveBatches call above 73 function getSaveBatchesConfig(config) 74 { 75 var wrapConfig = {}; 76 77 if (config.batches) 78 { 79 wrapConfig.batches = config.batches; 80 } 81 else 82 { 83 wrapConfig.batches=[]; 84 wrapConfig.batches.push(config.batch); 85 } 86 wrapConfig.assayId = config.assayId; 87 wrapConfig.assayName = config.assayName; 88 wrapConfig.providerName = config.providerName; 89 wrapConfig.protocolName = config.protocolName; 90 wrapConfig.scope = config.scope; 91 wrapConfig.success = LABKEY.Utils.getOnSuccess(config); 92 wrapConfig.failure = LABKEY.Utils.getOnFailure(config); 93 return wrapConfig; 94 } 95 96 /** @scope LABKEY.Experiment */ 97 return { 98 99 SAMPLE_DERIVATION_PROTOCOL : "Sample Derivation Protocol", 100 101 /** 102 * Create or recycle an existing run group. Run groups are the basis for some operations, like comparing 103 * MS2 runs to one another. 104 * @param config A configuration object with the following properties: 105 * @param {function} config.success A reference to a function to call with the API results. This 106 * function will be passed the following parameters: 107 * <ul> 108 * <li><b>runGroup:</b> a {@link LABKEY.Exp.RunGroup} object containing properties about the run group</li> 109 * <li><b>response:</b> The XMLHttpResponse object</li> 110 * </ul> 111 * @param {Integer[]} [config.runIds] An array of integer ids for the runs to be members of the group. Either 112 * runIds or selectionKey must be specified. 113 * @param {string} [config.selectionKey] The DataRegion's selectionKey to be used to resolve the runs to be 114 * members of the group. Either runIds or selectionKey must be specified. 115 * @param {function} [config.failure] A reference to a function to call when an error occurs. This 116 * function will be passed the following parameters: 117 * <ul> 118 * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li> 119 * <li><b>response:</b> The XMLHttpResponse object</li> 120 * </ul> 121 * @param {string} [config.containerPath] An alternate container path to get permissions from. If not specified, 122 * the current container path will be used. 123 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 124 * @static 125 */ 126 createHiddenRunGroup : function (config) 127 { 128 function createExp(json) 129 { 130 return new LABKEY.Exp.RunGroup(json); 131 } 132 133 var jsonData = {}; 134 if (config.runIds && config.selectionKey) 135 { 136 throw "Only one of runIds or selectionKey config parameter is allowed for a single call."; 137 } 138 else if (config.runIds) 139 { 140 jsonData.runIds = config.runIds; 141 } 142 else if (config.selectionKey) 143 { 144 jsonData.selectionKey = config.selectionKey; 145 } 146 else 147 { 148 throw "Either the runIds or the selectionKey config parameter is required."; 149 } 150 LABKEY.Ajax.request( 151 { 152 url : LABKEY.ActionURL.buildURL("experiment", "createHiddenRunGroup", config.containerPath), 153 method : 'POST', 154 jsonData : jsonData, 155 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope), 156 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true), 157 headers : 158 { 159 'Content-Type' : 'application/json' 160 } 161 }); 162 }, 163 164 /** 165 * Loads a batch from the server. 166 * @param config An object that contains the following configuration parameters 167 * @param {Number} config.assayId The assay protocol id. 168 * @param {Number} config.batchId The batch id. 169 * @param {function} config.success The function to call when the function finishes successfully. 170 * This function will be called with a the parameters: 171 * <ul> 172 * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object. 173 * <li><b>response</b> The original response 174 * </ul> 175 * @param {function} [config.failure] The function to call if this function encounters an error. 176 * This function will be called with the following parameters: 177 * <ul> 178 * <li><b>response</b> The original response 179 * </ul> 180 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 181 * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information. 182 * @static 183 */ 184 loadBatch : function (config) 185 { 186 function createExp(json) 187 { 188 return new LABKEY.Exp.RunGroup(json.batch); 189 } 190 191 LABKEY.Ajax.request({ 192 url: LABKEY.ActionURL.buildURL("assay", "getAssayBatch", LABKEY.ActionURL.getContainer()), 193 method: 'POST', 194 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope), 195 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true), 196 scope: config.scope, 197 jsonData : { 198 assayId: config.assayId, 199 assayName: config.assayName, 200 providerName: config.providerName, 201 batchId: config.batchId 202 }, 203 headers : { 204 'Content-Type' : 'application/json' 205 } 206 }); 207 }, 208 209 /** 210 * Loads batches from the server. 211 * @param config An object that contains the following configuration parameters 212 * @param {Number} config.assayId The assay protocol id. 213 * @param {Number} config.batchIds The list of batch ids. 214 * @param {function} config.success The function to call when the function finishes successfully. 215 * This function will be called with a the parameters: 216 * <ul> 217 * <li><b>batches</b> The list of {@link LABKEY.Exp.RunGroup} objects. 218 * <li><b>response</b> The original response 219 * </ul> 220 * @param {function} [config.failure] The function to call if this function encounters an error. 221 * This function will be called with the following parameters: 222 * <ul> 223 * <li><b>response</b> The original response 224 * </ul> 225 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 226 * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information. 227 * @static 228 */ 229 loadBatches : function (config) 230 { 231 function createExp(json) 232 { 233 var batches = []; 234 if (json.batches) { 235 for (var i = 0; i < json.batches.length; i++) { 236 batches.push(new LABKEY.Exp.RunGroup(json.batches[i])); 237 } 238 } 239 return batches; 240 } 241 242 LABKEY.Ajax.request({ 243 url: LABKEY.ActionURL.buildURL("assay", "getAssayBatches", LABKEY.ActionURL.getContainer()), 244 method: 'POST', 245 success: getSuccessCallbackWrapper(createExp, LABKEY.Utils.getOnSuccess(config), config.scope), 246 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true), 247 scope: config.scope, 248 jsonData : { 249 assayId: config.assayId, 250 assayName: config.assayName, 251 providerName: config.providerName, 252 batchIds: config.batchIds 253 }, 254 headers : { 255 'Content-Type' : 'application/json' 256 } 257 }); 258 }, 259 260 /** 261 * Saves a modified batch. 262 * Runs within the batch may refer to existing data and material objects, either inputs or outputs, by ID or LSID. 263 * Runs may also define new data and materials objects by not specifying an ID or LSID in their properties. 264 * @param config An object that contains the following configuration parameters 265 * @param {Number} [config.assayId] Optional assay protocol id, either assayId or protocolName must be specified. 266 * @param {String} [config.protocolName] Optional protocol name to be used for non-assay backed runs. Currently only 267 * SAMPLE_DERIVATION_PROTOCOL is supported. 268 * @param {LABKEY.Exp.RunGroup} config.batch The modified batch object. 269 * @param {function} config.success The function to call when the function finishes successfully. 270 * This function will be called with the following parameters: 271 * <ul> 272 * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object. Some values (such as IDs and LSIDs) will be filled in by the server. 273 * <li><b>response</b> The original response 274 * </ul> 275 * @param {function} [config.failure] The function to call if this function encounters an error. 276 * This function will be called with the following parameters: 277 * <ul> 278 * <li><b>response</b> The original response 279 * </ul> 280 * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information. 281 * @static 282 */ 283 saveBatch : function (config) 284 { 285 _saveBatches(getSaveBatchesConfig(config), function(json) { 286 if (json.batches) { 287 return new LABKEY.Exp.RunGroup(json.batches[0]) 288 } 289 }); 290 }, 291 292 /** 293 * Saves an array of modified batches. 294 * Runs within the batches may refer to existing data and material objects, either inputs or outputs, by ID or LSID. 295 * Runs may also define new data and materials objects by not specifying an ID or LSID in their properties. 296 * @param config An object that contains the following configuration parameters 297 * @param {Number} config.assayId The assay protocol id. 298 * @param {LABKEY.Exp.RunGroup[]} config.batches The modified batch objects. 299 * @param {function} config.success The function to call when the function finishes successfully. 300 * This function will be called with the following parameters: 301 * <ul> 302 * <li><b>batches</b> An array of new {@link LABKEY.Exp.RunGroup} objects. Some values (such as IDs and LSIDs) will be filled in by the server. 303 * <li><b>response</b> The original response 304 * </ul> 305 * @param {function} [config.failure] The function to call if this function encounters an error. 306 * This function will be called with the following parameters: 307 * <ul> 308 * <li><b>response</b> The original response 309 * </ul> 310 * @see The <a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>Module Assay</a> documentation for more information. 311 * @static 312 */ 313 saveBatches : function (config) 314 { 315 _saveBatches(getSaveBatchesConfig(config), function(json){ 316 var batches = []; 317 if (json.batches) { 318 for (var i = 0; i < json.batches.length; i++) { 319 batches.push(new LABKEY.Exp.RunGroup(json.batches[i])); 320 } 321 } 322 return batches; 323 }); 324 }, 325 326 /** 327 * Saves materials. 328 * @deprecated Use LABKEY.Query.insertRows({schemaName: 'Samples', queryName: '<sample set name>', ...}); 329 * 330 * @param config An object that contains the following configuration parameters 331 * @param config.name name of the sample set 332 * @param config.materials An array of LABKEY.Exp.Material objects to be saved. 333 * @param {function} config.success The function to call when the function finishes successfully. 334 * This function will be called with the following parameters: 335 * <ul> 336 * <li><b>batch</b> A new {@link LABKEY.Exp.RunGroup} object. Some values will be filled in by the server. 337 * <li><b>response</b> The original response 338 * </ul> 339 * @param {function} [config.failure] The function to call if this function encounters an error. 340 * This function will be called with the following parameters: 341 * <ul> 342 * <li><b>response</b> The original response 343 * </ul> 344 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 345 * @static 346 */ 347 saveMaterials : function (config) 348 { 349 LABKEY.Query.insertRows({ 350 schemaName: 'Samples', 351 queryName: config.name, 352 rows: config.materials, 353 success: LABKEY.Utils.getOnSuccess(config), 354 failure: LABKEY.Utils.getOnFailure(config), 355 scope: config.scope 356 }); 357 }, 358 359 /** 360 * Get parent/child relationships of an ExpData or ExpMaterial. 361 * @param config 362 * @param config.rowId The row id of the seed ExpData or ExpMaterial. Either rowId or lsid is required. 363 * @param config.lsid The LSID of the seed ExpData or ExpMaterial. Either rowId or lsid is required. 364 * @param {Number} [config.depth] An optional depth argument. Defaults to include all. 365 * @param {Boolean} [config.parents] Include parents in the lineage response. Defaults to true. 366 * @param {Boolean} [config.children] Include children in the lineage response. Defaults to true. 367 * @param {String} [config.expType] Optional experiment type to filter response -- either "Data", "Material", or "ExperimentRun". Defaults to include all. 368 * @param {String} [config.cpasType] Optional LSID of a SampleSet or DataClass to filter the response. Defaults to include all. 369 * @static 370 */ 371 lineage : function (config) 372 { 373 var params = {}; 374 if (config.rowId) 375 params.rowId = config.rowId; 376 else if (config.lsid) 377 params.lsid = config.lsid; 378 379 if (config.hasOwnProperty('parents')) 380 params.parents = config.parents; 381 if (config.hasOwnProperty('children')) 382 params.children = config.children; 383 if (config.hasOwnProperty('depth')) 384 params.depth = config.depth; 385 386 if (config.expType) 387 params.expType = config.expType; 388 if (config.cpasType) 389 params.cpasType = config.cpasType; 390 391 LABKEY.Ajax.request({ 392 method: 'GET', 393 url: LABKEY.ActionURL.buildURL("experiment", "lineage.api"), 394 params: params, 395 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope), 396 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true), 397 scope: config.scope 398 }); 399 } 400 }; 401 }; 402 403 if (typeof LABKEY.Exp == "undefined") 404 LABKEY.Exp = {}; 405 406 /** 407 * This constructor isn't called directly, but is used by derived classes. 408 * @class The experiment object base class describes basic 409 * characteristics of a protocol or an experimental run. Many experiment classes (such as {@link LABKEY.Exp.Run}, 410 * {@link LABKEY.Exp.Data} and {@link LABKEY.Exp.Material}) are subclasses 411 * of ExpObject, so they provide the fields defined by this object (e.g., name, lsid, etc). 412 * In a Java representation of these same classes, ExpObject is an abstract class. 413 * <p>Additional Documentation: 414 * <ul> 415 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 416 * </ul> 417 * </p> 418 * @memberOf LABKEY.Exp 419 * 420 * @param {Object} [config] Configuration object. 421 * 422 * @param {String} config.lsid The LSID of the ExpObject. 423 * @param {String} config.name The name of the ExpObject. 424 * @param {number} config.id The id of the ExpObject. 425 * @param {number} config.rowId The id of the ExpObject (alias of id property) 426 * @param {String} config.comment User editable comment. 427 * @param {Date} config.created When the ExpObject was created. 428 * @param {String} config.createdBy The person who created the ExpObject. 429 * @param {Date} config.modified When the ExpObject was last modified. 430 * @param {String} config.modifiedBy The person who last modified the ExpObject. 431 * @param {Object} config.properties Map of property descriptor names to values. Most types, such as strings and 432 * numbers, are just stored as simple properties. Properties of type FileLink will be returned by the server in the 433 * same format as {@link LABKEY.Exp.Data} objects (missing many properties such as id and createdBy if they exist on disk but 434 * have no row with metadata in the database). FileLink values are accepted from the client in the same way, or a simple value of the 435 * following three types: the data's RowId, the data's LSID, or the full path on the server's file system. 436 */ 437 LABKEY.Exp.ExpObject = function (config) { 438 config = config || {}; 439 this.lsid = config.lsid; 440 this.name = config.name; 441 this.id = config.id || config.rowId; 442 this.rowId = this.id; 443 this.comment = config.comment; 444 this.created = config.created; 445 this.createdBy = config.createdBy; 446 this.modified = config.modified; 447 this.modifiedBy = config.modifiedBy; 448 this.properties = config.properties || {}; 449 }; 450 451 /** 452 * Constructs a new experiment run object. 453 * @class The Exp.Run class describes an experiment run. An experiment run is an application of an experimental 454 * protocol to concrete inputs, producing concrete outputs. In object-oriented terminology, a protocol would be a class 455 * while a run would be an instance. 456 * <p>Additional Documentation: 457 * <ul> 458 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 459 * </ul> 460 * </p> 461 * @extends LABKEY.Exp.ExpObject 462 * @memberOf LABKEY.Exp 463 * 464 * @param {Object} [config] The configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 465 * @param {Object[]} config.dataInputs Array of {@link LABKEY.Exp.Data} objects that are the inputs to this run. Datas typically represent a file on the server's file system. 466 * @param {Object[]} config.dataOutputs Array of {@link LABKEY.Exp.Data} objects that are the outputs from this run. Datas typically represent a file on the server's file system. 467 * @param {Object[]} config.dataRows Array of Objects where each Object corresponds to a row in the results domain. 468 * @param {Object[]} config.materialInputs Array of {@link LABKEY.Exp.Material} objects that are material/sample inputs to the run. 469 * @param {Object[]} config.materialOutputs Array of {@link LABKEY.Exp.Material} objects that are material/sample outputs from the run. 470 * 471 * @see LABKEY.Exp.Data#getContent 472 * 473 * @example 474 * var result = // ... result of uploading a new assay results file 475 * var data = new LABKEY.Exp.Data(result); 476 * 477 * var run = new LABKEY.Exp.Run(); 478 * run.name = data.name; 479 * run.properties = { "MyRunProperty" : 3 }; 480 * run.dataInputs = [ data ]; 481 * 482 * data.getContent({ 483 * format: 'jsonTSV', 484 * success: function (content, format) { 485 * data.content = content; 486 * var sheet = content.sheets[0]; 487 * var filedata = sheet.data; 488 * 489 * // transform the file content into the dataRows array used by the run 490 * run.dataRows = []; 491 * for (var i = 1; i < filedata.length; i++) { 492 * var row = filedata[i]; 493 * run.dataRows.push({ 494 * "SampleId": row[0], 495 * "DataValue": row[1], 496 * // ... other columns 497 * }); 498 * } 499 * 500 * var batch = // ... the LABKEY.Exp.RunGroup object 501 * batch.runs.push(run); 502 * }, 503 * failure: function (error, format) { 504 * alert("error: " + error); 505 * } 506 * }); 507 */ 508 LABKEY.Exp.Run = function (config) { 509 LABKEY.Exp.ExpObject.call(this, config); 510 config = config || {}; 511 512 this.experiments = config.experiments || []; 513 this.protocol = config.protocol; 514 this.filePathRoot = config.filePathRoot; 515 516 this.dataInputs = []; 517 if (config.dataInputs) { 518 for (var i = 0; i < config.dataInputs.length; i++) { 519 this.dataInputs.push(new LABKEY.Exp.Data(config.dataInputs[i])); 520 } 521 } 522 523 this.dataOutputs = config.dataOutputs || []; 524 this.dataRows = config.dataRows || []; 525 this.materialInputs = config.materialInputs || []; 526 this.materialOutputs = config.materialOutputs || []; 527 this.objectProperties = config.objectProperties || {}; 528 }; 529 LABKEY.Exp.Run.prototype = new LABKEY.Exp.ExpObject; 530 LABKEY.Exp.Run.prototype.constructor = LABKEY.Exp.Run; 531 532 /** 533 * Deletes the run from the database. 534 * @param config An object that contains the following configuration parameters 535 * @param {Function} config.success A reference to a function to call with the API results. This 536 * function will be passed the following parameters: 537 * <ul> 538 * <li><b>data:</b> a simple object with one property called 'success' which will be set to true.</li> 539 * <li><b>response:</b> The XMLHttpResponse object</li> 540 * </ul> 541 * @param {Function} [config.failure] A reference to a function to call when an error occurs. This 542 * function will be passed the following parameters: 543 * <ul> 544 * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li> 545 * <li><b>response:</b> The XMLHttpResponse object</li> 546 * </ul> 547 */ 548 LABKEY.Exp.Run.prototype.deleteRun = function(config) 549 { 550 LABKEY.Ajax.request( 551 { 552 url : LABKEY.ActionURL.buildURL("experiment", "deleteRun"), 553 method : 'POST', 554 params : { runId : this.id }, 555 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), this, false), 556 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), this, true) 557 }); 558 }; 559 560 561 562 /** 563 * The Protocol constructor is private. 564 * @class Experiment protocol. 565 * @extends LABKEY.Exp.ExpObject 566 * @memberOf LABKEY.Exp 567 * 568 * @param {Object} [config] private constructor argument. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 569 * 570 * @ignore hide from JsDoc for now 571 */ 572 LABKEY.Exp.Protocol = function (config) { 573 LABKEY.Exp.ExpObject.call(this, config); 574 config = config || {}; 575 576 this.instrument = config.instrument; 577 this.software = config.software; 578 this.contact = config.contact; 579 this.childProtocols = config.childProtocols || []; 580 this.steps = config.steps || []; 581 this.applicationType = config.applicationType; 582 this.description = config.description; 583 this.runs = []; 584 if (config.runs) { 585 for (var i = 0; i < config.runs.length; i++) { 586 this.runs.push(new LABKEY.Exp.Run(config.runs[i])); 587 } 588 } 589 }; 590 LABKEY.Exp.Protocol.prototype = new LABKEY.Exp.ExpObject; 591 LABKEY.Exp.Protocol.prototype.constructor = LABKEY.Exp.Protocol; 592 593 /** 594 * The RunGroup constructor is private. To retrieve a batch RunGroup 595 * from the server, see {@link LABKEY.Experiment.loadBatch}. 596 * @class An experiment run group contains an array of 597 * {@link LABKEY.Exp.Run}s. If all runs have the same assay protocol, the run group 598 * is considered a batch. To add runs to a batch, insert new {@link LABKEY.Exp.Run} 599 * instances into to the 'runs' Array and save the batch. 600 * <p> 601 * Use {@link LABKEY.Experiment.loadBatch} and {@link LABKEY.Experiment.saveBatch} to 602 * load and save a RunGroup. 603 * </p> 604 * <p>Additional Documentation: 605 * <ul> 606 * <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li> 607 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 608 * </ul> 609 * </p> 610 * @extends LABKEY.Exp.ExpObject 611 * @memberOf LABKEY.Exp 612 * 613 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 614 * @param {LABKEY.Exp.Run[]} config.runs Array of {@link LABKEY.Exp.Run}s in this run group. 615 * @param {Boolean} config.hidden Determines whether the RunGroup is hidden. 616 */ 617 LABKEY.Exp.RunGroup = function (config) { 618 LABKEY.Exp.ExpObject.call(this, config); 619 config = config || {}; 620 621 this.batchProtocolId = config.batchProtocolId || 0; 622 this.runs = []; 623 if (config.runs) { 624 for (var i = 0; i < config.runs.length; i++) { 625 this.runs.push(new LABKEY.Exp.Run(config.runs[i])); 626 } 627 } 628 //this.protocols = config.protocols || []; 629 //this.batchProtocol = config.batchProtocol; 630 this.hidden = config.hidden; 631 }; 632 LABKEY.Exp.RunGroup.prototype = new LABKEY.Exp.ExpObject; 633 LABKEY.Exp.RunGroup.prototype.constructor = LABKEY.Exp.RunGroup; 634 635 /** 636 * The ProtocolApplication constructor is private. 637 * @class Experiment ProtocolApplication. 638 * @extends LABKEY.Exp.ExpObject 639 * @memberOf LABKEY.Exp 640 * 641 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 642 * 643 * @ignore hide from JsDoc for now 644 */ 645 LABKEY.Exp.ProtocolApplication = function (config) { 646 LABKEY.Exp.ExpObject.call(this, config); 647 config = config || {}; 648 649 }; 650 LABKEY.Exp.ProtocolApplication.prototype = new LABKEY.Exp.ExpObject; 651 LABKEY.Exp.ProtocolApplication.prototype.constructor = LABKEY.Exp.ProtocolApplication; 652 653 /** 654 * @class The SampleSet class describes a collection of experimental samples, which are 655 * also known as materials (see {@link LABKEY.Exp.Material}). This class defines the set of fields that 656 * you you wish to attach to all samples in the group. These fields supply characteristics of the sample 657 * (e.g., its volume, number of cells, color, etc.). 658 * <p>Additional Documentation: 659 * <ul> 660 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 661 * </ul> 662 * </p> 663 * @extends LABKEY.Exp.ExpObject 664 * @memberOf LABKEY.Exp 665 * 666 * @param {Object} [config] Describes the SampleSet's properties. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 667 * @param {Object[]} config.samples Array of {@link LABKEY.Exp.Material} config objects. 668 * @param {String} config.description Description of the SampleSet 669 */ 670 671 LABKEY.Exp.SampleSet = function (config) { 672 LABKEY.Exp.ExpObject.call(this, config); 673 config = config || {}; 674 this.samples = config.samples; 675 this.description = config.description; 676 }; 677 LABKEY.Exp.SampleSet.prototype = new LABKEY.Exp.ExpObject; 678 LABKEY.Exp.SampleSet.prototype.constructor = LABKEY.Exp.SampleSet; 679 680 /** 681 * Get a domain design for the SampleSet. 682 * 683 * @param {Function} config.success Required. Function called if the 684 * "getDomain" function executes successfully. Will be called with the argument {@link LABKEY.Domain.DomainDesign}, 685 * which describes the fields of a domain. 686 * @param {Function} [config.failure] Function called if execution of the "getDomain" function fails. 687 * @param {String} [config.containerPath] The container path in which the requested Domain is defined. 688 * If not supplied, the current container path will be used. 689 * 690 * @ignore hide from JsDoc for now 691 * 692 * @example 693 * <script type="text/javascript"> 694 * Ext.onReady(function() { 695 * var ss = new LABKEY.Exp.SampleSet({name: 'MySampleSet'}); 696 * ss.getDomain({ 697 * success : function (domain) { 698 * console.log(domain); 699 * } 700 * }); 701 * } 702 * </script> 703 */ 704 LABKEY.Exp.SampleSet.prototype.getDomain = function (config) 705 { 706 LABKEY.Domain.get(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "Samples", this.name, config.containerPath); 707 }; 708 709 /** 710 * Create a new Sample Set definition. 711 * @param {Function} config.success Required callback function. 712 * @param {Function} [config.failure] Failure callback function. 713 * @param {LABKEY.Domain.DomainDesign} config.domainDesign The domain design to save. 714 * @param {Object} [config.options] Set of extra options used when creating the SampleSet: 715 * <ul> 716 * <li>idCols: Optional. Array of indexes into the domain design fields. If the domain design contains a 'Name' field, no idCols are allowed. Either a 'Name' field must be present or at least one idCol must be supplied.. 717 * <li>parentCol: Optional. Index of the parent id column. 718 * </ul> 719 * @param {String} [config.containerPath] The container path in which to create the domain. 720 * @static 721 * 722 * @ignore hide from JsDoc for now 723 * 724 * @example 725 * var domainDesign = { 726 * name: "BoyHowdy", 727 * description: "A client api created sample set", 728 * fields: [{ 729 * name: 'TestName', 730 * label: 'The First Field', 731 * rangeURI: 'http://www.w3.org/2001/XMLSchema#string' 732 * },{ 733 * name: 'Num', 734 * rangeURI: 'http://www.w3.org/2001/XMLSchema#int' 735 * },{ 736 * name: 'Parent', 737 * rangeURI: 'http://www.w3.org/2001/XMLSchema#string' 738 * }] 739 * }; 740 * 741 * LABKEY.Exp.SampleSet.create({ 742 * success: function () { alert("success!"); }, 743 * failure: function () { alert("failure!"); }, 744 * domainDesign: domainDesign, 745 * options: { idCols: [0, 1], parentCol: 2 } 746 * }); 747 */ 748 LABKEY.Exp.SampleSet.create = function (config) 749 { 750 LABKEY.Domain.create(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "SampleSet", config.domainDesign, config.options, config.containerPath); 751 }; 752 753 /** 754 * DataClass represents a set of ExpData objects that share a set of properties. 755 * 756 * @class DataClass describes a collection of Data objects. 757 * This class defines the set of fields that you you wish to attach to all datas in the group. 758 * Within the DataClass, each Data has a unique name. 759 * 760 * @extends LABKEY.Exp.ExpObject 761 * @memberOf LABKEY.Exp 762 * 763 * @param config 764 * @param {String} config.description Description of the DataClass. 765 * @param {String} [config.nameExpression] Optional name expression used to generate unique names for ExpData inserted into the DataClass. 766 * @param {Object} [config.sampleSet] The optional SampleSet the DataClass is associated with. With the following properties: 767 * @param {Integer} [config.sampleSet.id] The row id of the SampleSet. 768 * @param {String} [config.sampleSet.name] The name of the SampleSet. 769 * @constructor 770 */ 771 LABKEY.Exp.DataClass = function (config) 772 { 773 "use strict"; 774 775 LABKEY.Exp.ExpObject.call(this, config); 776 config = config || {}; 777 this.data = config.data; 778 this.description = config.description; 779 this.sampleSet = config.sampleSet; 780 }; 781 LABKEY.Exp.DataClass.prototype = new LABKEY.Exp.ExpObject; 782 LABKEY.Exp.DataClass.prototype.constructor = LABKEY.Exp.DataClass; 783 784 LABKEY.Exp.DataClass.prototype.getDomain = function (config) 785 { 786 "use strict"; 787 LABKEY.Domain.get({ 788 success: LABKEY.Utils.getOnSuccess(config), 789 failure: LABKEY.Utils.getOnFailure(config), 790 schemaName: "exp.data", 791 queryName: this.name, 792 containerPath: config.containerPath 793 }); 794 }; 795 796 LABKEY.Exp.DataClass.create = function (config) 797 { 798 "use strict"; 799 LABKEY.Domain.create({ 800 success: LABKEY.Utils.getOnSuccess(config), 801 failure: LABKEY.Utils.getOnFailure(config), 802 type: "dataclass", 803 domainDesign: config.domainDesign, 804 options: config.options, 805 containerPath: config.containerPath 806 }); 807 }; 808 809 /** 810 * The ChildObject constructor is private. 811 * @class Experiment Child 812 * @extends LABKEY.Exp.ExpObject 813 * @memberOf LABKEY.Exp 814 * 815 * @param {Object} [config] Private configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 816 * 817 * @ignore hide from JsDoc for now 818 */ 819 LABKEY.Exp.ChildObject = function (config) { 820 LABKEY.Exp.ExpObject.call(this, config); 821 config = config || {}; 822 // property holder 823 }; 824 LABKEY.Exp.ChildObject.prototype = new LABKEY.Exp.ExpObject; 825 LABKEY.Exp.ChildObject.prototype.constructor = LABKEY.Exp.ChildObject; 826 827 /** 828 * The RunItem constructor is private. 829 * @class Experiment Run Item. Base class for {@link LABKEY.Exp.Data} 830 * and {@link LABKEY.Exp.Material}. 831 * @extends LABKEY.Exp.ExpObject 832 * @memberOf LABKEY.Exp 833 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 834 * @ignore hide from JsDoc for now 835 */ 836 LABKEY.Exp.RunItem = function (config) { 837 LABKEY.Exp.ExpObject.call(this, config); 838 config = config || {}; 839 840 this.sourceProtocol = config.sourceProtocol; 841 this.run = config.run; 842 this.targetApplications = config.targetApplications; 843 this.sourceApplications = config.sourceApplications; 844 this.sucessorRuns = config.sucessorRuns; 845 this.cpasType = config.cpasType; 846 }; 847 LABKEY.Exp.RunItem.prototype = new LABKEY.Exp.ExpObject; 848 LABKEY.Exp.RunItem.prototype.constructor = LABKEY.Exp.RunItem; 849 850 /** 851 * Constructs a new experiment material object. 852 * @class The Exp.Material class describes an experiment material. "Material" is a synonym for both 853 * "sample" and "specimen." Thus, for example, the input to an assay could be called a material. 854 * The fields of this class are inherited from the {@link LABKEY.Exp.ExpObject} object and 855 * the private LABKEY.Exp.RunItem object. 856 * <p>Additional Documentation: 857 * <ul> 858 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 859 * </ul> 860 * </p> 861 * @extends LABKEY.Exp.RunItem 862 * @extends LABKEY.Exp.ExpObject 863 * @memberOf LABKEY.Exp 864 * 865 * @param {Object} [config] Configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 866 * @param {Object} [config.sampleSet] The SampleSet the material belongs to. With the following properties: 867 * @param {Integer} [config.sampleSet.id] The row id of the SampleSet. 868 * @param {String} [config.sampleSet.name] The name of the SampleSet. 869 */ 870 LABKEY.Exp.Material = function (config) { 871 LABKEY.Exp.RunItem.call(this, config); 872 config = config || {}; 873 874 this.sampleSet = config.sampleSet; 875 }; 876 LABKEY.Exp.Material.prototype = new LABKEY.Exp.RunItem; 877 LABKEY.Exp.Material.prototype.constructor = LABKEY.Exp.Material; 878 879 /** 880 * The Data constructor is private. 881 * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of 882 * the "assay" controller. 883 * 884 * @class The Experiment Data class describes the data input or output of a {@link LABKEY.Exp.Run}. This typically 885 * corresponds to an assay results file uploaded to the LabKey server. 886 * <p> 887 * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of 888 * the "assay" controller. 889 * </p> 890 * <p>Additional Documentation: 891 * <ul> 892 * <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li> 893 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 894 * </ul> 895 * </p> 896 * 897 * @extends LABKEY.Exp.RunItem 898 * @extends LABKEY.Exp.ExpObject 899 * @memberOf LABKEY.Exp 900 * 901 * @param {Object} [config] Private configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 902 * @param {String} config.dataFileURL The local file url of the uploaded file. 903 * @param {Object} [config.dataClass] The DataClass the data belongs to. With the following properties: 904 * @param {Integer} [config.dataClass.id] The row id of the DataClass. 905 * @param {String} [config.dataClass.name] The name of the DataClass. 906 * 907 * @example 908 * // To perform a file upload over HTTP: 909 * <form id="upload-run-form" enctype="multipart/form-data" method="POST"> 910 * <div id="upload-run-button"></div> 911 * </form> 912 * <script type="text/javascript"> 913 * LABKEY.Utils.requiresScript("FileUploadField.js"); 914 * // Optional - specify a protocolId so that the Exp.Data object is assigned the related LSID namespace. 915 * var url = LABKEY.ActionURL.buildURL("assay", "assayFileUpload", LABKEY.ActionURL.getContainer(), { protocolId: 50 }); 916 * Ext.onReady(function() { 917 * var form = new Ext.form.BasicForm( 918 * Ext.get("upload-run-form"), { 919 * fileUpload: true, 920 * frame: false, 921 * url: url, 922 * listeners: { 923 * actioncomplete : function (form, action) { 924 * alert('Upload successful!'); 925 * var data = new LABKEY.Exp.Data(action.result); 926 * 927 * // now add the data as a dataInput to a LABKEY.Exp.Run 928 * var run = new LABKEY.Exp.Run(); 929 * run.name = data.name; 930 * run.dataInputs = [ data ]; 931 * 932 * // add the new run to a LABKEY.Exp.Batch object and 933 * // fetch the parsed file contents from the data object 934 * // using the LABKEY.Exp.Data#getContent() method. 935 * }, 936 * actionfailed: function (form, action) { 937 * alert('Upload failed!'); 938 * } 939 * } 940 * }); 941 * 942 * var uploadField = new Ext.form.FileUploadField({ 943 * id: "upload-run-field", 944 * renderTo: "upload-run-button", 945 * buttonText: "Upload Data...", 946 * buttonOnly: true, 947 * buttonCfg: { cls: "labkey-button" }, 948 * listeners: { 949 * "fileselected": function (fb, v) { 950 * form.submit(); 951 * } 952 * } 953 * }); 954 * }); 955 * </script> 956 * 957 * // Or, to upload the contents of a JavaScript string as a file: 958 * <script type="text/javascript"> 959 * Ext.onReady(function() { 960 * LABKEY.Ajax.request({ 961 * url: LABKEY.ActionURL.buildURL("assay", "assayFileUpload"), 962 * params: { fileName: 'test.txt', fileContent: 'Some text!' }, 963 * success: function(response, options) { 964 * var data = new LABKEY.Exp.Data(Ext.util.JSON.decode(response.responseText)); 965 * 966 * // now add the data as a dataInput to a LABKEY.Exp.Run 967 * var run = new LABKEY.Exp.Run(); 968 * run.name = data.name; 969 * run.dataInputs = [ data ]; 970 * 971 * // add the new run to a LABKEY.Exp.Batch object here 972 * } 973 * }); 974 * }); 975 * 976 * </script> 977 */ 978 LABKEY.Exp.Data = function (config) { 979 LABKEY.Exp.RunItem.call(this, config); 980 config = config || {}; 981 982 this.dataType = config.dataType; 983 this.dataFileURL = config.dataFileURL; 984 this.dataClass = config.dataClass; 985 if (config.pipelinePath) 986 this.pipelinePath = config.pipelinePath; 987 if (config.role) 988 this.role = config.role; 989 }; 990 LABKEY.Exp.Data.prototype = new LABKEY.Exp.RunItem; 991 LABKEY.Exp.Data.prototype.constructor = LABKEY.Exp.Data; 992 993 /** 994 * Retrieves the contents of the data object from the server. 995 * @param config An object that contains the following configuration parameters 996 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 997 * @param {function} config.success The function to call when the function finishes successfully. 998 * This function will be called with the parameters: 999 * <ul> 1000 * <li><b>content</b> The type of the content varies based on the format requested. 1001 * <li><b>format</b> The format used in the request 1002 * <li><b>response</b> The original response 1003 * </ul> 1004 * @param {function} [config.failure] The function to call if this function encounters an error. 1005 * This function will be called with the following parameters: 1006 * <ul> 1007 * <li><b>errorInfo:</b> An object with a property called "exception," which contains the error message.</li> 1008 * <li><b>format</b> The format used in the request 1009 * <li><b>response</b> The original response 1010 * </ul> 1011 * @param {String} [config.format] How to format the content. Defaults to plaintext, supported for text/* MIME types, 1012 * including .html, .xml, .tsv, .txt, and .csv. Use 'jsonTSV' to get a JSON version of the .xls, .tsv, .or .csv 1013 * files, the structure of which matches the argument to convertToExcel in {@link LABKEY.Utils}. 1014 * <ul> 1015 * <li><b>fileName:</b> the name of the file</li> 1016 * <li><b>sheets:</b> an array of the sheets in the file. Text file types will have a single sheet named 'flat'. 1017 * <ul><li><b>name:</b> the name of the sheet</li> 1018 * <li><b>values:</b> two-dimensional array of all the cells in the worksheet. First array index is row, second is column</li> 1019 * </ul> 1020 * </ul> 1021 * <br/>Use 'jsonTSVExtended' to get include metadata in the 2D array of cells. 1022 * Text file types will not supply additional metadata but populate the 'value' attribute in the map. 1023 * Excel files will include: 1024 * <ul> 1025 * <li><b>value:</b> the string, boolean, date, or number in the cell</li> 1026 * <li><b>timeOnly:</b> whether the date part should be ignored for dates</li> 1027 * <li><b>formatString:</b> the Java format string to be used to render the value for dates and numbers</li> 1028 * <li><b>formattedValue:</b> the formatted string for that value for all value types</li> 1029 * <li><b>error:</b> true if this cell has an error</li> 1030 * <li><b>formula:</b> if the cell's value is specified by a formula, the text of the formula</li> 1031 * </ul> 1032 * <br/>Use 'jsonTSVIgnoreTypes' to always return string values for all cells, regardless of type. 1033 * <br/> 1034 * An example of the results for a request for 'jsonTsv' format: 1035 * <pre> 1036 * { 1037 "sheets": [ 1038 { 1039 "name": "Sheet1", 1040 "data": [ 1041 [ 1042 "StringColumn", 1043 "DateColumn" 1044 ], 1045 [ 1046 "Hello", 1047 "16 May 2009 17:00:00" 1048 ], 1049 [ 1050 "world", 1051 "12/21/2008 08:45AM" 1052 ] 1053 ] 1054 }, 1055 { 1056 "name": "Sheet2", 1057 "data": [ 1058 ["NumberColumn"], 1059 [55.44], 1060 [100.34], 1061 [-1] 1062 ] 1063 }, 1064 { 1065 "name": "Sheet3", 1066 "data": [] 1067 } 1068 ], 1069 "fileName": "SimpleExcelFile.xls" 1070 }</pre> 1071 <br/> 1072 An example of the same file in the 'jsonTSVExtended' format: 1073 <pre> 1074 * { 1075 "sheets": [ 1076 { 1077 "name": "Sheet1", 1078 "data": [ 1079 [ 1080 { 1081 "value": "StringColumn", 1082 "formattedValue": "StringColumn" 1083 }, 1084 { 1085 "value": "DateColumn", 1086 "formattedValue": "DateColumn" 1087 } 1088 ], 1089 [ 1090 { 1091 "value": "Hello", 1092 "formattedValue": "Hello" 1093 }, 1094 { 1095 "formatString": "MMMM d, yyyy", 1096 "value": "16 May 2009 17:00:00", 1097 "timeOnly": false, 1098 "formattedValue": "May 17, 2009" 1099 } 1100 ], 1101 [ 1102 { 1103 "value": "world", 1104 "formattedValue": "world" 1105 }, 1106 { 1107 "formatString": "M/d/yy h:mm a", 1108 "value": "21 Dec 2008 19:31:00", 1109 "timeOnly": false, 1110 "formattedValue": "12/21/08 7:31 PM" 1111 } 1112 ] 1113 ] 1114 }, 1115 { 1116 "name": "Sheet2", 1117 "data": [ 1118 [{ 1119 "value": "NumberColumn", 1120 "formattedValue": "NumberColumn" 1121 }], 1122 [{ 1123 "formatString": "$#,##0.00", 1124 "value": 55.44, 1125 "formattedValue": "$55.44" 1126 }], 1127 [{ 1128 "value": 100.34, 1129 "formattedValue": "100.34" 1130 }], 1131 [{ 1132 "value": -1, 1133 "formattedValue": "-1" 1134 }] 1135 ] 1136 }, 1137 { 1138 "name": "Sheet3", 1139 "data": [] 1140 } 1141 ], 1142 "fileName": "SimpleExcelFile.xls" 1143 } 1144 </pre> 1145 * 1146 */ 1147 LABKEY.Exp.Data.prototype.getContent = function(config) 1148 { 1149 if(!LABKEY.Utils.getOnSuccess(config)) 1150 { 1151 alert("You must specify a callback function in config.success when calling LABKEY.Exp.Data.getContent()!"); 1152 return; 1153 } 1154 1155 function getSuccessCallbackWrapper(fn, format, scope) 1156 { 1157 return function(response) 1158 { 1159 //ensure response is JSON before trying to decode 1160 var content = null; 1161 if(response && response.getResponseHeader && response.getResponseHeader('Content-Type') 1162 && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0) 1163 { 1164 content = LABKEY.Utils.decode(response.responseText); 1165 } 1166 else 1167 { 1168 content = response.responseText; 1169 } 1170 1171 if(fn) 1172 fn.call(scope || this, content, format, response); 1173 }; 1174 } 1175 1176 LABKEY.Ajax.request( 1177 { 1178 url : LABKEY.ActionURL.buildURL("experiment", "showFile"), 1179 method : 'GET', 1180 params : { rowId : this.id, format: config.format }, 1181 success: getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.format, config.scope), 1182 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true) 1183 }); 1184 1185 }; 1186 1187 1188