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/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 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/Documentation/wiki-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/Documentation/wiki-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/Documentation/wiki-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/Documentation/wiki-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.veryNewHotness !== undefined) 369 params.veryNewHotness = config.veryNewHotness; 370 if (config.rowId) 371 params.rowId = config.rowId; 372 else if (config.lsid) 373 params.lsid = config.lsid; 374 375 if (config.hasOwnProperty('parents')) 376 params.parents = config.parents; 377 if (config.hasOwnProperty('children')) 378 params.children = config.children; 379 if (config.hasOwnProperty('depth')) 380 params.depth = config.depth; 381 382 if (config.expType) 383 params.expType = config.expType; 384 if (config.cpasType) 385 params.cpasType = config.cpasType; 386 387 LABKEY.Ajax.request({ 388 method: 'GET', 389 url: LABKEY.ActionURL.buildURL("experiment", "lineage.api"), 390 params: params, 391 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.scope), 392 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true), 393 scope: config.scope 394 }); 395 } 396 }; 397 398 }; 399 400 if (typeof LABKEY.Exp == "undefined") 401 LABKEY.Exp = {}; 402 403 /** 404 * This constructor isn't called directly, but is used by derived classes. 405 * @class The experiment object base class describes basic 406 * characteristics of a protocol or an experimental run. Many experiment classes (such as {@link LABKEY.Exp.Run}, 407 * {@link LABKEY.Exp.Data} and {@link LABKEY.Exp.Material}) are subclasses 408 * of ExpObject, so they provide the fields defined by this object (e.g., name, lsid, etc). 409 * In a Java representation of these same classes, ExpObject is an abstract class. 410 * <p>Additional Documentation: 411 * <ul> 412 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 413 * </ul> 414 * </p> 415 * @memberOf LABKEY.Exp 416 * 417 * @param {Object} [config] Configuration object. 418 * 419 * @param {String} config.lsid The LSID of the ExpObject. 420 * @param {String} config.name The name of the ExpObject. 421 * @param {number} config.id The id of the ExpObject. 422 * @param {number} config.rowId The id of the ExpObject (alias of id property) 423 * @param {String} config.comment User editable comment. 424 * @param {Date} config.created When the ExpObject was created. 425 * @param {String} config.createdBy The person who created the ExpObject. 426 * @param {Date} config.modified When the ExpObject was last modified. 427 * @param {String} config.modifiedBy The person who last modified the ExpObject. 428 * @param {Object} config.properties Map of property descriptor names to values. Most types, such as strings and 429 * numbers, are just stored as simple properties. Properties of type FileLink will be returned by the server in the 430 * same format as {@link LABKEY.Exp.Data} objects (missing many properties such as id and createdBy if they exist on disk but 431 * 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 432 * following three types: the data's RowId, the data's LSID, or the full path on the server's file system. 433 */ 434 LABKEY.Exp.ExpObject = function (config) { 435 config = config || {}; 436 this.lsid = config.lsid; 437 this.name = config.name; 438 this.id = config.id || config.rowId; 439 this.rowId = this.id; 440 this.comment = config.comment; 441 this.created = config.created; 442 this.createdBy = config.createdBy; 443 this.modified = config.modified; 444 this.modifiedBy = config.modifiedBy; 445 this.properties = config.properties || {}; 446 }; 447 448 /** 449 * Constructs a new experiment run object. 450 * @class The Exp.Run class describes an experiment run. An experiment run is an application of an experimental 451 * protocol to concrete inputs, producing concrete outputs. In object-oriented terminology, a protocol would be a class 452 * while a run would be an instance. 453 * <p>Additional Documentation: 454 * <ul> 455 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 456 * </ul> 457 * </p> 458 * @extends LABKEY.Exp.ExpObject 459 * @memberOf LABKEY.Exp 460 * 461 * @param {Object} [config] The configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 462 * @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. 463 * @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. 464 * @param {Object[]} config.dataRows Array of Objects where each Object corresponds to a row in the results domain. 465 * @param {Object[]} config.materialInputs Array of {@link LABKEY.Exp.Material} objects that are material/sample inputs to the run. 466 * @param {Object[]} config.materialOutputs Array of {@link LABKEY.Exp.Material} objects that are material/sample outputs from the run. 467 * 468 * @see LABKEY.Exp.Data#getContent 469 * 470 * @example 471 * var result = // ... result of uploading a new assay results file 472 * var data = new LABKEY.Exp.Data(result); 473 * 474 * var run = new LABKEY.Exp.Run(); 475 * run.name = data.name; 476 * run.properties = { "MyRunProperty" : 3 }; 477 * run.dataInputs = [ data ]; 478 * 479 * data.getContent({ 480 * format: 'jsonTSV', 481 * success: function (content, format) { 482 * data.content = content; 483 * var sheet = content.sheets[0]; 484 * var filedata = sheet.data; 485 * 486 * // transform the file content into the dataRows array used by the run 487 * run.dataRows = []; 488 * for (var i = 1; i < filedata.length; i++) { 489 * var row = filedata[i]; 490 * run.dataRows.push({ 491 * "SampleId": row[0], 492 * "DataValue": row[1], 493 * // ... other columns 494 * }); 495 * } 496 * 497 * var batch = // ... the LABKEY.Exp.RunGroup object 498 * batch.runs.push(run); 499 * }, 500 * failure: function (error, format) { 501 * alert("error: " + error); 502 * } 503 * }); 504 */ 505 LABKEY.Exp.Run = function (config) { 506 LABKEY.Exp.ExpObject.call(this, config); 507 config = config || {}; 508 509 this.experiments = config.experiments || []; 510 this.protocol = config.protocol; 511 this.filePathRoot = config.filePathRoot; 512 513 this.dataInputs = []; 514 if (config.dataInputs) { 515 for (var i = 0; i < config.dataInputs.length; i++) { 516 this.dataInputs.push(new LABKEY.Exp.Data(config.dataInputs[i])); 517 } 518 } 519 520 this.dataOutputs = config.dataOutputs || []; 521 this.dataRows = config.dataRows || []; 522 this.materialInputs = config.materialInputs || []; 523 this.materialOutputs = config.materialOutputs || []; 524 this.objectProperties = config.objectProperties || {}; 525 }; 526 LABKEY.Exp.Run.prototype = new LABKEY.Exp.ExpObject; 527 LABKEY.Exp.Run.prototype.constructor = LABKEY.Exp.Run; 528 529 /** 530 * Deletes the run from the database. 531 * @param config An object that contains the following configuration parameters 532 * @param {Function} config.success A reference to a function to call with the API results. This 533 * function will be passed the following parameters: 534 * <ul> 535 * <li><b>data:</b> a simple object with one property called 'success' which will be set to true.</li> 536 * <li><b>response:</b> The XMLHttpResponse object</li> 537 * </ul> 538 * @param {Function} [config.failure] A reference to a function to call when an error occurs. This 539 * function will be passed the following parameters: 540 * <ul> 541 * <li><b>errorInfo:</b> an object containing detailed error information (may be null)</li> 542 * <li><b>response:</b> The XMLHttpResponse object</li> 543 * </ul> 544 */ 545 LABKEY.Exp.Run.prototype.deleteRun = function(config) 546 { 547 LABKEY.Ajax.request( 548 { 549 url : LABKEY.ActionURL.buildURL("experiment", "deleteRun"), 550 method : 'POST', 551 params : { runId : this.id }, 552 success: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnSuccess(config), this, false), 553 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), this, true) 554 }); 555 }; 556 557 558 559 /** 560 * The Protocol constructor is private. 561 * @class Experiment protocol. 562 * @extends LABKEY.Exp.ExpObject 563 * @memberOf LABKEY.Exp 564 * 565 * @param {Object} [config] private constructor argument. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 566 * 567 * @ignore hide from JsDoc for now 568 */ 569 LABKEY.Exp.Protocol = function (config) { 570 LABKEY.Exp.ExpObject.call(this, config); 571 config = config || {}; 572 573 this.instrument = config.instrument; 574 this.software = config.software; 575 this.contact = config.contact; 576 this.childProtocols = config.childProtocols || []; 577 this.steps = config.steps || []; 578 this.applicationType = config.applicationType; 579 this.description = config.description; 580 this.runs = []; 581 if (config.runs) { 582 for (var i = 0; i < config.runs.length; i++) { 583 this.runs.push(new LABKEY.Exp.Run(config.runs[i])); 584 } 585 } 586 }; 587 LABKEY.Exp.Protocol.prototype = new LABKEY.Exp.ExpObject; 588 LABKEY.Exp.Protocol.prototype.constructor = LABKEY.Exp.Protocol; 589 590 /** 591 * The RunGroup constructor is private. To retrieve a batch RunGroup 592 * from the server, see {@link LABKEY.Experiment.loadBatch}. 593 * @class An experiment run group contains an array of 594 * {@link LABKEY.Exp.Run}s. If all runs have the same assay protocol, the run group 595 * is considered a batch. To add runs to a batch, insert new {@link LABKEY.Exp.Run} 596 * instances into to the 'runs' Array and save the batch. 597 * <p> 598 * Use {@link LABKEY.Experiment.loadBatch} and {@link LABKEY.Experiment.saveBatch} to 599 * load and save a RunGroup. 600 * </p> 601 * <p>Additional Documentation: 602 * <ul> 603 * <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li> 604 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 605 * </ul> 606 * </p> 607 * @extends LABKEY.Exp.ExpObject 608 * @memberOf LABKEY.Exp 609 * 610 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 611 * @param {LABKEY.Exp.Run[]} config.runs Array of {@link LABKEY.Exp.Run}s in this run group. 612 * @param {Boolean} config.hidden Determines whether the RunGroup is hidden. 613 */ 614 LABKEY.Exp.RunGroup = function (config) { 615 LABKEY.Exp.ExpObject.call(this, config); 616 config = config || {}; 617 618 this.batchProtocolId = config.batchProtocolId || 0; 619 this.runs = []; 620 if (config.runs) { 621 for (var i = 0; i < config.runs.length; i++) { 622 this.runs.push(new LABKEY.Exp.Run(config.runs[i])); 623 } 624 } 625 //this.protocols = config.protocols || []; 626 //this.batchProtocol = config.batchProtocol; 627 this.hidden = config.hidden; 628 }; 629 LABKEY.Exp.RunGroup.prototype = new LABKEY.Exp.ExpObject; 630 LABKEY.Exp.RunGroup.prototype.constructor = LABKEY.Exp.RunGroup; 631 632 /** 633 * The ProtocolApplication constructor is private. 634 * @class Experiment ProtocolApplication. 635 * @extends LABKEY.Exp.ExpObject 636 * @memberOf LABKEY.Exp 637 * 638 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 639 * 640 * @ignore hide from JsDoc for now 641 */ 642 LABKEY.Exp.ProtocolApplication = function (config) { 643 LABKEY.Exp.ExpObject.call(this, config); 644 config = config || {}; 645 646 }; 647 LABKEY.Exp.ProtocolApplication.prototype = new LABKEY.Exp.ExpObject; 648 LABKEY.Exp.ProtocolApplication.prototype.constructor = LABKEY.Exp.ProtocolApplication; 649 650 /** 651 * @class The SampleSet class describes a collection of experimental samples, which are 652 * also known as materials (see {@link LABKEY.Exp.Material}). This class defines the set of fields that 653 * you you wish to attach to all samples in the group. These fields supply characteristics of the sample 654 * (e.g., its volume, number of cells, color, etc.). 655 * <p>Additional Documentation: 656 * <ul> 657 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 658 * </ul> 659 * </p> 660 * @extends LABKEY.Exp.ExpObject 661 * @memberOf LABKEY.Exp 662 * 663 * @param {Object} [config] Describes the SampleSet's properties. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 664 * @param {Object[]} config.samples Array of {@link LABKEY.Exp.Material} config objects. 665 * @param {String} config.description Description of the SampleSet 666 */ 667 668 LABKEY.Exp.SampleSet = function (config) { 669 LABKEY.Exp.ExpObject.call(this, config); 670 config = config || {}; 671 this.samples = config.samples; 672 this.description = config.description; 673 }; 674 LABKEY.Exp.SampleSet.prototype = new LABKEY.Exp.ExpObject; 675 LABKEY.Exp.SampleSet.prototype.constructor = LABKEY.Exp.SampleSet; 676 677 /** 678 * Get a domain design for the SampleSet. 679 * 680 * @param {Function} config.success Required. Function called if the 681 * "getDomain" function executes successfully. Will be called with the argument {@link LABKEY.Domain.DomainDesign}, 682 * which describes the fields of a domain. 683 * @param {Function} [config.failure] Function called if execution of the "getDomain" function fails. 684 * @param {String} [config.containerPath] The container path in which the requested Domain is defined. 685 * If not supplied, the current container path will be used. 686 * 687 * @ignore hide from JsDoc for now 688 * 689 * @example 690 * <script type="text/javascript"> 691 * Ext.onReady(function() { 692 * var ss = new LABKEY.Exp.SampleSet({name: 'MySampleSet'}); 693 * ss.getDomain({ 694 * success : function (domain) { 695 * console.log(domain); 696 * } 697 * }); 698 * } 699 * </script> 700 */ 701 LABKEY.Exp.SampleSet.prototype.getDomain = function (config) 702 { 703 LABKEY.Domain.get(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "Samples", this.name, config.containerPath); 704 }; 705 706 /** 707 * Create a new Sample Set definition. 708 * @param {Function} config.success Required callback function. 709 * @param {Function} [config.failure] Failure callback function. 710 * @param {LABKEY.Domain.DomainDesign} config.domainDesign The domain design to save. 711 * @param {Object} [config.options] Set of extra options used when creating the SampleSet: 712 * <ul> 713 * <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.. 714 * <li>parentCol: Optional. Index of the parent id column. 715 * </ul> 716 * @param {String} [config.containerPath] The container path in which to create the domain. 717 * @static 718 * 719 * @ignore hide from JsDoc for now 720 * 721 * @example 722 * var domainDesign = { 723 * name: "BoyHowdy", 724 * description: "A client api created sample set", 725 * fields: [{ 726 * name: 'TestName', 727 * label: 'The First Field', 728 * rangeURI: 'http://www.w3.org/2001/XMLSchema#string' 729 * },{ 730 * name: 'Num', 731 * rangeURI: 'http://www.w3.org/2001/XMLSchema#int' 732 * },{ 733 * name: 'Parent', 734 * rangeURI: 'http://www.w3.org/2001/XMLSchema#string' 735 * }] 736 * }; 737 * 738 * LABKEY.Exp.SampleSet.create({ 739 * success: function () { alert("success!"); }, 740 * failure: function () { alert("failure!"); }, 741 * domainDesign: domainDesign, 742 * options: { idCols: [0, 1], parentCol: 2 } 743 * }); 744 */ 745 LABKEY.Exp.SampleSet.create = function (config) 746 { 747 LABKEY.Domain.create(LABKEY.Utils.getOnSuccess(config), LABKEY.Utils.getOnFailure(config), "SampleSet", config.domainDesign, config.options, config.containerPath); 748 }; 749 750 /** 751 * DataClass represents a set of ExpData objects that share a set of properties. 752 * 753 * @class DataClass describes a collection of Data objects. 754 * This class defines the set of fields that you you wish to attach to all datas in the group. 755 * Within the DataClass, each Data has a unique name. 756 * 757 * @extends LABKEY.Exp.ExpObject 758 * @memberOf LABKEY.Exp 759 * 760 * @param config 761 * @param {String} config.description Description of the DataClass. 762 * @param {String} [config.nameExpression] Optional name expression used to generate unique names for ExpData inserted into the DataClass. 763 * @param {Object} [config.sampleSet] The optional SampleSet the DataClass is associated with. With the following properties: 764 * @param {Integer} [config.sampleSet.id] The row id of the SampleSet. 765 * @param {String} [config.sampleSet.name] The name of the SampleSet. 766 * @constructor 767 */ 768 LABKEY.Exp.DataClass = function (config) 769 { 770 "use strict"; 771 772 LABKEY.Exp.ExpObject.call(this, config); 773 config = config || {}; 774 this.data = config.data; 775 this.description = config.description; 776 this.sampleSet = config.sampleSet; 777 }; 778 LABKEY.Exp.DataClass.prototype = new LABKEY.Exp.ExpObject; 779 LABKEY.Exp.DataClass.prototype.constructor = LABKEY.Exp.DataClass; 780 781 LABKEY.Exp.DataClass.prototype.getDomain = function (config) 782 { 783 "use strict"; 784 LABKEY.Domain.get({ 785 success: LABKEY.Utils.getOnSuccess(config), 786 failure: LABKEY.Utils.getOnFailure(config), 787 schemaName: "exp.data", 788 queryName: this.name, 789 containerPath: config.containerPath 790 }); 791 }; 792 793 LABKEY.Exp.DataClass.create = function (config) 794 { 795 "use strict"; 796 LABKEY.Domain.create({ 797 success: LABKEY.Utils.getOnSuccess(config), 798 failure: LABKEY.Utils.getOnFailure(config), 799 type: "dataclass", 800 domainDesign: config.domainDesign, 801 options: config.options, 802 containerPath: config.containerPath 803 }); 804 }; 805 806 /** 807 * The ChildObject constructor is private. 808 * @class Experiment Child 809 * @extends LABKEY.Exp.ExpObject 810 * @memberOf LABKEY.Exp 811 * 812 * @param {Object} [config] Private configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 813 * 814 * @ignore hide from JsDoc for now 815 */ 816 LABKEY.Exp.ChildObject = function (config) { 817 LABKEY.Exp.ExpObject.call(this, config); 818 config = config || {}; 819 // property holder 820 }; 821 LABKEY.Exp.ChildObject.prototype = new LABKEY.Exp.ExpObject; 822 LABKEY.Exp.ChildObject.prototype.constructor = LABKEY.Exp.ChildObject; 823 824 /** 825 * The ProtocolOutput constructor is private. 826 * @class Experiment Protocol Output. Base class for {@link LABKEY.Exp.Data} 827 * and {@link LABKEY.Exp.Material}. 828 * @extends LABKEY.Exp.ExpObject 829 * @memberOf LABKEY.Exp 830 * @param {Object} [config] Private configuration object. Inherits config properties of {@link LABKEY.Exp.ExpObject}. 831 * @ignore hide from JsDoc for now 832 */ 833 LABKEY.Exp.ProtocolOutput = function (config) { 834 LABKEY.Exp.ExpObject.call(this, config); 835 config = config || {}; 836 837 this.sourceProtocol = config.sourceProtocol; 838 this.run = config.run; 839 this.targetApplications = config.targetApplications; 840 this.sourceApplications = config.sourceApplications; 841 this.sucessorRuns = config.sucessorRuns; 842 this.cpasType = config.cpasType; 843 }; 844 LABKEY.Exp.ProtocolOutput.prototype = new LABKEY.Exp.ExpObject; 845 LABKEY.Exp.ProtocolOutput.prototype.constructor = LABKEY.Exp.ProtocolOutput; 846 847 /** 848 * Constructs a new experiment material object. 849 * @class The Exp.Material class describes an experiment material. "Material" is a synonym for both 850 * "sample" and "specimen." Thus, for example, the input to an assay could be called a material. 851 * The fields of this class are inherited from the {@link LABKEY.Exp.ExpObject} object and 852 * the private LABKEY.Exp.ProtocolOutput object. 853 * <p>Additional Documentation: 854 * <ul> 855 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 856 * </ul> 857 * </p> 858 * @extends LABKEY.Exp.ProtocolOutput 859 * @extends LABKEY.Exp.ExpObject 860 * @memberOf LABKEY.Exp 861 * 862 * @param {Object} [config] Configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 863 * @param {Object} [config.sampleSet] The SampleSet the material belongs to. With the following properties: 864 * @param {Integer} [config.sampleSet.id] The row id of the SampleSet. 865 * @param {String} [config.sampleSet.name] The name of the SampleSet. 866 */ 867 LABKEY.Exp.Material = function (config) { 868 LABKEY.Exp.ProtocolOutput.call(this, config); 869 config = config || {}; 870 871 this.sampleSet = config.sampleSet; 872 }; 873 LABKEY.Exp.Material.prototype = new LABKEY.Exp.ProtocolOutput; 874 LABKEY.Exp.Material.prototype.constructor = LABKEY.Exp.Material; 875 876 /** 877 * The Data constructor is private. 878 * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of 879 * the "assay" controller. 880 * 881 * @class The Experiment Data class describes the data input or output of a {@link LABKEY.Exp.Run}. This typically 882 * corresponds to an assay results file uploaded to the LabKey server. 883 * <p> 884 * To create a LABKEY.Exp.Data object, upload a file using to the "assayFileUpload" action of 885 * the "assay" controller. 886 * </p> 887 * <p>Additional Documentation: 888 * <ul> 889 * <li><a href='https://www.labkey.org/Documentation/wiki-page.view?name=moduleassay'>LabKey File-Based Assays</a></li> 890 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=experiment">LabKey Experiment</a></li> 891 * </ul> 892 * </p> 893 * 894 * @extends LABKEY.Exp.ProtocolOutput 895 * @extends LABKEY.Exp.ExpObject 896 * @memberOf LABKEY.Exp 897 * 898 * @param {Object} [config] Private configuration object. Inherits the config properties of {@link LABKEY.Exp.ExpObject}. 899 * @param {String} config.dataFileURL The local file url of the uploaded file. 900 * @param {Object} [config.dataClass] The DataClass the data belongs to. With the following properties: 901 * @param {Integer} [config.dataClass.id] The row id of the DataClass. 902 * @param {String} [config.dataClass.name] The name of the DataClass. 903 * 904 * @example 905 * // To perform a file upload over HTTP: 906 * <form id="upload-run-form" enctype="multipart/form-data" method="POST"> 907 * <div id="upload-run-button"></div> 908 * </form> 909 * <script type="text/javascript"> 910 * LABKEY.Utils.requiresScript("FileUploadField.js"); 911 * // Optional - specify a protocolId so that the Exp.Data object is assigned the related LSID namespace. 912 * var url = LABKEY.ActionURL.buildURL("assay", "assayFileUpload", LABKEY.ActionURL.getContainer(), { protocolId: 50 }); 913 * Ext.onReady(function() { 914 * var form = new Ext.form.BasicForm( 915 * Ext.get("upload-run-form"), { 916 * fileUpload: true, 917 * frame: false, 918 * url: url, 919 * listeners: { 920 * actioncomplete : function (form, action) { 921 * alert('Upload successful!'); 922 * var data = new LABKEY.Exp.Data(action.result); 923 * 924 * // now add the data as a dataInput to a LABKEY.Exp.Run 925 * var run = new LABKEY.Exp.Run(); 926 * run.name = data.name; 927 * run.dataInputs = [ data ]; 928 * 929 * // add the new run to a LABKEY.Exp.Batch object and 930 * // fetch the parsed file contents from the data object 931 * // using the LABKEY.Exp.Data#getContent() method. 932 * }, 933 * actionfailed: function (form, action) { 934 * alert('Upload failed!'); 935 * } 936 * } 937 * }); 938 * 939 * var uploadField = new Ext.form.FileUploadField({ 940 * id: "upload-run-field", 941 * renderTo: "upload-run-button", 942 * buttonText: "Upload Data...", 943 * buttonOnly: true, 944 * buttonCfg: { cls: "labkey-button" }, 945 * listeners: { 946 * "fileselected": function (fb, v) { 947 * form.submit(); 948 * } 949 * } 950 * }); 951 * }); 952 * </script> 953 * 954 * // Or, to upload the contents of a JavaScript string as a file: 955 * <script type="text/javascript"> 956 * Ext.onReady(function() { 957 * LABKEY.Ajax.request({ 958 * url: LABKEY.ActionURL.buildURL("assay", "assayFileUpload"), 959 * params: { fileName: 'test.txt', fileContent: 'Some text!' }, 960 * success: function(response, options) { 961 * var data = new LABKEY.Exp.Data(Ext.util.JSON.decode(response.responseText)); 962 * 963 * // now add the data as a dataInput to a LABKEY.Exp.Run 964 * var run = new LABKEY.Exp.Run(); 965 * run.name = data.name; 966 * run.dataInputs = [ data ]; 967 * 968 * // add the new run to a LABKEY.Exp.Batch object here 969 * } 970 * }); 971 * }); 972 * 973 * </script> 974 */ 975 LABKEY.Exp.Data = function (config) { 976 LABKEY.Exp.ProtocolOutput.call(this, config); 977 config = config || {}; 978 979 this.dataType = config.dataType; 980 this.dataFileURL = config.dataFileURL; 981 this.dataClass = config.dataClass; 982 if (config.pipelinePath) 983 this.pipelinePath = config.pipelinePath; 984 if (config.role) 985 this.role = config.role; 986 }; 987 LABKEY.Exp.Data.prototype = new LABKEY.Exp.ProtocolOutput; 988 LABKEY.Exp.Data.prototype.constructor = LABKEY.Exp.Data; 989 990 /** 991 * Retrieves the contents of the data object from the server. 992 * @param config An object that contains the following configuration parameters 993 * @param {object} [config.scope] A scoping object for the success and error callback functions (default to this). 994 * @param {function} config.success The function to call when the function finishes successfully. 995 * This function will be called with the parameters: 996 * <ul> 997 * <li><b>content</b> The type of the content varies based on the format requested. 998 * <li><b>format</b> The format used in the request 999 * <li><b>response</b> The original response 1000 * </ul> 1001 * @param {function} [config.failure] The function to call if this function encounters an error. 1002 * This function will be called with the following parameters: 1003 * <ul> 1004 * <li><b>errorInfo:</b> An object with a property called "exception," which contains the error message.</li> 1005 * <li><b>format</b> The format used in the request 1006 * <li><b>response</b> The original response 1007 * </ul> 1008 * @param {String} [config.format] How to format the content. Defaults to plaintext, supported for text/* MIME types, 1009 * including .html, .xml, .tsv, .txt, and .csv. Use 'jsonTSV' to get a JSON version of the .xls, .tsv, .or .csv 1010 * files, the structure of which matches the argument to convertToExcel in {@link LABKEY.Utils}. 1011 * <ul> 1012 * <li><b>fileName:</b> the name of the file</li> 1013 * <li><b>sheets:</b> an array of the sheets in the file. Text file types will have a single sheet named 'flat'. 1014 * <ul><li><b>name:</b> the name of the sheet</li> 1015 * <li><b>values:</b> two-dimensional array of all the cells in the worksheet. First array index is row, second is column</li> 1016 * </ul> 1017 * </ul> 1018 * <br/>Use 'jsonTSVExtended' to get include metadata in the 2D array of cells. 1019 * Text file types will not supply additional metadata but populate the 'value' attribute in the map. 1020 * Excel files will include: 1021 * <ul> 1022 * <li><b>value:</b> the string, boolean, date, or number in the cell</li> 1023 * <li><b>timeOnly:</b> whether the date part should be ignored for dates</li> 1024 * <li><b>formatString:</b> the Java format string to be used to render the value for dates and numbers</li> 1025 * <li><b>formattedValue:</b> the formatted string for that value for all value types</li> 1026 * <li><b>error:</b> true if this cell has an error</li> 1027 * <li><b>formula:</b> if the cell's value is specified by a formula, the text of the formula</li> 1028 * </ul> 1029 * <br/>Use 'jsonTSVIgnoreTypes' to always return string values for all cells, regardless of type. 1030 * <br/> 1031 * An example of the results for a request for 'jsonTsv' format: 1032 * <pre> 1033 * { 1034 "sheets": [ 1035 { 1036 "name": "Sheet1", 1037 "data": [ 1038 [ 1039 "StringColumn", 1040 "DateColumn" 1041 ], 1042 [ 1043 "Hello", 1044 "16 May 2009 17:00:00" 1045 ], 1046 [ 1047 "world", 1048 "12/21/2008 08:45AM" 1049 ] 1050 ] 1051 }, 1052 { 1053 "name": "Sheet2", 1054 "data": [ 1055 ["NumberColumn"], 1056 [55.44], 1057 [100.34], 1058 [-1] 1059 ] 1060 }, 1061 { 1062 "name": "Sheet3", 1063 "data": [] 1064 } 1065 ], 1066 "fileName": "SimpleExcelFile.xls" 1067 }</pre> 1068 <br/> 1069 An example of the same file in the 'jsonTSVExtended' format: 1070 <pre> 1071 * { 1072 "sheets": [ 1073 { 1074 "name": "Sheet1", 1075 "data": [ 1076 [ 1077 { 1078 "value": "StringColumn", 1079 "formattedValue": "StringColumn" 1080 }, 1081 { 1082 "value": "DateColumn", 1083 "formattedValue": "DateColumn" 1084 } 1085 ], 1086 [ 1087 { 1088 "value": "Hello", 1089 "formattedValue": "Hello" 1090 }, 1091 { 1092 "formatString": "MMMM d, yyyy", 1093 "value": "16 May 2009 17:00:00", 1094 "timeOnly": false, 1095 "formattedValue": "May 17, 2009" 1096 } 1097 ], 1098 [ 1099 { 1100 "value": "world", 1101 "formattedValue": "world" 1102 }, 1103 { 1104 "formatString": "M/d/yy h:mm a", 1105 "value": "21 Dec 2008 19:31:00", 1106 "timeOnly": false, 1107 "formattedValue": "12/21/08 7:31 PM" 1108 } 1109 ] 1110 ] 1111 }, 1112 { 1113 "name": "Sheet2", 1114 "data": [ 1115 [{ 1116 "value": "NumberColumn", 1117 "formattedValue": "NumberColumn" 1118 }], 1119 [{ 1120 "formatString": "$#,##0.00", 1121 "value": 55.44, 1122 "formattedValue": "$55.44" 1123 }], 1124 [{ 1125 "value": 100.34, 1126 "formattedValue": "100.34" 1127 }], 1128 [{ 1129 "value": -1, 1130 "formattedValue": "-1" 1131 }] 1132 ] 1133 }, 1134 { 1135 "name": "Sheet3", 1136 "data": [] 1137 } 1138 ], 1139 "fileName": "SimpleExcelFile.xls" 1140 } 1141 </pre> 1142 * 1143 */ 1144 LABKEY.Exp.Data.prototype.getContent = function(config) 1145 { 1146 if(!LABKEY.Utils.getOnSuccess(config)) 1147 { 1148 alert("You must specify a callback function in config.success when calling LABKEY.Exp.Data.getContent()!"); 1149 return; 1150 } 1151 1152 function getSuccessCallbackWrapper(fn, format, scope) 1153 { 1154 return function(response) 1155 { 1156 //ensure response is JSON before trying to decode 1157 var content = null; 1158 if(response && response.getResponseHeader && response.getResponseHeader('Content-Type') 1159 && response.getResponseHeader('Content-Type').indexOf('application/json') >= 0) 1160 { 1161 content = LABKEY.Utils.decode(response.responseText); 1162 } 1163 else 1164 { 1165 content = response.responseText; 1166 } 1167 1168 if(fn) 1169 fn.call(scope || this, content, format, response); 1170 }; 1171 } 1172 1173 LABKEY.Ajax.request( 1174 { 1175 url : LABKEY.ActionURL.buildURL("experiment", "showFile"), 1176 method : 'GET', 1177 params : { rowId : this.id, format: config.format }, 1178 success: getSuccessCallbackWrapper(LABKEY.Utils.getOnSuccess(config), config.format, config.scope), 1179 failure: LABKEY.Utils.getCallbackWrapper(LABKEY.Utils.getOnFailure(config), config.scope, true) 1180 }); 1181 1182 }; 1183 1184 1185