diff --git a/client/.babelrc b/client/.babelrc index d0f4b953ce4c..21f360d74a17 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -1,6 +1,7 @@ { "presets": ["env"], "moduleIds": true, + "plugins": ["transform-vue-template"], "ignore": [ "i18n.js", "utils/localization.js", diff --git a/client/galaxy/scripts/components/RuleCollectionBuilder.vue b/client/galaxy/scripts/components/RuleCollectionBuilder.vue new file mode 100644 index 000000000000..990db904c81f --- /dev/null +++ b/client/galaxy/scripts/components/RuleCollectionBuilder.vue @@ -0,0 +1,2217 @@ + + + + diff --git a/client/galaxy/scripts/mvc/collection/list-collection-creator.js b/client/galaxy/scripts/mvc/collection/list-collection-creator.js index 5a0072d0503c..9cb7212d0b56 100644 --- a/client/galaxy/scripts/mvc/collection/list-collection-creator.js +++ b/client/galaxy/scripts/mvc/collection/list-collection-creator.js @@ -5,6 +5,9 @@ import baseCreator from "mvc/collection/base-creator"; import UI_MODAL from "mvc/ui/ui-modal"; import naturalSort from "utils/natural-sort"; import _l from "utils/localization"; +import RuleCollectionBuilder from "components/RuleCollectionBuilder.vue"; +import Vue from "vue"; + import "ui/hoverhighlight"; var logNamespace = "collections"; @@ -1011,17 +1014,11 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin) } }); -//============================================================================= -/** Create a modal and load its body with the given CreatorClass creator type - * @returns {Deferred} resolved when creator has built a collection. - */ -var collectionCreatorModal = function _collectionCreatorModal(elements, options, CreatorClass) { - var deferred = jQuery.Deferred(); - var modal = Galaxy.modal || new UI_MODAL.View(); - var creator; +const collectionCreatorModalSetup = function _collectionCreatorModalSetup(options) { + const deferred = jQuery.Deferred(); + const modal = Galaxy.modal || new UI_MODAL.View(); - options = _.defaults(options || {}, { - elements: elements, + const creatorOptions = _.defaults(options || {}, { oncancel: function() { modal.hide(); deferred.reject("cancelled"); @@ -1032,18 +1029,67 @@ var collectionCreatorModal = function _collectionCreatorModal(elements, options, } }); - creator = new CreatorClass(options); - modal.show({ - title: options.title || _l("Create a collection"), - body: creator.$el, - width: "80%", - height: "100%", - closing_events: true + const showEl = function(el) { + modal.show({ + title: options.title || _l("Create a collection"), + body: el, + width: "80%", + height: "100%", + closing_events: true + }); + }; + + return { deferred, creatorOptions, showEl }; +}; + +//============================================================================= +/** Create a modal and load its body with the given CreatorClass creator type + * @returns {Deferred} resolved when creator has built a collection. + */ +var collectionCreatorModal = function _collectionCreatorModal(elements, options, CreatorClass) { + options = _.defaults(options || {}, { + elements: elements }); + const { deferred, creatorOptions, showEl } = collectionCreatorModalSetup(options); + var creator = new CreatorClass(creatorOptions); + showEl(creator.$el); creator.render(); - window._collectionCreator = creator; + return deferred; +}; - //TODO: remove modal header +var ruleBasedCollectionCreatorModal = function _ruleBasedCollectionCreatorModal( + elements, + elementsType, + importType, + options +) { + let title; + if (importType == "datasets") { + title = _l("Build Rules for Uploading Datasets"); + } else if (elementsType == "datasets") { + title = _l("Build Rules for Creating Collection"); + } else { + title = _l("Build Rules for Uploading Collections"); + } + options = _.defaults(options || {}, { + title: title + }); + const { deferred, creatorOptions, showEl } = collectionCreatorModalSetup(options); + var ruleCollectionBuilderInstance = Vue.extend(RuleCollectionBuilder); + var vm = document.createElement("div"); + showEl(vm); + new ruleCollectionBuilderInstance({ + propsData: { + initialElements: elements, + elementsType: elementsType, + importType: importType, + ftpUploadSite: options.ftpUploadSite, + creationFn: options.creationFn, + oncancel: options.oncancel, + oncreate: options.oncreate, + defaultHideSourceItems: options.defaultHideSourceItems + } + }).$mount(vm); return deferred; }; @@ -1059,15 +1105,14 @@ var listCollectionCreatorModal = function _listCollectionCreatorModal(elements, * @returns {Deferred} resolved when the collection is added to the history. */ function createListCollection(contents, defaultHideSourceItems) { - var elements = contents.toJSON(); + const elements = contents.toJSON(); - var promise = listCollectionCreatorModal(elements, { + const promise = listCollectionCreatorModal(elements, { defaultHideSourceItems: defaultHideSourceItems, creationFn: function(elements, name, hideSourceItems) { elements = elements.map(element => ({ id: element.id, name: element.name, - //TODO: this allows for list:list even if the filter above does not - reconcile src: element.history_content_type === "dataset" ? "hda" : "hdca" })); @@ -1078,12 +1123,50 @@ function createListCollection(contents, defaultHideSourceItems) { return promise; } +function createCollectionViaRules(selection, defaultHideSourceItems) { + let elements, elementsType, importType; + if (!selection.selectionType) { + // Have HDAs from the history panel. + elements = selection.toJSON(); + elementsType = "datasets"; + importType = "collections"; + } else { + const hasNonWhitespaceChars = RegExp(/[^\s]/); + // Have pasted data, data from a history dataset, or FTP list. + const lines = selection.content + .split(/[\n\r]/) + .filter(line => line.length > 0 && hasNonWhitespaceChars.exec(line)); + + // Really poor tabular parser - we should get a library for this or expose options? I'm not + // sure. + let hasTabs = false; + if (lines.length > 0) { + const firstLine = lines[0]; + if (firstLine.indexOf("\t") >= 0) { + hasTabs = true; + } + } + const regex = hasTabs ? /\t/ : /\s+/; + elements = lines.map(line => line.split(regex)); + elementsType = selection.selectionType; + importType = selection.dataType || "collections"; + } + const promise = ruleBasedCollectionCreatorModal(elements, elementsType, importType, { + ftpUploadSite: selection.ftpUploadSite, + defaultHideSourceItems: defaultHideSourceItems, + creationFn: function(elements, collectionType, name, hideSourceItems) { + return selection.createHDCA(elements, collectionType, name, hideSourceItems); + } + }); + return promise; +} + //============================================================================== export default { DatasetCollectionElementView: DatasetCollectionElementView, ListCollectionCreator: ListCollectionCreator, - collectionCreatorModal: collectionCreatorModal, listCollectionCreatorModal: listCollectionCreatorModal, - createListCollection: createListCollection + createListCollection: createListCollection, + createCollectionViaRules: createCollectionViaRules }; diff --git a/client/galaxy/scripts/mvc/history/history-view-edit.js b/client/galaxy/scripts/mvc/history/history-view-edit.js index f2cd6648d394..76ee95512c07 100644 --- a/client/galaxy/scripts/mvc/history/history-view-edit.js +++ b/client/galaxy/scripts/mvc/history/history-view-edit.js @@ -294,22 +294,20 @@ var HistoryViewEdit = _super.extend( return [ { html: _l("Build Dataset List"), - func: function() { - panel.buildCollection("list"); - } + func: () => panel.buildCollection("list") }, // TODO: Only show quick pair if two things selected. { html: _l("Build Dataset Pair"), - func: function() { - panel.buildCollection("paired"); - } + func: () => panel.buildCollection("paired") }, { html: _l("Build List of Dataset Pairs"), - func: function() { - panel.buildCollection("list:paired"); - } + func: () => panel.buildCollection("list:paired") + }, + { + html: _l("Build Collection from Rules"), + func: () => panel.buildCollection("rules") } ]; }, @@ -325,6 +323,8 @@ var HistoryViewEdit = _super.extend( createFunc = PAIR_COLLECTION_CREATOR.createPairCollection; } else if (collectionType == "list:paired") { createFunc = LIST_OF_PAIRS_COLLECTION_CREATOR.createListOfPairsCollection; + } else if (collectionType.startsWith("rules")) { + createFunc = LIST_COLLECTION_CREATOR.createCollectionViaRules; } else { console.warn(`Unknown collectionType encountered ${collectionType}`); } diff --git a/client/galaxy/scripts/mvc/history/job-states-model.js b/client/galaxy/scripts/mvc/history/job-states-model.js index fffee020ead1..eb0229273f79 100644 --- a/client/galaxy/scripts/mvc/history/job-states-model.js +++ b/client/galaxy/scripts/mvc/history/job-states-model.js @@ -165,4 +165,4 @@ var JobStatesSummaryCollection = Backbone.Collection.extend({ } }); -export default { JobStatesSummary, JobStatesSummaryCollection, FETCH_STATE_ON_ADD }; +export default { JobStatesSummary, JobStatesSummaryCollection, FETCH_STATE_ON_ADD, NON_TERMINAL_STATES, ERROR_STATES }; diff --git a/client/galaxy/scripts/mvc/upload/collection/rules-input-view.js b/client/galaxy/scripts/mvc/upload/collection/rules-input-view.js new file mode 100644 index 000000000000..c79221ffc040 --- /dev/null +++ b/client/galaxy/scripts/mvc/upload/collection/rules-input-view.js @@ -0,0 +1,177 @@ +import _l from "utils/localization"; +import Ui from "mvc/ui/ui-misc"; +import Select from "mvc/ui/ui-select"; +import UploadUtils from "mvc/upload/upload-utils"; +import axios from "axios"; + +export default Backbone.View.extend({ + initialize: function(app) { + this.app = app; + this.options = app.options; + this.ftpUploadSite = app.currentFtp(); + this.setElement(this._template()); + this.btnBuild = new Ui.Button({ + id: "btn-build", + title: _l("Build"), + onclick: () => { + this._eventBuild(); + } + }); + _.each([this.btnBuild], button => { + this.$(".upload-buttons").prepend(button.$el); + }); + const dataTypeOptions = [{ id: "datasets", text: "Datasets" }, { id: "collections", text: "Collection(s)" }]; + this.dataType = "datasets"; + this.dataTypeView = new Select.View({ + css: "upload-footer-selection", + container: this.$(".rule-data-type"), + data: dataTypeOptions, + value: this.dataType, + onchange: value => { + this.dataType = value; + // this._renderSelectedType(); + } + }); + + const selectionTypeOptions = [ + { id: "paste", text: "Pasted Table" }, + { id: "dataset", text: "History Dataset" }, + ]; + if (this.ftpUploadSite) { + selectionTypeOptions.push({ id: "ftp", text: "FTP Directory" }); + } + this.selectionType = "paste"; + this.selectionTypeView = new Select.View({ + css: "upload-footer-selection", + container: this.$(".rule-select-type"), + data: selectionTypeOptions, + value: this.selectionType, + onchange: value => { + this.selectionType = value; + this._renderSelectedType(); + } + }); + this.selectedDatasetId = null; + + this._renderSelectedType(); + }, + + _renderSelectedType: function() { + const selectionType = this.selectionType; + if (selectionType == "dataset") { + if (!this.datasetSelectorView) { + this.selectedDatasetId = null; + const history = parent.Galaxy && parent.Galaxy.currHistoryPanel && parent.Galaxy.currHistoryPanel.model; + const historyContentModels = history.contents.models; + const options = []; + for (let historyContentModel of historyContentModels) { + const attr = historyContentModel.attributes; + if (attr.history_content_type !== "dataset") { + continue; + } + options.push({ id: attr.id, text: `${attr.hid}: ${_.escape(attr.name)}` }); + } + this.datasetSelectorView = new Select.View({ + container: this.$(".dataset-selector"), + data: options, + placeholder: _l("Select a dataset"), + onchange: val => { + this._onDataset(val); + } + }); + } else { + this.datasetSelectorView.value(null); + } + } else if (selectionType == "ftp") { + UploadUtils.getRemoteFiles(ftp_files => { + this._setPreview(ftp_files.map(file => file["path"]).join("\n")); + }); + } + this._updateScreen(); + }, + + _onDataset: function(selectedDatasetId) { + this.selectedDatasetId = selectedDatasetId; + if (!selectedDatasetId) { + this._setPreview(""); + return; + } + axios + .get( + `${Galaxy.root}api/histories/${Galaxy.currHistoryPanel.model.id}/contents/${selectedDatasetId}/display` + ) + .then(response => { + this._setPreview(response.data); + }) + .catch(error => console.log(error)); + }, + + _eventBuild: function() { + const selection = this.$(".upload-rule-source-content").val(); + this._buildSelection(selection); + }, + + _buildSelection: function(content) { + const selectionType = this.selectionType; + const selection = { content: content }; + if (selectionType == "dataset" || selectionType == "paste") { + selection.selectionType = "raw"; + } else if (selectionType == "ftp") { + selection.selectionType = "ftp"; + } + selection.ftpUploadSite = this.ftpUploadSite; + selection.dataType = this.dataType; + Galaxy.currHistoryPanel.buildCollection("rules", selection, true); + this.app.modal.hide(); + }, + + _setPreview: function(content) { + $(".upload-rule-source-content").val(content); + this._updateScreen(); + }, + + _updateScreen: function() { + const selectionType = this.selectionType; + const selection = this.$(".upload-rule-source-content").val(); + this.btnBuild[selection || selectionType == "paste" ? "enable" : "disable"](); + this.$("#upload-rule-dataset-option")[selectionType == "dataset" ? "show" : "hide"](); + this.$(".upload-rule-source-content").attr("disabled", selectionType !== "paste"); + }, + + _template: function() { + return ` +
+
+
+ Tabular source data to extract collection files and metadata from +
+
+
+ +
+
${_l("Upload data as")}:
+
+
+
+
${_l("Load tabular data from")}:
+
+
+
+
${_l("Select dataset to load")}:
+
+
+ + + + +
+
+ +
+
+ `; + } +}); diff --git a/client/galaxy/scripts/mvc/upload/upload-ftp.js b/client/galaxy/scripts/mvc/upload/upload-ftp.js index 1db7bbfcaba9..124aa4f85702 100644 --- a/client/galaxy/scripts/mvc/upload/upload-ftp.js +++ b/client/galaxy/scripts/mvc/upload/upload-ftp.js @@ -1,5 +1,7 @@ /** This renders the content of the ftp popup **/ import Utils from "utils/utils"; +import UploadUtils from "mvc/upload/upload-utils"; + export default Backbone.View.extend({ initialize: function(options) { var self = this; @@ -36,18 +38,16 @@ export default Backbone.View.extend({ this.$content.hide(); this.$warning.hide(); this.$help.hide(); - $.ajax({ - url: `${Galaxy.root}api/remote_files`, - method: "GET", - success: function(ftp_files) { + UploadUtils.getRemoteFiles( + function(ftp_files) { self.model.set("ftp_files", ftp_files); self._index(); self._renderTable(); }, - error: function() { + function() { self._renderTable(); } - }); + ); }, /** Fill table with ftp entries */ diff --git a/client/galaxy/scripts/mvc/upload/upload-utils.js b/client/galaxy/scripts/mvc/upload/upload-utils.js new file mode 100644 index 000000000000..58e93884adc6 --- /dev/null +++ b/client/galaxy/scripts/mvc/upload/upload-utils.js @@ -0,0 +1,81 @@ +import Utils from "utils/utils"; + +const AUTO_EXTENSION = { + id: "auto", + text: "Auto-detect", + description: + "This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed." +}; +const DEFAULT_GENOME = "?"; +const DEFAULT_EXTENSION = "auto"; + +function getUploadDatatypes(callback, datatypesDisableAuto, auto) { + Utils.get({ + url: `${Galaxy.root}api/datatypes?extension_only=False`, + success: function(datatypes) { + const listExtensions = []; + for (var key in datatypes) { + listExtensions.push({ + id: datatypes[key].extension, + text: datatypes[key].extension, + description: datatypes[key].description, + description_url: datatypes[key].description_url, + composite_files: datatypes[key].composite_files + }); + } + listExtensions.sort((a, b) => { + var a_text = a.text && a.text.toLowerCase(); + var b_text = b.text && b.text.toLowerCase(); + return a_text > b_text ? 1 : a_text < b_text ? -1 : 0; + }); + if (!datatypesDisableAuto) { + listExtensions.unshift(auto); + } + callback(listExtensions); + } + }); +} + +function getUploadGenomes(callback, defaultGenome) { + Utils.get({ + url: `${Galaxy.root}api/genomes`, + success: function(genomes) { + const listGenomes = []; + + for (var key in genomes) { + listGenomes.push({ + id: genomes[key][1], + text: genomes[key][0] + }); + } + listGenomes.sort((a, b) => { + if (a.id == defaultGenome) { + return -1; + } + if (b.id == defaultGenome) { + return 1; + } + return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; + }); + callback(listGenomes); + } + }); +} + +function getRemoteFiles(success, error) { + return $.ajax({ + url: `${Galaxy.root}api/remote_files`, + method: "GET", + success: success, + error: error + }); +} + +export default { + AUTO_EXTENSION, + DEFAULT_GENOME, + DEFAULT_EXTENSION, + getRemoteFiles, + getUploadDatatypes, + getUploadGenomes +}; diff --git a/client/galaxy/scripts/mvc/upload/upload-view.js b/client/galaxy/scripts/mvc/upload/upload-view.js index ec9aaaeacd25..9741c7618e0e 100644 --- a/client/galaxy/scripts/mvc/upload/upload-view.js +++ b/client/galaxy/scripts/mvc/upload/upload-view.js @@ -3,23 +3,21 @@ import _l from "utils/localization"; import Utils from "utils/utils"; import Modal from "mvc/ui/ui-modal"; import Tabs from "mvc/ui/ui-tabs"; +import UploadUtils from "mvc/upload/upload-utils"; import UploadButton from "mvc/upload/upload-button"; import UploadViewDefault from "mvc/upload/default/default-view"; import UploadViewComposite from "mvc/upload/composite/composite-view"; import UploadViewCollection from "mvc/upload/collection/collection-view"; +import UploadViewRuleBased from "mvc/upload/collection/rules-input-view"; + export default Backbone.View.extend({ options: { ftp_upload_site: "n/a", - default_genome: "?", - default_extension: "auto", + default_genome: UploadUtils.DEFAULT_GENOME, + default_extension: UploadUtils.DEFAULT_EXTENSION, height: 500, width: 900, - auto: { - id: "auto", - text: "Auto-detect", - description: - "This system will try to detect the file type automatically. If your file is not detected properly as one of the known formats, it most likely means that it has some format problems (e.g., different number of columns on different rows). You can still coerce the system to set your data to the format you think it should be. You can also upload compressed files, which will automatically be decompressed." - } + auto: UploadUtils.AUTO_EXTENSION }, // contains all available dataset extensions/types @@ -29,17 +27,16 @@ export default Backbone.View.extend({ list_genomes: [], initialize: function(options) { - var self = this; this.options = Utils.merge(options, this.options); // create view for upload/progress button this.ui_button = new UploadButton.View({ - onclick: function(e) { + onclick: e => { e.preventDefault(); - self.show(); + this.show(); }, - onunload: function() { - var percentage = self.ui_button.model.get("percentage", 0); + onunload: () => { + var percentage = this.ui_button.model.get("percentage", 0); if (percentage > 0 && percentage < 100) { return "Several uploads are queued."; } @@ -50,51 +47,18 @@ export default Backbone.View.extend({ this.setElement(this.ui_button.$el); // load extensions - var self = this; - Utils.get({ - url: `${Galaxy.root}api/datatypes?extension_only=False`, - success: function(datatypes) { - for (var key in datatypes) { - self.list_extensions.push({ - id: datatypes[key].extension, - text: datatypes[key].extension, - description: datatypes[key].description, - description_url: datatypes[key].description_url, - composite_files: datatypes[key].composite_files - }); - } - self.list_extensions.sort((a, b) => { - var a_text = a.text && a.text.toLowerCase(); - var b_text = b.text && b.text.toLowerCase(); - return a_text > b_text ? 1 : a_text < b_text ? -1 : 0; - }); - if (!self.options.datatypes_disable_auto) { - self.list_extensions.unshift(self.options.auto); - } - } - }); + UploadUtils.getUploadDatatypes( + list_extensions => { + this.list_extensions = list_extensions; + }, + this.options.datatypes_disable_auto, + this.options.auto + ); // load genomes - Utils.get({ - url: `${Galaxy.root}api/genomes`, - success: function(genomes) { - for (var key in genomes) { - self.list_genomes.push({ - id: genomes[key][1], - text: genomes[key][0] - }); - } - self.list_genomes.sort((a, b) => { - if (a.id == self.options.default_genome) { - return -1; - } - if (b.id == self.options.default_genome) { - return 1; - } - return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; - }); - } - }); + UploadUtils.getUploadGenomes(list_genomes => { + this.list_genomes = list_genomes; + }, this.default_genome); }, /** Show/hide upload dialog */ @@ -127,6 +91,12 @@ export default Backbone.View.extend({ title: _l("Collection"), $el: this.collection_view.$el }); + this.rule_based_view = new UploadViewRuleBased(this); + this.tabs.add({ + id: "rule-based", + title: _l("Rule-based"), + $el: this.rule_based_view.$el + }); this.modal = new Modal.View({ title: _l("Download from web or upload from disk"), body: this.tabs.$el, diff --git a/client/galaxy/style/less/upload.less b/client/galaxy/style/less/upload.less index 93cc6cb463e0..45926f8de976 100644 --- a/client/galaxy/style/less/upload.less +++ b/client/galaxy/style/less/upload.less @@ -160,6 +160,14 @@ } } } + .upload-rule-option { + padding-top: 20px; + + .upload-rule-option-title { + font-weight: bold; + padding-bottom: 5px; + } + } .upload-box-solid { border: 1px solid @btn-default-border; } diff --git a/client/package.json b/client/package.json index 7c51eb18ff7a..13e76ad9795f 100644 --- a/client/package.json +++ b/client/package.json @@ -28,7 +28,8 @@ "raven-js": "^3.17.0", "requirejs": "2", "underscore": "^1.8.3", - "vue": "^2.5.9" + "vue": "^2.5.9", + "vue-handsontable-official": "^1.0.0" }, "scripts": { "watch": "gulp stage-libs && concurrently \"yarn run webpack-watch\" \"yarn run gulp watch\" \"yarn run style-watch\"", @@ -54,6 +55,7 @@ "devDependencies": { "babel-core": "^6.24.0", "babel-loader": "^7.1.1", + "babel-plugin-transform-vue-template": "^0.3.1", "babel-preset-env": "^1.6.1", "concurrently": "^3.5.1", "css-loader": "^0.28.7", @@ -90,7 +92,7 @@ "qunitjs": "^2.4.1", "sinon": "^4.1.2", "vue-loader": "^13.5.0", - "vue-template-compiler": "^2.5.9", + "vue-template-compiler": "^2.5.13", "webpack": "^3.10.0" } } diff --git a/client/webpack.config.js b/client/webpack.config.js index 45d94b0f3813..c59bb58538fd 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -61,9 +61,18 @@ let buildconfig = { }, module: { rules: [ + { + test: /\.vue$/, + loader: "vue-loader", + options: { + loaders: { + js: "babel-loader" + } + } + }, { test: /\.js$/, - exclude: [/(node_modules|bower_components)/, libsBase], + exclude: [/(node_modules\/(?!(vue-handsontable-official)\/)|bower_components)/, libsBase], loader: "babel-loader" }, { @@ -78,10 +87,6 @@ let buildconfig = { options: "$" } ] - }, - { - test: /\.vue$/, - loader: "vue-loader" } ] }, diff --git a/client/yarn.lock b/client/yarn.lock index 26bb2146e24b..46ea1084ed04 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -887,6 +887,13 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" +babel-plugin-transform-vue-template@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-template/-/babel-plugin-transform-vue-template-0.3.1.tgz#9681fa2fae40c0248e52852b7aebd51ce6a5ac2f" + dependencies: + babylon "^6.17.4" + vue-template-es2015-compiler "^1.5.3" + babel-preset-env@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48" @@ -1022,7 +1029,7 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0, babel-types@^6.7. lodash "^4.17.4" to-fast-properties "^1.0.3" -babylon@^6.1.0, babylon@^6.18.0: +babylon@^6.1.0, babylon@^6.17.4, babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -3432,6 +3439,14 @@ handlebars@~3.0.0: optionalDependencies: uglify-js "~2.3" +handsontable@^0.33.0: + version "0.33.0" + resolved "https://registry.yarnpkg.com/handsontable/-/handsontable-0.33.0.tgz#3df41804fd00a878b3904a478bf7ef54f2bc6d95" + dependencies: + moment "2.18.1" + numbro "1.11.0" + pikaday "1.5.1" + har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -4824,6 +4839,14 @@ mocha@^3.2.0: mkdirp "0.5.1" supports-color "3.1.2" +moment@2.18.1: + version "2.18.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" + +moment@2.x: + version "2.20.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -5068,6 +5091,10 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" +numbro@1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/numbro/-/numbro-1.11.0.tgz#39aa17b358b4682aec8ca0d5755f35c5d9ce8f9e" + oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -5451,6 +5478,12 @@ pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" +pikaday@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pikaday/-/pikaday-1.5.1.tgz#0a48549bc1a14ea1d08c44074d761bc2f2bfcfd3" + optionalDependencies: + moment "2.x" + pinkie-promise@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-1.0.0.tgz#d1da67f5482563bb7cf57f286ae2822ecfbf3670" @@ -7470,13 +7503,19 @@ void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" +vue-handsontable-official@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vue-handsontable-official/-/vue-handsontable-official-1.0.0.tgz#d943c192a3a7725153186c682d6aef9e2955bff2" + dependencies: + handsontable "^0.33.0" + vue-hot-reload-api@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.3.tgz#43c8e5506d65a271d2571936d77253019fd3eb17" vue-loader@^13.5.0: - version "13.5.0" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.5.0.tgz#52f7b3790a267eff80012b77ea187a54586dd5d4" + version "13.7.0" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.7.0.tgz#4d6a35b169c2a0a488842fb95c85052105fa9729" dependencies: consolidate "^0.14.0" hash-sum "^1.0.2" @@ -7499,20 +7538,20 @@ vue-style-loader@^3.0.0: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-template-compiler@^2.5.9: - version "2.5.9" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.9.tgz#7fabc73c8d3d12d32340cd86c5fc33e00e86d686" +vue-template-compiler@^2.5.13: + version "2.5.13" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.13.tgz#12a2aa0ecd6158ac5e5f14d294b0993f399c3d38" dependencies: de-indent "^1.0.2" he "^1.1.0" -vue-template-es2015-compiler@^1.6.0: +vue-template-es2015-compiler@^1.5.3, vue-template-es2015-compiler@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18" vue@^2.5.9: - version "2.5.9" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.9.tgz#b2380cd040915dca69881dafd121d760952e65f7" + version "2.5.13" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1" walk-sync@0.3.1: version "0.3.1" diff --git a/lib/galaxy/webapps/galaxy/api/jobs.py b/lib/galaxy/webapps/galaxy/api/jobs.py index 3015349cbe81..31b350312cdb 100644 --- a/lib/galaxy/webapps/galaxy/api/jobs.py +++ b/lib/galaxy/webapps/galaxy/api/jobs.py @@ -111,7 +111,7 @@ def build_and_apply_filters(query, objects, filter_func): return out - @expose_api + @expose_api_anonymous def show(self, trans, id, **kwd): """ show( trans, id ) @@ -255,7 +255,9 @@ def __get_job(self, trans, id): job = trans.sa_session.query(trans.app.model.Job).filter(trans.app.model.Job.id == decoded_job_id).first() if job is None: raise exceptions.ObjectNotFound() - if not trans.user_is_admin() and job.user != trans.user: + belongs_to_user = (job.user == trans.user) if job.user else (job.session_id == trans.get_galaxy_session().id) + if not trans.user_is_admin() and not belongs_to_user: + # Check access granted via output datasets. if not job.output_datasets: raise exceptions.ItemAccessibilityException("Job has no output datasets.") for data_assoc in job.output_datasets: diff --git a/test-data/rules/PRJDA60709.tsv b/test-data/rules/PRJDA60709.tsv new file mode 100644 index 000000000000..f50d7d078ccd --- /dev/null +++ b/test-data/rules/PRJDA60709.tsv @@ -0,0 +1,7 @@ +study_accession sample_accession experiment_accession fastq_ftp +PRJDA60709 SAMD00016379 DRX000475 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000770/DRR000770.fastq.gz +PRJDA60709 SAMD00016383 DRX000476 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000771/DRR000771.fastq.gz +PRJDA60709 SAMD00016380 DRX000477 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000772/DRR000772.fastq.gz +PRJDA60709 SAMD00016378 DRX000478 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000773/DRR000773.fastq.gz +PRJDA60709 SAMD00016381 DRX000479 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000774/DRR000774.fastq.gz +PRJDA60709 SAMD00016382 DRX000480 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000775/DRR000775.fastq.gz diff --git a/test-data/rules/PRJDB3920.tsv b/test-data/rules/PRJDB3920.tsv new file mode 100644 index 000000000000..04524d381f65 --- /dev/null +++ b/test-data/rules/PRJDB3920.tsv @@ -0,0 +1,8 @@ +study_accession sample_accession experiment_accession fastq_ftp +PRJDB3920 SAMD00034150 DRX036147 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039919/DRR039919_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039919/DRR039919_2.fastq.gz +PRJDB3920 SAMD00034150 DRX036148 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039920/DRR039920_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039920/DRR039920_2.fastq.gz +PRJDB3920 SAMD00034150 DRX036149 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039921/DRR039921_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039921/DRR039921_2.fastq.gz +PRJDB3920 SAMD00034150 DRX036150 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039922/DRR039922_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039922/DRR039922_2.fastq.gz +PRJDB3920 SAMD00034150 DRX036151 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039923/DRR039923_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039923/DRR039923_2.fastq.gz +PRJDB3920 SAMD00034153 DRX036152 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039924/DRR039924_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039924/DRR039924_2.fastq.gz +PRJDB3920 SAMD00034152 DRX036164 ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039936/DRR039936_1.fastq.gz;ftp.sra.ebi.ac.uk/vol1/fastq/DRR039/DRR039936/DRR039936_2.fastq.gz diff --git a/test-data/rules/PRJNA355367.tsv b/test-data/rules/PRJNA355367.tsv new file mode 100644 index 000000000000..f164437d9c9d --- /dev/null +++ b/test-data/rules/PRJNA355367.tsv @@ -0,0 +1,84 @@ +Run ReleaseDate LoadDate spots bases spots_with_mates avgLength size_MB AssemblyName download_path Experiment LibraryName +SRR5363633 2017-03-21 11:40:07 2017-03-21 11:29:13 9942851 1993124486 9791175 200 809 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363633 SRX2659078 wtPA14 +SRR5363634 2017-03-21 13:19:08 2017-03-21 13:17:11 1158307 232298840 1141697 200 81 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363634 SRX2659079 con10 +SRR5363635 2017-03-21 13:19:08 2017-03-21 13:16:39 1270067 254490549 1249658 200 89 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363635 SRX2659080 con9 +SRR5363636 2017-03-21 13:19:08 2017-03-21 13:16:56 1023725 205709452 1013013 200 74 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363636 SRX2659081 con8 +SRR5363637 2017-03-21 11:37:06 2017-03-21 11:28:00 3179821 638410351 3141113 200 216 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363637 SRX2659082 con7 +SRR5363638 2017-03-21 12:53:08 2017-03-21 12:04:58 3359498 675138344 3325080 200 288 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363638 SRX2659083 con6 +SRR5363639 2017-03-21 13:19:08 2017-03-21 13:17:25 2074389 416191247 2046343 200 145 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363639 SRX2659084 con5 +SRR5363640 2017-03-21 13:19:08 2017-03-21 13:17:46 2692438 540402576 2658118 200 187 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363640 SRX2659085 con4 +SRR5363641 2017-03-21 13:19:08 2017-03-21 13:17:14 2271913 456015568 2243122 200 161 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363641 SRX2659086 con3 +SRR5363642 2017-03-21 13:19:08 2017-03-21 13:16:58 2164949 434399488 2136063 200 151 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363642 SRX2659087 con2 +SRR5363643 2017-03-21 11:37:06 2017-03-21 11:27:34 1999861 401313700 1973567 200 140 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363643 SRX2659088 con1 +SRR5363644 2017-03-21 11:44:06 2017-03-21 11:36:06 13473203 2704705108 13306228 200 1076 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363644 SRX2659089 str10 +SRR5363645 2017-03-21 11:37:06 2017-03-21 11:27:24 1801962 362038363 1782599 200 130 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363645 SRX2659090 str9 +SRR5363646 2017-03-21 11:36:06 2017-03-21 11:26:22 39113 7861711 39106 200 4 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363646 SRX2659091 str8 +SRR5363647 2017-03-21 11:36:06 2017-03-21 11:26:43 1255569 252289127 1242359 200 90 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363647 SRX2659092 str7 +SRR5363648 2017-03-21 11:36:06 2017-03-21 11:26:42 903374 181362535 892308 200 64 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363648 SRX2659093 str6 +SRR5363649 2017-03-21 11:37:06 2017-03-21 11:27:05 2059145 413598822 2035919 200 140 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363649 SRX2659094 str5 +SRR5363650 2017-03-21 11:37:06 2017-03-21 11:27:37 1685689 338152407 1662375 200 119 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363650 SRX2659095 str4 +SRR5363651 2017-03-21 11:36:06 2017-03-21 11:26:51 698470 140689157 694502 201 48 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363651 SRX2659096 str3 +SRR5363652 2017-03-21 11:37:06 2017-03-21 11:27:53 755393 152119594 750753 201 52 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363652 SRX2659097 str2 +SRR5363653 2017-03-21 11:37:06 2017-03-21 11:27:41 1651613 331529685 1630882 200 119 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363653 SRX2659098 str1 +SRR5363654 2017-03-21 11:37:06 2017-03-21 11:27:37 1693765 340003252 1672625 200 118 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363654 SRX2659099 gen10 +SRR5363655 2017-03-21 11:37:06 2017-03-21 11:28:30 2173449 436124391 2144642 200 153 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363655 SRX2659100 gen9 +SRR5363656 2017-03-21 11:43:06 2017-03-21 11:33:05 9625215 1933561588 9519090 200 778 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363656 SRX2659101 gen8 +SRR5363657 2017-03-21 11:37:06 2017-03-21 11:27:38 1106350 222126014 1092932 200 79 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363657 SRX2659102 gen7 +SRR5363658 2017-03-21 11:37:07 2017-03-21 11:27:07 565475 113765064 560920 201 42 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363658 SRX2659103 gen6 +SRR5363659 2017-03-21 11:37:07 2017-03-21 11:28:20 1320926 265553033 1308329 201 96 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363659 SRX2659104 gen5 +SRR5363660 2017-03-21 11:37:07 2017-03-21 11:28:18 949047 190786413 939940 201 70 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363660 SRX2659105 gen4 +SRR5363661 2017-03-21 11:39:07 2017-03-21 11:29:11 1609659 323469152 1593027 200 116 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363661 SRX2659106 gen3 +SRR5363662 2017-03-21 11:37:07 2017-03-21 11:28:20 1244105 249942443 1230587 200 90 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363662 SRX2659107 gen2 +SRR5363663 2017-03-21 11:44:06 2017-03-21 11:34:00 13546763 2714292352 13327595 200 1112 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363663 SRX2659108 gen1 +SRR5363664 2017-03-21 11:37:07 2017-03-21 11:28:35 1995417 400617217 1971115 200 140 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363664 SRX2659109 pit9 +SRR5363665 2017-03-21 11:39:07 2017-03-21 11:29:18 2098280 421450493 2074524 200 146 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363665 SRX2659110 pit10 +SRR5363666 2017-03-21 11:39:07 2017-03-21 11:28:56 1624164 326200210 1605561 200 115 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363666 SRX2659111 pit8 +SRR5363667 2017-03-21 11:39:07 2017-03-21 11:28:56 1765896 355178220 1750742 201 122 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363667 SRX2659112 pit7 +SRR5363668 2017-03-21 11:39:07 2017-03-21 11:29:36 834144 167643900 825708 200 59 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363668 SRX2659113 pit6 +SRR5363669 2017-03-21 11:42:06 2017-03-21 11:31:07 9524989 1913263017 9418320 200 780 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363669 SRX2659114 pit5 +SRR5363670 2017-03-21 11:39:07 2017-03-21 11:28:48 1196456 240479817 1184548 200 84 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363670 SRX2659115 pit4 +SRR5363671 2017-03-21 11:39:07 2017-03-21 11:29:21 1703068 341745049 1680568 200 119 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363671 SRX2659116 pit3 +SRR5363672 2017-03-21 11:40:07 2017-03-21 11:29:21 1752519 352259586 1735224 201 122 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363672 SRX2659117 pit2 +SRR5363673 2017-03-21 11:37:07 2017-03-21 11:28:40 1702102 341794907 1682027 200 121 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363673 SRX2659118 pit1 +SRR5363674 2017-03-21 11:37:07 2017-03-21 11:28:41 2186337 438861929 2158858 200 155 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363674 SRX2659119 car10 +SRR5363675 2017-03-21 11:37:07 2017-03-21 11:28:37 1806341 362679360 1784566 200 128 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363675 SRX2659120 car9 +SRR5363676 2017-03-21 11:37:07 2017-03-21 11:28:29 1492644 299621613 1473926 200 106 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363676 SRX2659121 car8 +SRR5363677 2017-03-21 11:40:07 2017-03-21 11:29:02 2139706 429729175 2115063 200 151 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363677 SRX2659122 car7 +SRR5363678 2017-03-21 11:40:07 2017-03-21 11:29:13 1787521 358790286 1764881 200 124 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363678 SRX2659123 car6 +SRR5363679 2017-03-21 11:38:06 2017-03-21 11:28:48 1500378 300767719 1477539 200 106 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363679 SRX2659124 car5 +SRR5363680 2017-03-21 11:38:06 2017-03-21 11:28:54 879242 176495860 868255 200 63 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363680 SRX2659125 car4 +SRR5363681 2017-03-21 11:40:07 2017-03-21 11:30:02 1852256 371663665 1827605 200 130 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363681 SRX2659126 car3 +SRR5363682 2017-03-21 11:40:07 2017-03-21 11:29:40 996680 199979148 983324 200 72 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363682 SRX2659127 car2 +SRR5363683 2017-03-21 11:40:07 2017-03-21 11:29:49 1089078 218680766 1076092 200 79 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363683 SRX2659128 car1 +SRR5363684 2017-03-21 11:40:07 2017-03-21 11:29:57 1682993 337921519 1662784 200 118 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363684 SRX2659129 cef10 +SRR5363685 2017-03-21 11:40:07 2017-03-21 11:29:46 1274849 255689623 1256747 200 92 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363685 SRX2659130 cef9 +SRR5363686 2017-03-21 11:40:07 2017-03-21 11:29:22 1218188 244568636 1203299 200 87 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363686 SRX2659131 cef8 +SRR5363687 2017-03-21 11:40:07 2017-03-21 11:30:06 2027741 406979584 2001785 200 142 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363687 SRX2659132 cef7 +SRR5363688 2017-03-21 11:40:07 2017-03-21 11:29:38 998237 200918449 991068 201 72 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363688 SRX2659133 cef6 +SRR5363689 2017-03-21 11:40:07 2017-03-21 11:29:26 518920 104507529 515814 201 41 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363689 SRX2659134 cef5 +SRR5363690 2017-03-21 11:44:06 2017-03-21 11:33:47 11530820 2311357478 11354074 200 982 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363690 SRX2659135 cef4 +SRR5363691 2017-03-21 11:40:07 2017-03-21 11:29:41 516125 103920037 512793 201 41 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363691 SRX2659136 cef3 +SRR5363692 2017-03-21 11:40:07 2017-03-21 11:30:11 890572 179326220 884948 201 65 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363692 SRX2659137 cef2 +SRR5363693 2017-03-21 11:40:07 2017-03-21 11:30:04 978135 196865238 971039 201 74 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363693 SRX2659138 cef1 +SRR5363694 2017-03-21 11:40:07 2017-03-21 11:29:57 1959249 393031721 1932180 200 138 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363694 SRX2659139 imi2 +SRR5363695 2017-03-21 11:40:07 2017-03-21 11:29:43 1074984 215595123 1059636 200 76 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363695 SRX2659140 imi1 +SRR5363696 2017-03-21 11:41:07 2017-03-21 11:29:48 1288979 258545055 1270889 200 92 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363696 SRX2659141 dor10 +SRR5363697 2017-03-21 11:42:06 2017-03-21 11:31:06 2908416 583478795 2868638 200 200 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363697 SRX2659142 dor9 +SRR5363698 2017-03-21 11:49:07 2017-03-21 11:37:59 15442120 3097410993 15225523 200 1224 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363698 SRX2659143 dor8 +SRR5363699 2017-03-21 11:40:07 2017-03-21 11:30:00 1718167 344971206 1697410 200 123 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363699 SRX2659144 dor7 +SRR5363700 2017-03-21 11:41:07 2017-03-21 11:29:57 1739828 349655178 1722126 200 125 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363700 SRX2659145 dor6 +SRR5363701 2017-03-21 11:45:06 2017-03-21 11:35:22 18584730 3732962863 18375556 200 1453 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363701 SRX2659146 dor5 +SRR5363702 2017-03-21 11:44:06 2017-03-21 11:35:13 12158676 2438787680 11987886 200 981 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363702 SRX2659147 dor4 +SRR5363703 2017-03-21 11:44:06 2017-03-21 11:34:44 11391713 2283748728 11219804 200 916 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363703 SRX2659148 dor3 +SRR5363704 2017-03-21 11:42:06 2017-03-21 11:32:45 6155766 1236497552 6086854 200 513 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363704 SRX2659149 dor2 +SRR5363705 2017-03-21 11:41:07 2017-03-21 11:30:25 1002355 201209957 989835 200 72 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363705 SRX2659150 dor1 +SRR5363706 2017-03-21 11:41:07 2017-03-21 11:30:25 752409 151419028 746795 201 59 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363706 SRX2659151 cip10 +SRR5363707 2017-03-21 11:44:06 2017-03-21 11:33:49 10594069 2127067244 10466132 200 870 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363707 SRX2659152 cip9 +SRR5363708 2017-03-21 11:41:07 2017-03-21 11:30:31 1431059 287670501 1417182 201 105 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363708 SRX2659153 cip8 +SRR5363709 2017-03-21 11:41:07 2017-03-21 11:30:41 1208297 242791534 1195595 200 88 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363709 SRX2659154 cip7 +SRR5363710 2017-03-21 11:41:07 2017-03-21 11:30:35 1370364 275356971 1355960 200 100 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363710 SRX2659155 cip6 +SRR5363711 2017-03-21 11:42:06 2017-03-21 11:31:24 1593545 320335049 1578109 201 116 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363711 SRX2659156 cip5 +SRR5363712 2017-03-21 11:41:07 2017-03-21 11:31:00 1332160 267714312 1318490 200 99 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363712 SRX2659157 cip4 +SRR5363713 2017-03-21 11:42:06 2017-03-21 11:31:02 1893119 379943269 1868718 200 134 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363713 SRX2659158 cip3 +SRR5363714 2017-03-21 11:42:06 2017-03-21 11:31:01 1839276 369651139 1820660 200 129 assembly https://raw.githubusercontent.com/jmchilton/galaxy-examples/master/sra_PRJNA355367/SRR5363714 SRX2659159 cip2 +SRR5363715 2017-03-21 11:26:06 2017-03-21 10:59:33 712060 142833136 702138 200 51 assembly https://sra-download.ncbi.nlm.nih.gov/traces/sra46/SRR/005238/SRR5363715 SRX2659160 cip1 diff --git a/test-data/rules/uniprot.json b/test-data/rules/uniprot.json new file mode 100644 index 000000000000..be924eb20835 --- /dev/null +++ b/test-data/rules/uniprot.json @@ -0,0 +1,52 @@ +{ + "rules": [ + { + "type": "add_filter_count", + "count": "1", + "which": "first", + "invert": false + }, + { + "type": "remove_columns", + "target_columns": [ + 1, + 2, + 4, + 5, + 6 + ] + }, + { + "type": "sort", + "target_column": 0, + "numeric": false + }, + { + "type": "add_column_regex", + "target_column": 0, + "expression": ".*", + "replacement": "http://www.uniprot.org/uniprot/$&.fasta" + } + ], + "mapping": [ + { + "type": "info", + "columns": [ + 1 + ] + }, + { + "type": "list_identifiers", + "columns": [ + 0 + ] + }, + { + "type": "url", + "columns": [ + 2 + ] + } + ], + "extension": "csfasta" +} \ No newline at end of file diff --git a/test-data/rules/uniprot.tsv b/test-data/rules/uniprot.tsv new file mode 100644 index 000000000000..aae30c199f5c --- /dev/null +++ b/test-data/rules/uniprot.tsv @@ -0,0 +1,8 @@ +Entry Entry name Status Protein names Gene names Organism Length +E7C0H6 E7C0H6_9PAPI unreviewed Major capsid protein L1 L1 Equus caballus papillomavirus 3 498 +E7C0H0 E7C0H0_9PAPI unreviewed Protein E6 E6 Equus caballus papillomavirus 3 150 +E7C0H5 E7C0H5_9PAPI unreviewed Minor capsid protein L2 L2 Equus caballus papillomavirus 3 498 +E7C0H1 E7C0H1_9PAPI unreviewed Protein E7 Equus caballus papillomavirus 3 93 +E7C0H3 E7C0H3_9PAPI unreviewed Regulatory protein E2 E2 Equus caballus papillomavirus 3 421 +E7C0H4 E7C0H4_9PAPI unreviewed Putative E4 early protein (Fragment) Equus caballus papillomavirus 3 175 +E7C0H2 E7C0H2_9PAPI unreviewed Replication protein E1 (EC 3.6.4.12) (ATP-dependent helicase E1) E1 Equus caballus papillomavirus 3 621 diff --git a/test/functional/tools/sample_datatypes_conf.xml b/test/functional/tools/sample_datatypes_conf.xml index 8010f26e5617..271b8762c224 100644 --- a/test/functional/tools/sample_datatypes_conf.xml +++ b/test/functional/tools/sample_datatypes_conf.xml @@ -37,6 +37,7 @@ + diff --git a/test/galaxy_selenium/has_driver.py b/test/galaxy_selenium/has_driver.py index c9bb46f33367..ffba975258ee 100644 --- a/test/galaxy_selenium/has_driver.py +++ b/test/galaxy_selenium/has_driver.py @@ -173,6 +173,9 @@ def send_enter(self, element): def send_escape(self, element): element.send_keys(Keys.ESCAPE) + def send_backspace(self, element): + element.send_keys(Keys.BACKSPACE) + def wait(self, timeout=UNSPECIFIED_TIMEOUT, **kwds): if timeout is UNSPECIFIED_TIMEOUT: timeout = self.timeout_for(**kwds) diff --git a/test/galaxy_selenium/navigates_galaxy.py b/test/galaxy_selenium/navigates_galaxy.py index 92387fe7ce4d..2cd601e2659d 100644 --- a/test/galaxy_selenium/navigates_galaxy.py +++ b/test/galaxy_selenium/navigates_galaxy.py @@ -531,11 +531,10 @@ def _collection_upload_start(self, test_paths, ext, genome, collection_type): self.upload_build() def upload_tab_click(self, tab): - tab_tag_id = "#tab-title-link-%s" % tab - self.wait_for_and_click_selector(tab_tag_id) + self.components.upload.tab(tab=tab).wait_for_and_click() def upload_start_click(self): - self.wait_for_and_click_selector(".upload-button") + self.components.upload.start.wait_for_and_click() @retry_during_transitions def upload_set_footer_extension(self, ext, tab_id="regular"): @@ -560,8 +559,8 @@ def upload_start(self, tab_id="regular"): self.wait_for_and_click_selector("div#%s button#btn-start" % tab_id) @retry_during_transitions - def upload_build(self): - build_selector = "div#collection button#btn-build" + def upload_build(self, tab="collection"): + build_selector = "div#%s button#btn-build" % tab # Pause a bit to let the callback on the build button be registered. time.sleep(.5) # Click the Build button and make sure it disappears. @@ -581,6 +580,191 @@ def upload_queue_local_file(self, test_path, tab_id="regular"): file_upload = self.wait_for_selector('div#%s input[type="file"]' % tab_id) file_upload.send_keys(test_path) + def upload_rule_start(self): + self.upload_start_click() + self.upload_tab_click("rule-based") + + def upload_rule_build(self): + self.upload_build(tab="rule-based") + + def upload_rule_set_data_type(self, type_description): + upload = self.components.upload + data_type_element = upload.rule_select_data_type.wait_for_visible() + self.select2_set_value(data_type_element, type_description) + + def upload_rule_set_input_type(self, input_description): + upload = self.components.upload + input_type_element = upload.rule_select_input_type.wait_for_visible() + self.select2_set_value(input_type_element, input_description) + + def upload_rule_set_dataset(self, dataset_description="1:"): + upload = self.components.upload + rule_dataset_element = upload.rule_dataset_selector.wait_for_visible() + self.select2_set_value(rule_dataset_element, dataset_description) + + def rule_builder_set_collection_name(self, name): + rule_builder = self.components.rule_builder + name_element = rule_builder.collection_name_input.wait_for_and_click() + name_element.send_keys(name) + + def rule_builder_set_extension(self, extension): + self.select2_set_value(".rule-option-extension", extension) + + def rule_builder_filter_count(self, count=1): + rule_builder = self.components.rule_builder + rule_builder.menu_button_filter.wait_for_and_click() + with self.rule_builder_rule_editor("add-filter-count") as editor_element: + filter_input = editor_element.find_element_by_css_selector("input[type='number']") + filter_input.clear() + filter_input.send_keys("%s" % count) + + def rule_builder_sort(self, column_label, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_rules.wait_for_and_click() + with self.rule_builder_rule_editor("sort") as editor_element: + column_elem = editor_element.find_element_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elem, column_label) + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_add_regex_groups(self, column_label, group_count, regex, screenshot_name): + rule_builder = self.components.rule_builder + rule_builder.menu_button_column.wait_for_and_click() + with self.rule_builder_rule_editor("add-column-regex") as editor_element: + + column_elem = editor_element.find_element_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elem, column_label) + + groups_elem = editor_element.find_element_by_css_selector("input[type='radio'][value='groups']") + groups_elem.click() + + regex_elem = editor_element.find_element_by_css_selector("input.rule-regular-expression") + regex_elem.clear() + regex_elem.send_keys(regex) + + filter_input = editor_element.find_element_by_css_selector("input[type='number']") + filter_input.clear() + filter_input.send_keys("%s" % group_count) + + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_add_regex_replacement(self, column_label, regex, replacement, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_column.wait_for_and_click() + with self.rule_builder_rule_editor("add-column-regex") as editor_element: + + column_elem = editor_element.find_element_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elem, column_label) + + groups_elem = editor_element.find_element_by_css_selector("input[type='radio'][value='replacement']") + groups_elem.click() + + regex_elem = editor_element.find_element_by_css_selector("input.rule-regular-expression") + regex_elem.clear() + regex_elem.send_keys(regex) + + filter_input = editor_element.find_element_by_css_selector("input.rule-replacement") + filter_input.clear() + filter_input.send_keys("%s" % replacement) + + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_add_value(self, value, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_column.wait_for_and_click() + with self.rule_builder_rule_editor("add-column-value") as editor_element: + filter_input = editor_element.find_element_by_css_selector("input[type='text']") + filter_input.clear() + filter_input.send_keys(value) + + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_remove_columns(self, column_labels, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_rules.wait_for_and_click() + with self.rule_builder_rule_editor("remove-columns") as filter_editor_element: + column_elem = filter_editor_element.find_element_by_css_selector(".rule-column-selector") + for column_label in column_labels: + self.select2_set_value(column_elem, column_label) + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_concatenate_columns(self, column_label_1, column_label_2, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_column.wait_for_and_click() + with self.rule_builder_rule_editor("add-column-concatenate") as filter_editor_element: + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elems[0], column_label_1) + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elems[1], column_label_2) + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_split_columns(self, column_labels_1, column_labels_2, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_rules.wait_for_and_click() + with self.rule_builder_rule_editor("split-columns") as filter_editor_element: + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + clear = True + for column_label_1 in column_labels_1: + self.select2_set_value(column_elems[0], column_label_1, clear_value=clear) + clear = False + + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + clear = True + for column_label_2 in column_labels_2: + self.select2_set_value(column_elems[1], column_label_2, clear_value=clear) + clear = False + + if screenshot_name: + self.screenshot(screenshot_name) + + def rule_builder_swap_columns(self, column_label_1, column_label_2, screenshot_name): + rule_builder = self.components.rule_builder + rule_builder.menu_button_rules.wait_for_and_click() + with self.rule_builder_rule_editor("swap-columns") as filter_editor_element: + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elems[0], column_label_1) + column_elems = filter_editor_element.find_elements_by_css_selector(".rule-column-selector") + self.select2_set_value(column_elems[1], column_label_2) + if screenshot_name: + self.screenshot(screenshot_name) + + @contextlib.contextmanager + def rule_builder_rule_editor(self, rule_type): + rule_builder = self.components.rule_builder + rule_builder.menu_item_rule_type(rule_type=rule_type).wait_for_and_click() + filter_editor = rule_builder.rule_editor(rule_type=rule_type) + filter_editor_element = filter_editor.wait_for_visible() + yield filter_editor_element + rule_builder.rule_editor_ok.wait_for_and_click() + + def rule_builder_set_mapping(self, mapping_type, column_label, screenshot_name=None): + rule_builder = self.components.rule_builder + rule_builder.menu_button_rules.wait_for_and_click() + rule_builder.menu_item_rule_type(rule_type="mapping").wait_for_and_click() + rule_builder.add_mapping_menu.wait_for_and_click() + rule_builder.add_mapping_button(mapping_type=mapping_type).wait_for_and_click() + if mapping_type != "list-identifiers" or not isinstance(column_label, list): + mapping_elem = rule_builder.mapping_edit(mapping_type=mapping_type).wait_for_visible() + self.select2_set_value(mapping_elem, column_label) + if screenshot_name: + self.screenshot(screenshot_name) + else: + assert len(column_label) > 0 + column_labels = column_label + for i, column_label in enumerate(column_labels): + if i > 0: + rule_builder.mapping_add_column(mapping_type=mapping_type).wait_for_and_click() + mapping_elem = rule_builder.mapping_edit(mapping_type=mapping_type).wait_for_visible() + self.select2_set_value(mapping_elem, column_label) + if screenshot_name: + self.screenshot(screenshot_name) + rule_builder.mapping_ok.wait_for_and_click() + def workflow_editor_click_option(self, option_label): self.workflow_editor_click_options() menu_element = self.workflow_editor_options_menu_element() @@ -1166,7 +1350,7 @@ def wait_for_and_click(self, selector_template): element.click() return element - def select2_set_value(self, container_selector, value, with_click=True): + def select2_set_value(self, container_selector_or_elem, value, with_click=True, clear_value=False): # There are two hacky was to select things from the select2 widget - # with_click=True: This simulates the mouse click after the suggestion contains # only the selected value. @@ -1174,8 +1358,15 @@ def select2_set_value(self, container_selector, value, with_click=True): # why. # with_click seems to work in all situtations - the enter methods # doesn't seem to work with the tool form for some reason. - container_elem = self.wait_for_selector(container_selector) + if not hasattr(container_selector_or_elem, "find_element_by_css_selector"): + container_elem = self.wait_for_selector(container_selector_or_elem) + else: + container_elem = container_selector_or_elem + text_element = container_elem.find_element_by_css_selector("input[type='text']") + if clear_value: + self.send_backspace(text_element) + self.send_backspace(text_element) text_element.send_keys(value) # Wait for select2 options to load and then click to add this one. drop_elem = self.wait_for_selector_visible("#select2-drop") diff --git a/test/galaxy_selenium/navigation.yml b/test/galaxy_selenium/navigation.yml index fa338d65825d..017adf9e07a7 100644 --- a/test/galaxy_selenium/navigation.yml +++ b/test/galaxy_selenium/navigation.yml @@ -104,6 +104,7 @@ history_panel: build_pair: "Build Dataset Pair" build_list: "Build Dataset List" build_list_pairs: "Build List of Dataset Pairs" + build_from_rules: "Build Collection from Rules" collection_view: selectors: @@ -279,6 +280,36 @@ gies: spinner: 'img#spinner' iframe: 'body iframe[seamless="seamless"]' +upload: + selectors: + tab: '#tab-title-link-${tab}' + start: '.upload-button' + rule_source_content: 'textarea.upload-rule-source-content' + rule_select_data_type: '.rule-data-type' + rule_select_input_type: '.rule-select-type' + rule_dataset_selector: '.upload-rule-option .dataset-selector' + +rule_builder: + selectors: + _: '.rule-collection-creator' + menu_button_filter: '.rule-menu-filter-button' + menu_button_rules: '.rule-menu-rules-button' + menu_button_column: '.rule-menu-column-button' + menu_item_rule_type: '.rule-link-${rule_type}' + rule_editor: '.rule-edit-${rule_type}' + rule_editor_ok: '.rule-editor-ok' + add_mapping_menu: '.rule-add-mapping' + add_mapping_button: '.rule-add-mapping-${mapping_type}' + mapping_edit: '.rule-map-${mapping_type} .select2-container' + mapping_remove_column: '.rule-map-${mapping_type} .rule-column-selector-target-remove' + mapping_add_column: '.rule-map-${mapping_type} .rule-column-selector-target-add' + mapping_ok: '.rule-mapping-ok' + main_button_ok: '.rule-btn-okay' + collection_name_input: 'input.collection-name' + view_source: '.rule-builder-view-source' + source: '.rule-source' + table: '#hot-table .htCore' + charts: selectors: visualize_button: '.ui-portlet .button i.fa-line-chart' # without icon - it waits on other buttons that aren't visible, need more specific class diff --git a/test/galaxy_selenium/smart_components.py b/test/galaxy_selenium/smart_components.py index c0e0356f6c6e..83972a923eb3 100644 --- a/test/galaxy_selenium/smart_components.py +++ b/test/galaxy_selenium/smart_components.py @@ -86,3 +86,6 @@ def assert_absent_or_hidden_after_transitions(self, **kwds): def has_class(self, class_name): return class_name in self._has_driver.driver.find_element(*self._target.element_locator).get_attribute("class") + + def wait_for_and_send_keys(self, text): + self.wait_for_visible().send_keys(text) diff --git a/test/selenium_tests/framework.py b/test/selenium_tests/framework.py index 1d7f0f6207f3..40e23c6fdb31 100644 --- a/test/selenium_tests/framework.py +++ b/test/selenium_tests/framework.py @@ -252,17 +252,33 @@ def screenshot(self, label): if more for creating a set of images to augment automated testing with manual human inspection after a test or test suite has executed. """ + target = self._screenshot_path(label) + if target is None: + return + + self.driver.save_screenshot(target) + + def write_screenshot_directory_file(self, label, content): + target = self._screenshot_path(label, ".txt") + if target is None: + return + + with open(target, "w") as f: + f.write(content) + + def _screenshot_path(self, label, extension=".png"): if GALAXY_TEST_SCREENSHOTS_DIRECTORY is None: return if not os.path.exists(GALAXY_TEST_SCREENSHOTS_DIRECTORY): os.makedirs(GALAXY_TEST_SCREENSHOTS_DIRECTORY) - target = os.path.join(GALAXY_TEST_SCREENSHOTS_DIRECTORY, label + ".png") + target = os.path.join(GALAXY_TEST_SCREENSHOTS_DIRECTORY, label + extension) copy = 1 while os.path.exists(target): # Maybe previously a test re-run - keep the original. - target = os.path.join(GALAXY_TEST_SCREENSHOTS_DIRECTORY, "%s-%d.png" % (label, copy)) + target = os.path.join(GALAXY_TEST_SCREENSHOTS_DIRECTORY, "%s-%d%s" % (label, copy, extension)) copy += 1 - self.driver.save_screenshot(target) + + return target def reset_driver_and_session(self): self.tear_down_driver() diff --git a/test/selenium_tests/test_collection_builders.py b/test/selenium_tests/test_collection_builders.py index 009defe26701..5ec784b7dd35 100644 --- a/test/selenium_tests/test_collection_builders.py +++ b/test/selenium_tests/test_collection_builders.py @@ -100,5 +100,18 @@ def test_build_paired_list_hide_original(self): self.history_panel_wait_for_hid_hidden(1) self.history_panel_wait_for_hid_hidden(2) + @selenium_test + def test_build_simple_list_via_rules(self): + self.perform_upload(self.get_filename("1.fasta")) + self.history_panel_wait_for_hid_ok(1, allowed_force_refreshes=1) + self.history_panel_multi_operations_show() + self.history_panel_muli_operation_select_hid(1) + self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_from_rules) + + self.collection_builder_set_name("my cool list") + self.screenshot("collection_builder_rules_list") + self.collection_builder_create() + self.history_panel_wait_for_hid_ok(2, allowed_force_refreshes=1) + def _wait_for_hid_visible(self, hid): self.history_panel_wait_for_hid_visible(hid, allowed_force_refreshes=1) diff --git a/test/selenium_tests/test_uploads.py b/test/selenium_tests/test_uploads.py index 0eb9ef0f1d7e..a23a6d2381d4 100644 --- a/test/selenium_tests/test_uploads.py +++ b/test/selenium_tests/test_uploads.py @@ -1,3 +1,7 @@ +import os + +from selenium.webdriver.common.keys import Keys + from .framework import ( selenium_test, SeleniumTestCase, @@ -104,3 +108,221 @@ def test_upload_paired_list(self): # Make sure source items are hidden when the collection is created. self.history_panel_wait_for_hid_hidden(1) self.history_panel_wait_for_hid_hidden(2) + + @selenium_test + def test_rules_example_1_datasets(self): + # Test case generated for: + # https://www.ebi.ac.uk/ena/data/view/PRJDA60709 + self.home() + self.upload_rule_start() + self.screenshot("rules_example_1_1_rules_landing") + self.components.upload.rule_source_content.wait_for_and_send_keys("""study_accession sample_accession experiment_accession fastq_ftp +PRJDA60709 SAMD00016379 DRX000475 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000770/DRR000770.fastq.gz +PRJDA60709 SAMD00016383 DRX000476 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000771/DRR000771.fastq.gz +PRJDA60709 SAMD00016380 DRX000477 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000772/DRR000772.fastq.gz +PRJDA60709 SAMD00016378 DRX000478 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000773/DRR000773.fastq.gz +PRJDA60709 SAMD00016381 DRX000479 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000774/DRR000774.fastq.gz +PRJDA60709 SAMD00016382 DRX000480 ftp.sra.ebi.ac.uk/vol1/fastq/DRR000/DRR000775/DRR000775.fastq.gz""") + self.screenshot("rules_example_1_2_paste") + self.upload_rule_build() + rule_builder = self.components.rule_builder + rule_builder._.wait_for_and_click() + self.screenshot("rules_example_1_3_initial_rules") + rule_builder.menu_button_filter.wait_for_and_click() + self.screenshot("rule_builder_filters") + rule_builder.menu_item_rule_type(rule_type="add-filter-count").wait_for_and_click() + filter_editor = rule_builder.rule_editor(rule_type="add-filter-count") + filter_editor_element = filter_editor.wait_for_visible() + filter_input = filter_editor_element.find_element_by_css_selector("input[type='number']") + filter_input.clear() + filter_input.send_keys("1") + self.screenshot("rules_example_1_4_filter_header") + rule_builder.rule_editor_ok.wait_for_and_click() + self.rule_builder_set_mapping("url", "D") + self.rule_builder_set_mapping("name", "C", screenshot_name="rules_example_1_5_mapping_edit") + self.screenshot("rules_example_1_6_mapping_set") + self.select2_set_value(".rule-option-extension", "fastqsanger.gz") + self.screenshot("rules_example_1_7_extension_set") + # rule_builder.main_button_ok.wait_for_and_click() + # self.history_panel_wait_for_hid_ok(6) + # self.screenshot("rules_example_1_6_download_complete") + + @selenium_test + def test_rules_example_2_list(self): + self.perform_upload(self.get_filename("rules/PRJDA60709.tsv")) + self.history_panel_wait_for_hid_ok(1) + self.upload_rule_start() + self.upload_rule_set_data_type("Collection") + self.upload_rule_set_input_type("History Dataset") + self.upload_rule_set_dataset("1:") + self.screenshot("rules_example_2_1_inputs") + self.upload_rule_build() + rule_builder = self.components.rule_builder + rule_builder._.wait_for_and_click() + self.screenshot("rules_example_2_2_initial_rules") + # Filter header. + self.rule_builder_filter_count(1) + self.rule_builder_set_mapping("url", "D") + self.rule_builder_set_mapping("list-identifiers", "C") + self.rule_builder_set_extension("fastqsanger.gz") + self.screenshot("rules_example_2_3_rules") + self.rule_builder_set_collection_name("PRJDA60709") + self.screenshot("rules_example_2_4_name") + # rule_builder.main_buton_ok.wait_for_and_click() + # self.history_panel_wait_for_hid_ok(2) + # self.screenshot("rules_example_2_5_download_complete") + + @selenium_test + def test_rules_example_3_list_pairs(self): + self.perform_upload(self.get_filename("rules/PRJDB3920.tsv")) + self.history_panel_wait_for_hid_ok(1) + self.upload_rule_start() + self.upload_rule_set_data_type("Collection") + self.upload_rule_set_input_type("History Dataset") + self.upload_rule_set_dataset("1:") + self.screenshot("rules_example_3_1_inputs") + self.upload_rule_build() + rule_builder = self.components.rule_builder + rule_builder._.wait_for_and_click() + self.screenshot("rules_example_3_2_initial_rules") + # Filter header. + self.rule_builder_filter_count(1) + self.rule_builder_set_mapping("list-identifiers", "C") + self.select2_set_value(".rule-option-extension", "fastqsanger.gz") + self.screenshot("rules_example_3_3_old_rules") + self.rule_builder_add_regex_groups("D", 2, "(.*);(.*)", screenshot_name="rules_example_3_4_regex") + self.screenshot("rules_example_3_6_with_regexes") + # Remove A also? + self.rule_builder_remove_columns(["D"], screenshot_name="rules_example_3_7_removed_column") + self.rule_builder_split_columns(["D"], ["E"], screenshot_name="rules_example_3_8_split_columns") + self.screenshot("rules_example_3_9_columns_are_split") + self.rule_builder_add_regex_groups("D", 1, ".*_(\d).fastq.gz", screenshot_name="rules_example_3_10_regex_paired") + self.screenshot("rules_example_3_11_has_paired_id") + self.rule_builder_swap_columns("D", "E", screenshot_name="rules_example_3_12_swap_columns") + self.screenshot("rules_example_3_13_swapped_columns") + self.rule_builder_set_mapping("paired-identifier", "D") + self.rule_builder_set_mapping("url", "E") + self.rule_builder_set_collection_name("PRJDB3920") + self.screenshot("rules_example_3_14_paired_identifier_set") + + @selenium_test + def test_rules_example_4_accessions(self): + # http://www.uniprot.org/uniprot/?query=proteome:UP000052092+AND+proteomecomponent:%22Genome%22 + self._setup_uniprot_example() + self.screenshot("rules_example_4_1_inputs") + self.upload_rule_build() + rule_builder = self.components.rule_builder + rule_builder._.wait_for_and_click() + self.screenshot("rules_example_4_2_initial_rules") + self.rule_builder_filter_count(1) + self.rule_builder_remove_columns(["B", "C", "E", "F", "G"]) + self.rule_builder_set_mapping("info", "B") + self.screenshot("rules_example_4_3_basic_rules") + self.rule_builder_sort("A", screenshot_name="rules_example_4_4_sort") + + self.rule_builder_add_regex_replacement("A", ".*", "http://www.uniprot.org/uniprot/$&.fasta", screenshot_name="rules_example_4_5_url") + self.screenshot("rules_example_4_6_url_built") + self.rule_builder_set_mapping("list-identifiers", "A") + self.rule_builder_set_mapping("url", "C") + self.rule_builder_set_extension("fasta") + self.rule_builder_set_collection_name("UP000052092") + self.screenshot("rules_example_4_7_mapping_extension_and_name") + + rule_builder.view_source.wait_for_and_click() + text_area_elem = rule_builder.source.wait_for_visible() + + self.screenshot("rules_example_4_8_source") + self.write_screenshot_directory_file("rules_example_4_8_text", text_area_elem.get_attribute("value")) + + rule_builder.main_button_ok.wait_for_and_click() + rule_builder.view_source.wait_for_visible() + + @selenium_test + def test_rules_example_5_matching_collections(self): + self._setup_uniprot_example() + self.screenshot("rules_example_5_1_inputs") + self.upload_rule_build() + + rule_builder = self.components.rule_builder + rule_builder.view_source.wait_for_and_click() + + text_area_elem = rule_builder.source.wait_for_visible() + + content = self._read_rules_test_data_file("uniprot.json") + text_area_elem.clear() + text_area_elem.send_keys(content) + self.screenshot("rules_example_5_2_source") + rule_builder.main_button_ok.wait_for_and_click() + rule_builder.view_source.wait_for_visible() + self.screenshot("rules_example_5_3_initial_rules") + self.rule_builder_add_value("fasta", screenshot_name="rules_example_5_4_fasta") + self.rule_builder_add_value("UP000052092 FASTA") + self.rule_builder_add_regex_replacement("A", ".*", "http://www.uniprot.org/uniprot/$&.gff", screenshot_name="rules_example_5_5_url") + self.rule_builder_add_value("gff3") + self.rule_builder_add_value("UP000052092 GFF3") + self._scroll_to_end_of_table() + self.screenshot("rules_example_5_6_presplit") + self.rule_builder_split_columns(["C", "D", "E"], ["F", "G", "H"], screenshot_name="rules_example_5_7_split_columns") + self.screenshot("rules_example_5_8_split") + self.rule_builder_set_mapping("file-type", "D") + self.rule_builder_set_mapping("collection-name", "E") + self.screenshot("rules_example_5_9_mapping") + + @selenium_test + def test_rules_example_6_nested_lists(self): + self.home() + self.perform_upload(self.get_filename("rules/PRJNA355367.tsv")) + self.history_panel_wait_for_hid_ok(1) + self.upload_rule_start() + self.upload_rule_set_data_type("Collection") + self.upload_rule_set_input_type("History Dataset") + self.upload_rule_set_dataset("1:") + self.screenshot("rules_example_6_1_paste") + self.upload_rule_build() + + rule_builder = self.components.rule_builder + rule_builder._.wait_for_and_click() + + self.screenshot("rules_example_6_2_rules_landing") + + self.rule_builder_filter_count(1) + + self.rule_builder_set_mapping("url", "J") + self.rule_builder_set_extension("sra") + self._scroll_to_end_of_table() + self.screenshot("rules_example_6_3_end_of_table") + self.rule_builder_add_regex_groups("L", 1, "([^\d]+)\d+", screenshot_name="rules_example_6_4_regex") + self.rule_builder_set_mapping("list-identifiers", ["M", "A"], screenshot_name="rules_example_6_5_multiple_identifiers_edit") + self._scroll_to_end_of_table() + self.screenshot("rules_example_6_6_multiple_identifiers") + self.rule_builder_set_collection_name("PRJNA355367") + self.screenshot("rules_example_6_7_named") + + def _read_rules_test_data_file(self, name): + with open(self.test_data_resolver.get_filename(os.path.join("rules", name)), "r") as f: + return f.read() + + def _scroll_to_end_of_table(self): + rule_builder = self.components.rule_builder + table_elem = rule_builder.table.wait_for_visible() + first_cell = table_elem.find_elements_by_css_selector("td")[0] + action_chains = self.action_chains() + action_chains.move_to_element(first_cell) + action_chains.click(first_cell) + for i in range(15): + action_chains.send_keys(Keys.ARROW_RIGHT) + action_chains.perform() + + def _setup_uniprot_example(self): + self.perform_upload(self.get_filename("rules/uniprot.tsv")) + self.history_panel_wait_for_hid_ok(1) + self.upload_rule_start() + self.upload_rule_set_data_type("Collection") + self.upload_rule_set_input_type("History Dataset") + self.upload_rule_set_dataset("1:") + + # @selenium_test + # def test_rules_example_5_matching_collections(self): + # self.rule_builder_add_value("Protein FASTA") + # self.rule_builder_add_value("gff") + # self.rule_builder_add_value("Protein GFF")