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